Skip to content

Commit b77fff4

Browse files
Merge branch 'main' into codex/fix-build-driver-escalation
2 parents 8e880f9 + 7eebf83 commit b77fff4

28 files changed

Lines changed: 6865 additions & 51 deletions

‎.prettierignore‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,10 @@ tests/e2e/electron-shell/.e2e-env.json
1010
tests/fixtures/git-changes-repo/
1111
# E2E markdown fixture with intentional hard-break trailing spaces +  .
1212
tests/fixtures/job-description-metadata.md
13+
# Vendored upstream hook schemas (G3) — pinned byte-identical to upstream; never
14+
# reformatted so the drift detector compares against exactly what was published.
15+
schemas/vendor/*.json
16+
# Hook wire-payload snapshots (G4) — a generated artifact written byte-exact by
17+
# payload-snapshots.test.ts (UPDATE_HOOK_PAYLOAD_SNAPSHOTS=1); the test compares
18+
# against these bytes, so prettier must not reformat them (decision 14).
19+
src/main/services/hooks/__snapshots__/*.json

‎docs/claude-hooks.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
handlers) so repos that already ship Claude Code policy hooks work without a Cursor-format
66
`hooks.json`. Tracks issue #639.
77

8-
This is the Claude counterpart to [`docs/cursor-hooks.md`](./cursor-hooks.md).
8+
This is the Claude counterpart to [`docs/cursor-hooks.md`](./cursor-hooks.md). For the
9+
dialect-agnostic architecture (registry, canonical events, async/budget/epoch, spine,
10+
sandbox, UI) see [`docs/hooks.md`](./hooks.md).
911

1012
## On-disk layout
1113

‎docs/copse-hooks.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ vendor translation layer. It also exposes Copse-native knobs the imported dialec
1010

1111
Each hook is a process that receives a JSON payload on **stdin**, may print a JSON response
1212
on **stdout**, and can observe, block, or annotate the action that triggered it. Dialect is
13-
determined by source path (decision 8): `.copse/hooks.json` → the Copse adapter.
13+
determined by source path (decision 8): `.copse/hooks.json` → the Copse adapter. For the
14+
dialect-agnostic architecture (registry, canonical events, async/budget/epoch, spine,
15+
sandbox, UI) see [`docs/hooks.md`](./hooks.md).
1416

1517
The official JSON schema is published at
1618
[`schemas/copse-hooks.schema.json`](../schemas/copse-hooks.schema.json)

‎docs/cursor-hooks.md‎

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ can observe, block, or annotate the action that triggered it.
77

88
This document records the Cursor hooks contract, what Copse honours today, and what
99
remains for fuller parity. It is the hooks counterpart to
10-
[`docs/cursor-plugins.md`](./cursor-plugins.md).
10+
[`docs/cursor-plugins.md`](./cursor-plugins.md). For the cross-cutting architecture — the
11+
unified registry, canonical events, executors, async/budget/epoch, spine, sandbox, and UI
12+
that are dialect-agnostic — see [`docs/hooks.md`](./hooks.md).
1113

1214
## On-disk layout
1315

@@ -246,6 +248,55 @@ This is the same trust boundary described in
246248
[`docs/supply-chain-security.md`](./supply-chain-security.md): trusting a workspace means
247249
trusting the code it can cause to run.
248250

251+
## Vendored upstream schemas & drift detection (G3)
252+
253+
Copse pins committed copies of the upstream hook-config JSON schemas for both
254+
foreign dialects under [`schemas/vendor/`](../schemas/vendor/) —
255+
`claude-code-settings.schema.json` (Claude Code, from SchemaStore) and
256+
`cursor-hooks.schema.json` (the community `cursor-hooks` npm schema). See
257+
[`schemas/vendor/README.md`](../schemas/vendor/README.md) for provenance, pins,
258+
and the re-vendoring steps.
259+
260+
These exist for exactly two purposes, and are subject to two hard rules — they
261+
are **never fetched over the network** at runtime or in CI, and they are **never
262+
a load gate** (a config that violates an upstream schema still loads):
263+
264+
1. **Warn-level authoring lint.** Parsing a foreign config uses the schema's
265+
published event list to warn when a hooks group targets an event the vendor
266+
recognises but Copse does not act on yet (vs an outright typo). The valid
267+
hooks still load; the warning surfaces in Settings → Sources.
268+
2. **CI drift detector** (`src/main/services/hooks/vendor-schema-drift.test.ts`)
269+
diffs each vendored schema's published events against the events our adapters
270+
wire. Every published event must be either wired or listed in an explicit
271+
intentionally-unsupported set (`src/shared/hooks/vendored-hook-schemas.ts`); an
272+
upstream release adding an unaccounted event fails CI until it is wired or
273+
documented. Copse currently wires Claude `PreToolUse` + `SessionStart` and the
274+
Cursor events above; the long tail of Claude events (`Notification`,
275+
`TeammateIdle`, …) is intentionally-unsupported v1.
276+
277+
## Wire payload snapshots (G4)
278+
279+
Every dialect wire **request** payload — the stdin JSON a Cursor / Claude / Copse
280+
hook actually receives — is snapshot-tested against a committed golden fixture
281+
[`src/main/services/hooks/__snapshots__/wire-payloads.json`](../src/main/services/hooks/__snapshots__/wire-payloads.json)
282+
by [`src/main/services/hooks/payload-snapshots.test.ts`](../src/main/services/hooks/payload-snapshots.test.ts).
283+
The test marshals a fixed synthetic payload (with a fixed agent-session identity,
284+
so the B4 `model` fields are captured) for every canonical event each dialect
285+
declares a marshaller for — including the tool-flavor splits (shell / MCP / read
286+
for `toolGate`, shell / MCP for `afterToolUse`) — and asserts the result is
287+
byte-identical to the fixture.
288+
289+
This implements **decision 14** of
290+
[`docs/plans/hooks-and-feature-packs.md`](plans/hooks-and-feature-packs.md):
291+
pre-v1 with zero consumers we do not version payloads, but the request direction
292+
is the stability contract, so **changing a snapshot is a publish-time stability
293+
audit** — the reviewed JSON diff of the golden fixture _is_ the stability
294+
declaration. Regenerate the fixture (and review the diff) with:
295+
296+
```bash
297+
UPDATE_HOOK_PAYLOAD_SNAPSHOTS=1 npm test
298+
```
299+
249300
## Gaps and future work
250301

251302
1. **Content rewriting** — `updated_input` on tool gates (rewrite the proposed tool
@@ -272,7 +323,13 @@ trusting the code it can cause to run.
272323
- `src/main/services/security/permission-gate.ts` — calls the tool-gate hooks
273324
- `src/shared/types/cursor-hooks.ts` — `CursorHookEvent` / `CursorHookSummary`
274325
- `src/shared/types/hooks.ts` — shared `HookSummary` for Sources / `hooks:list`
326+
- `src/shared/hooks/vendored-hook-schemas.ts` — published-event mirrors + intentionally-unsupported sets (G3)
327+
- `schemas/vendor/` — pinned upstream Cursor + Claude hook schemas (G3); see its `README.md`
328+
- `src/main/services/hooks/vendor-schema-drift.test.ts` — CI drift detector (G3)
329+
- `src/main/services/hooks/payload-snapshots.test.ts` — dialect wire payload snapshot tests (G4)
330+
- `src/main/services/hooks/__snapshots__/wire-payloads.json` — committed golden wire-payload fixture (G4)
275331
- `src/main/services/exec/child-process-env.ts` — secret-scrubbed env for hook processes
332+
- `docs/hooks.md` — dialect-agnostic hooks architecture umbrella
276333
- `docs/claude-hooks.md` — Claude Code hooks contract
277334
- `docs/cursor-plugins.md` — sibling exploration of Cursor plugin support
278335
- `docs/supply-chain-security.md` — trust boundaries for executed code

‎docs/cursor-plugins.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ servers from Settings after changing plugin MCP configs.
115115

116116
## Related files
117117

118+
- [`docs/packs.md`](packs.md) — the feature-pack manifest that **extends** this plugin.json shape with hooks/prompt/ui/settings/storage slots + the pack lifecycle (P1)
118119
- `src/main/services/cursor-plugins.ts` — discovery and manifest parsing
119120
- `src/main/services/skills-registry.ts` — skill indexing
120121
- `src/main/services/mcp-registry.ts` — MCP spawn and tool registration

0 commit comments

Comments
 (0)