@event4u/agent-config 17.1.0 → 17.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +70 -0
  5. package/README.md +2 -2
  6. package/dist/agent-src/commands/project/analyze.md +9 -6
  7. package/dist/agent-src/commands/project/map.md +58 -0
  8. package/dist/agent-src/commands/project.md +5 -3
  9. package/dist/agent-src/skills/code-intelligence/SKILL.md +6 -0
  10. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  11. package/dist/cli/registry.js +1 -1
  12. package/dist/cli-delegate/cmd_conformance.js +23 -39
  13. package/dist/discovery/deprecation-report.md +1 -1
  14. package/dist/discovery/discovery-manifest.json +39 -13
  15. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  16. package/dist/discovery/discovery-manifest.summary.md +3 -3
  17. package/dist/discovery/dormancy-report.md +1 -1
  18. package/dist/discovery/orphan-report.md +1 -1
  19. package/dist/discovery/packs.json +6 -5
  20. package/dist/discovery/trust-report.md +2 -2
  21. package/dist/discovery/workspaces.json +5 -4
  22. package/dist/hooks/dispatch.js +65 -74
  23. package/dist/hooks/dispatch.sha256 +1 -1
  24. package/dist/hooks/instructions_observer.chunk.cjs +301 -0
  25. package/dist/hooks/instructions_observer.chunk.cjs.sha256 +1 -0
  26. package/dist/mcp/registry-manifest.json +2 -2
  27. package/dist/mcp/server.json +2 -2
  28. package/dist/mcp/server.mjs +33058 -22930
  29. package/package.json +4 -1
  30. package/src/config/continuity-surface.json +7 -0
  31. package/src/config/evaluator-budgets.json +4 -4
  32. package/src/config/gate-violation-baselines.json +3 -3
  33. package/src/config/gitignore-block.txt +6 -0
  34. package/src/config/hook-bundle-budget.json +17 -2
  35. package/src/config/hook-token-budget.json +1 -1
  36. package/src/config/packed-binary-manifest.json +27 -0
  37. package/src/config/skill-body-budget.json +7 -1
  38. package/src/scripts/_lib/bench_ab_complexity.ts +14 -2
  39. package/src/scripts/_lib/collector_record.ts +1 -0
  40. package/src/scripts/_lib/runtime_journal.ts +7 -7
  41. package/src/scripts/code_graph/build.ts +210 -10
  42. package/src/scripts/code_graph/cli.ts +168 -46
  43. package/src/scripts/code_graph/detect.ts +5 -4
  44. package/src/scripts/code_graph/docs_layer.ts +57 -0
  45. package/src/scripts/code_graph/extract.ts +58 -13
  46. package/src/scripts/code_graph/extract_more.ts +314 -0
  47. package/src/scripts/code_graph/laravel_overlay.ts +454 -0
  48. package/src/scripts/code_graph/map.ts +846 -0
  49. package/src/scripts/code_graph/map_model.ts +977 -0
  50. package/src/scripts/code_graph/map_verify.ts +140 -0
  51. package/src/scripts/code_graph/map_viewer/viewer.css +278 -0
  52. package/src/scripts/code_graph/map_viewer/viewer.js +756 -0
  53. package/src/scripts/code_graph/scip_reader.ts +201 -0
  54. package/src/scripts/code_graph/sqlite_store.ts +8 -1
  55. package/src/scripts/code_graph/types.ts +81 -15
  56. package/src/scripts/code_graph/validate.ts +16 -26
  57. package/src/scripts/code_graph/verbs.ts +7 -0
  58. package/src/scripts/hook_chunks.mjs +7 -0
  59. package/src/scripts/hook_manifest.json +1 -1
  60. package/src/scripts/hook_manifest.yaml +20 -0
  61. package/src/scripts/hooks/dispatch_hook.ts +2 -2
  62. package/src/scripts/hooks/instructions_observer_hook.ts +25 -6
  63. package/src/scripts/hooks/lazy_concern.ts +4 -0
  64. package/src/scripts/install-hooks.sh +5 -0
  65. package/src/scripts/lint_agents_layout.ts +2 -0
  66. package/src/scripts/lint_hook_manifest.ts +1 -0
  67. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -6
  68. package/src/scripts/mcp_server/graph_tools.ts +73 -1
  69. package/src/scripts/mcp_server/tools.ts +19 -36
  70. package/src/vendor/grammars/README.md +10 -5
  71. package/src/vendor/grammars/tree-sitter-go.wasm +0 -0
  72. package/src/vendor/grammars/tree-sitter-java.wasm +0 -0
  73. package/src/vendor/grammars/tree-sitter-python.wasm +0 -0
@@ -0,0 +1,201 @@
1
+ /**
2
+ * A reader for the part of a SCIP index (`index.scip`) the graph uses, decoded
3
+ * in-process with no protobuf runtime (road-to-a-map-that-draws-itself 6.2, E8).
4
+ *
5
+ * SCIP is a compiler-grade index a language's own indexer writes
6
+ * (scip-typescript, scip-java, scip-python, …). Its occurrences say, per file
7
+ * and line, which symbol is DEFINED there and which is REFERENCED — the
8
+ * precise answer the native extractor can only approximate by name. Read here:
9
+ *
10
+ * Index.documents (2) Document.relative_path (1)
11
+ * Document.occurrences (2) Occurrence.range (1) · symbol (2) · symbol_roles (3)
12
+ *
13
+ * Everything else is skipped by wire type, so a newer index with more fields
14
+ * still reads. A malformed buffer throws; the caller treats that as "no SCIP".
15
+ */
16
+ import type { CodeEdge, CodeNode } from './types.js';
17
+ import { GUESS_RESOLVED_VIA } from './types.js';
18
+
19
+ export interface ScipOccurrence {
20
+ /** 0-based start line. */
21
+ line: number;
22
+ symbol: string;
23
+ definition: boolean;
24
+ }
25
+
26
+ export interface ScipDocument {
27
+ path: string;
28
+ occurrences: ScipOccurrence[];
29
+ }
30
+
31
+ const DEFINITION_ROLE = 0x1;
32
+
33
+ class Reader {
34
+ private pos = 0;
35
+ constructor(private readonly buf: Uint8Array) {}
36
+ get done(): boolean {
37
+ return this.pos >= this.buf.length;
38
+ }
39
+ varint(): number {
40
+ let result = 0;
41
+ let shift = 0;
42
+ for (;;) {
43
+ if (this.pos >= this.buf.length) throw new Error('scip: truncated varint');
44
+ const b = this.buf[this.pos++] as number;
45
+ if (shift < 49) result += (b & 0x7f) * 2 ** shift;
46
+ if ((b & 0x80) === 0) return result;
47
+ shift += 7;
48
+ if (shift > 63) throw new Error('scip: varint too long');
49
+ }
50
+ }
51
+ bytes(): Uint8Array {
52
+ const n = this.varint();
53
+ if (this.pos + n > this.buf.length) throw new Error('scip: truncated field');
54
+ const out = this.buf.subarray(this.pos, this.pos + n);
55
+ this.pos += n;
56
+ return out;
57
+ }
58
+ skip(wire: number): void {
59
+ if (wire === 0) this.varint();
60
+ else if (wire === 1) this.pos += 8;
61
+ else if (wire === 2) this.bytes();
62
+ else if (wire === 5) this.pos += 4;
63
+ else throw new Error(`scip: unsupported wire type ${wire}`);
64
+ if (this.pos > this.buf.length) throw new Error('scip: truncated field');
65
+ }
66
+ }
67
+
68
+ const utf8 = new TextDecoder('utf-8');
69
+
70
+ function readOccurrence(buf: Uint8Array): ScipOccurrence {
71
+ const r = new Reader(buf);
72
+ const range: number[] = [];
73
+ let symbol = '';
74
+ let roles = 0;
75
+ while (!r.done) {
76
+ const key = r.varint();
77
+ const field = Math.floor(key / 8);
78
+ const wire = key % 8;
79
+ if (field === 1 && wire === 2) {
80
+ const packed = new Reader(r.bytes());
81
+ while (!packed.done) range.push(packed.varint());
82
+ } else if (field === 1 && wire === 0) range.push(r.varint());
83
+ else if (field === 2 && wire === 2) symbol = utf8.decode(r.bytes());
84
+ else if (field === 3 && wire === 0) roles = r.varint();
85
+ else r.skip(wire);
86
+ }
87
+ return { line: range[0] ?? 0, symbol, definition: (roles & DEFINITION_ROLE) !== 0 };
88
+ }
89
+
90
+ function readDocument(buf: Uint8Array): ScipDocument {
91
+ const r = new Reader(buf);
92
+ let docPath = '';
93
+ const occurrences: ScipOccurrence[] = [];
94
+ while (!r.done) {
95
+ const key = r.varint();
96
+ const field = Math.floor(key / 8);
97
+ const wire = key % 8;
98
+ if (field === 1 && wire === 2) docPath = utf8.decode(r.bytes());
99
+ else if (field === 2 && wire === 2) occurrences.push(readOccurrence(r.bytes()));
100
+ else r.skip(wire);
101
+ }
102
+ return { path: docPath, occurrences };
103
+ }
104
+
105
+ export function readScipIndex(buf: Uint8Array): ScipDocument[] {
106
+ const r = new Reader(buf);
107
+ const docs: ScipDocument[] = [];
108
+ while (!r.done) {
109
+ const key = r.varint();
110
+ const field = Math.floor(key / 8);
111
+ const wire = key % 8;
112
+ if (field === 2 && wire === 2) docs.push(readDocument(r.bytes()));
113
+ else r.skip(wire);
114
+ }
115
+ return docs;
116
+ }
117
+
118
+ /**
119
+ * Edges a SCIP index proves: a reference in file F at line L to a symbol
120
+ * defined in file G at line M becomes `innermost node at F:L → node at G:M`.
121
+ * Local symbols (`local N`) are file-private and skipped; a definition the
122
+ * native graph has no file for is skipped too, so every edge lands on a node.
123
+ */
124
+ export function scipEdges(docs: readonly ScipDocument[], nodes: readonly CodeNode[]): CodeEdge[] {
125
+ const byFile = new Map<string, CodeNode[]>();
126
+ for (const n of nodes) {
127
+ const list = byFile.get(n.source_file) ?? [];
128
+ list.push(n);
129
+ byFile.set(n.source_file, list);
130
+ }
131
+ const innermost = (file: string, line1: number): string | null => {
132
+ let best: CodeNode | null = null;
133
+ for (const n of byFile.get(file) ?? []) {
134
+ if (n.kind === 'file' || n.source_location.length < 3) continue;
135
+ const s = n.source_location[0] as number;
136
+ const e = n.source_location[2] as number;
137
+ if (s <= line1 && line1 <= e && (!best || s >= (best.source_location[0] as number))) best = n;
138
+ }
139
+ return best ? best.id : byFile.has(file) ? file : null;
140
+ };
141
+ const declaredAt = (file: string, line1: number): string | null => {
142
+ const hit = (byFile.get(file) ?? []).find((n) => n.kind !== 'file' && n.source_location[0] === line1);
143
+ return hit ? hit.id : byFile.has(file) ? file : null;
144
+ };
145
+
146
+ const defs = new Map<string, { file: string; line: number }>();
147
+ for (const d of docs) {
148
+ for (const o of d.occurrences) {
149
+ if (o.definition && o.symbol && !o.symbol.startsWith('local ') && !defs.has(o.symbol)) {
150
+ defs.set(o.symbol, { file: d.path, line: o.line + 1 });
151
+ }
152
+ }
153
+ }
154
+ const seen = new Set<string>();
155
+ const out: CodeEdge[] = [];
156
+ for (const d of [...docs].sort((a, b) => (a.path < b.path ? -1 : 1))) {
157
+ for (const o of d.occurrences) {
158
+ if (o.definition || !o.symbol || o.symbol.startsWith('local ')) continue;
159
+ const def = defs.get(o.symbol);
160
+ if (!def) continue;
161
+ const source = innermost(d.path, o.line + 1);
162
+ const target = declaredAt(def.file, def.line);
163
+ if (!source || !target || source === target) continue;
164
+ const key = `${source}\0${target}`;
165
+ if (seen.has(key)) continue;
166
+ seen.add(key);
167
+ out.push({
168
+ source,
169
+ target,
170
+ relation: 'uses',
171
+ confidence: 'EXTRACTED',
172
+ resolved_via: 'compiler-index',
173
+ provider: 'scip',
174
+ });
175
+ }
176
+ }
177
+ return out;
178
+ }
179
+
180
+ /**
181
+ * A native guess (name lookup, dynamic dispatch) from the same source to a
182
+ * same-named target is superseded once the compiler index states the link.
183
+ */
184
+ export function supersededByScip(scip: readonly CodeEdge[], nodes: readonly CodeNode[]): (e: CodeEdge) => boolean {
185
+ const label = new Map(nodes.map((n) => [n.id, n.label] as const));
186
+ const named = new Map<string, Set<string>>();
187
+ for (const e of scip) {
188
+ const l = label.get(e.target);
189
+ if (!l) continue;
190
+ const set = named.get(e.source) ?? new Set<string>();
191
+ set.add(l);
192
+ named.set(e.source, set);
193
+ }
194
+ return (e: CodeEdge): boolean => {
195
+ if (!GUESS_RESOLVED_VIA.has(e.resolved_via)) return false;
196
+ const set = named.get(e.source);
197
+ if (!set) return false;
198
+ const target = label.get(e.target) ?? e.target.replace(/^symbol:/, '');
199
+ return set.has(target);
200
+ };
201
+ }
@@ -110,6 +110,9 @@ export function emitSqliteTwin(graph: CodeGraph, serialized: string, jsonPath: s
110
110
  'CREATE INDEX nodes_label ON nodes(label);' +
111
111
  'CREATE INDEX nodes_source_file ON nodes(source_file);',
112
112
  );
113
+ // One transaction: in autocommit mode every row was its own fsync'd
114
+ // commit, ~30 s on this repository's graph against ~0.5 s batched.
115
+ db.exec('BEGIN');
113
116
  const meta = db.prepare('INSERT INTO meta (key, value) VALUES (?, ?)');
114
117
  meta.run('source_checksum', graph.source_checksum);
115
118
  meta.run('json_size', String(stat.size));
@@ -152,14 +155,18 @@ export function emitSqliteTwin(graph: CodeGraph, serialized: string, jsonPath: s
152
155
  e.provider,
153
156
  );
154
157
  }
158
+ db.exec('COMMIT');
155
159
  stampUserVersion(db, GRAPH_STORE_VERSION);
156
160
  } finally {
157
161
  db.close();
158
162
  }
159
163
  fs.renameSync(tmp, twin);
160
164
  return true;
161
- } catch {
165
+ } catch (e) {
162
166
  fs.rmSync(tmp, { force: true });
167
+ // Said once, on stderr: a silent `false` here is how the index went
168
+ // unwritten on every real repository without anyone noticing.
169
+ process.stderr.write(`code-graph: index not written: ${(e as Error).message}\n`);
163
170
  return false;
164
171
  }
165
172
  }
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  /** Launch set — the stacks this suite's consumers actually run. */
12
- export type Lang = 'php' | 'typescript' | 'javascript';
12
+ export type Lang = 'php' | 'typescript' | 'javascript' | 'python' | 'go' | 'java';
13
13
 
14
14
  /** Same scale as `discovery_graph.ts::EdgeConfidence` (reused, not forked). */
15
15
  export type EdgeConfidence = 'EXTRACTED' | 'INFERRED' | 'AMBIGUOUS';
@@ -28,7 +28,29 @@ export type EdgeConfidence = 'EXTRACTED' | 'INFERRED' | 'AMBIGUOUS';
28
28
  * extractor per-file and pure: the predicate needs only the two paths, and no
29
29
  * grammar can see whether a file is a test.
30
30
  */
31
- export type Relation = 'calls' | 'imports' | 'uses' | 'inherits' | 'member' | 'tests';
31
+ /**
32
+ * The last seven are the framework links the Laravel overlay reads by rule
33
+ * (`laravel_overlay.ts`): a route to its handler, a method to the view it
34
+ * renders, a view to the view it includes, code to the translation or config
35
+ * file it reads, a migration to its table, a model to its table, a model to
36
+ * the model an Eloquent relation names.
37
+ */
38
+ export type Relation =
39
+ | 'calls'
40
+ | 'imports'
41
+ | 'uses'
42
+ | 'inherits'
43
+ | 'member'
44
+ | 'tests'
45
+ | 'routes-to'
46
+ | 'renders'
47
+ | 'includes'
48
+ | 'reads'
49
+ | 'migrates'
50
+ | 'persists'
51
+ | 'relates'
52
+ /** A Markdown document links to, or names, the file (`docs_layer.ts`). */
53
+ | 'documents';
32
54
 
33
55
  /**
34
56
  * HOW an edge's target was arrived at — the mechanism, not the confidence.
@@ -64,6 +86,14 @@ export type ResolvedVia =
64
86
  | 'path-alias'
65
87
  | 'psr4'
66
88
  | 'route-table'
89
+ /** A Blade directive, a `view()` / `__()` / `config()` call, or a Markdown link naming a file. */
90
+ | 'directive-scan'
91
+ /** A `Schema::create|table` call, or a model whose table one of them creates. */
92
+ | 'schema-call'
93
+ /** `$this->hasMany(Other::class)` and its siblings inside a model. */
94
+ | 'eloquent-relation'
95
+ /** Stated by a compiler-grade SCIP index the project ships (`scip_reader.ts`). */
96
+ | 'compiler-index'
67
97
  | 'test-import'
68
98
  | 'name-lookup'
69
99
  | 'dynamic'
@@ -96,7 +126,9 @@ export type EdgeProvider =
96
126
  * the latter. The field exists precisely so a mixed graph can be told
97
127
  * apart, and a consumer-shipped graph is the first case where it is.
98
128
  */
99
- | 'foreign';
129
+ | 'foreign'
130
+ /** Read from the project's own SCIP index, merged into the native build. */
131
+ | 'scip';
100
132
 
101
133
  /**
102
134
  * The two mechanisms that are GUESSES rather than statements.
@@ -114,16 +146,14 @@ export type EdgeProvider =
114
146
  * from it and tagging that `test-import` made a wrong target trustworthy to
115
147
  * every consumer of the filter. One set, two readers, and the build pass can no
116
148
  * longer disagree with the query tier about what a guess is.
149
+ *
150
+ * The accepted-edge filter is a DENYLIST, so a new value is accepted by
151
+ * default — which is exactly backwards for `foreign`. It must be listed here
152
+ * or a consumer's graph would silently answer the gate verbs as if this
153
+ * package had resolved its edges. (Kept out of the literal: esbuild carries a
154
+ * comment inside an expression into the hook bundle.)
117
155
  */
118
- export const GUESS_RESOLVED_VIA: ReadonlySet<ResolvedVia> = new Set<ResolvedVia>([
119
- 'name-lookup',
120
- 'dynamic',
121
- // The accepted-edge filter is a DENYLIST, so a new value is accepted by
122
- // default — which is exactly backwards for this one. `foreign` must be
123
- // listed here or a consumer's graph would silently answer the gate verbs
124
- // as if this package had resolved its edges.
125
- 'foreign',
126
- ]);
156
+ export const GUESS_RESOLVED_VIA: ReadonlySet<ResolvedVia> = new Set<ResolvedVia>(['name-lookup', 'dynamic', 'foreign']);
127
157
 
128
158
  /** Does this edge's target rest on a stated fact rather than a guess? */
129
159
  export function isStatedResolution(resolved_via: ResolvedVia): boolean {
@@ -147,7 +177,21 @@ export interface CodeNode {
147
177
  * to one by name. `const Widget = class {}` is emitted as `class` for
148
178
  * exactly that reason.
149
179
  */
150
- kind: 'file' | 'class' | 'interface' | 'trait' | 'function' | 'method' | 'constant' | 'type' | 'enum' | 'skipped';
180
+ kind:
181
+ | 'file'
182
+ | 'class'
183
+ | 'interface'
184
+ | 'trait'
185
+ | 'function'
186
+ | 'method'
187
+ | 'constant'
188
+ | 'type'
189
+ | 'enum'
190
+ | 'skipped'
191
+ /** A declared route, `<VERB> <path>` (Laravel overlay). */
192
+ | 'route'
193
+ /** A database table a migration creates (Laravel overlay). */
194
+ | 'table';
151
195
  source_file: string;
152
196
  /** 1-based [startLine, startCol, endLine, endCol]; [] for synthetic nodes. */
153
197
  source_location: readonly number[];
@@ -220,7 +264,16 @@ export interface CodeGraph {
220
264
  * query verb could reach it: `affected` and `path` resolve their seeds
221
265
  * against real nodes. It was 257 of 660 edges on this engine's own source.
222
266
  */
223
- suppressed_edge_counts: { dynamic_no_candidate: number };
267
+ suppressed_edge_counts: {
268
+ dynamic_no_candidate: number;
269
+ /**
270
+ * A fluent call on Laravel's route builder (`->middleware()`,
271
+ * `->prefix()`) that name lookup had bound to an unrelated in-repo
272
+ * method of the same name. Present only when it is non-zero, so a
273
+ * graph without routes keeps its bytes.
274
+ */
275
+ route_builder_call?: number;
276
+ };
224
277
  nodes: CodeNode[];
225
278
  edges: CodeEdge[];
226
279
  }
@@ -250,13 +303,19 @@ export interface CodeGraph {
250
303
  * `--update` resolve imports the old (name-lookup) way for every unchanged
251
304
  * file — a silently mixed graph. `readSidecar` refuses a version mismatch, so
252
305
  * the bump is what makes that impossible rather than merely unlikely.
306
+ *
307
+ * Bumped 4 → 5 by `road-to-a-map-that-draws-itself` 1.3: same-file
308
+ * same-name declarations now carry `@2`, `@3` suffixes, so a cache written
309
+ * at v4 holds ids a v5 build would not produce.
253
310
  */
254
- export const SCHEMA_VERSION = 4;
311
+ export const SCHEMA_VERSION = 5;
255
312
 
256
313
  /** Deterministic per-file byte cap — files above this become a SKIPPED node. */
257
314
  export const MAX_FILE_BYTES = 1_000_000;
258
315
 
259
316
  /** file-extension → language, launch set only. */
317
+ // `.vue`: a component's `<script>` is TypeScript or JavaScript; extract.ts blanks the
318
+ // template and style around it. (Outside the literal: esbuild keeps comments inside one.)
260
319
  export const EXT_LANG: Readonly<Record<string, Lang>> = {
261
320
  '.php': 'php',
262
321
  '.ts': 'typescript',
@@ -267,6 +326,10 @@ export const EXT_LANG: Readonly<Record<string, Lang>> = {
267
326
  '.jsx': 'javascript',
268
327
  '.mjs': 'javascript',
269
328
  '.cjs': 'javascript',
329
+ '.py': 'python',
330
+ '.go': 'go',
331
+ '.java': 'java',
332
+ '.vue': 'typescript',
270
333
  };
271
334
 
272
335
  /** Grammar wasm filename per language (in `tree-sitter-wasms/out/`). */
@@ -274,6 +337,9 @@ export const GRAMMAR_WASM: Readonly<Record<Lang, string>> = {
274
337
  php: 'tree-sitter-php.wasm',
275
338
  typescript: 'tree-sitter-typescript.wasm',
276
339
  javascript: 'tree-sitter-javascript.wasm',
340
+ python: 'tree-sitter-python.wasm',
341
+ go: 'tree-sitter-go.wasm',
342
+ java: 'tree-sitter-java.wasm',
277
343
  };
278
344
 
279
345
  /** The single ABI version the pinned grammar set is built against. */
@@ -4,37 +4,23 @@
4
4
  */
5
5
  import type { CodeGraph } from './types.js';
6
6
 
7
+ /** One space-separated string per vocabulary: this module ships in the hook bundle, paid on every dispatch. */
8
+ const words = (s: string): ReadonlySet<string> => new Set(s.split(' '));
9
+
7
10
  /** Mirrors `Relation` in types.ts. `tests` is derived by the build pass (3.2),
8
11
  * not extracted, and is validated on exactly the same terms as the rest. */
9
- const RELATIONS = new Set(['calls', 'imports', 'uses', 'inherits', 'member', 'tests']);
12
+ const RELATIONS = words(
13
+ 'calls imports uses inherits member tests routes-to renders includes reads migrates persists relates documents',
14
+ );
10
15
  const CONFIDENCES = new Set(['EXTRACTED', 'INFERRED', 'AMBIGUOUS']);
11
16
  /** Mirrors `ResolvedVia` in types.ts — the full union, including the four
12
17
  * values no build emits yet (2.3 and Phase 3 add them). */
13
- const RESOLVED_VIA = new Set([
14
- 'same-file',
15
- 'import-specifier',
16
- 'path-alias',
17
- 'psr4',
18
- 'route-table',
19
- 'test-import',
20
- 'name-lookup',
21
- 'dynamic',
22
- // Adapted from a consumer's own graph file; see `foreign.ts`.
23
- 'foreign',
24
- ]);
25
- const PROVIDERS = new Set(['native', 'foreign']);
26
- const KINDS = new Set([
27
- 'file',
28
- 'class',
29
- 'interface',
30
- 'trait',
31
- 'function',
32
- 'method',
33
- 'constant',
34
- 'type',
35
- 'enum',
36
- 'skipped',
37
- ]);
18
+ // `foreign`: adapted from a consumer's own graph file; see `foreign.ts`.
19
+ const RESOLVED_VIA = words(
20
+ 'same-file import-specifier path-alias psr4 route-table directive-scan schema-call eloquent-relation compiler-index test-import name-lookup dynamic foreign',
21
+ );
22
+ const PROVIDERS = new Set(['native', 'foreign', 'scip']);
23
+ const KINDS = words('file class interface trait function method constant type enum skipped route table');
38
24
 
39
25
  export interface ValidateResult {
40
26
  ok: boolean;
@@ -60,6 +46,10 @@ export function validateGraph(g: unknown): ValidateResult {
60
46
  if (typeof n !== 'object' || n === null) return push(`node[${i}] not an object`);
61
47
  const nn = n as Record<string, unknown>;
62
48
  if (typeof nn['id'] !== 'string') push(`node[${i}].id must be a string`);
49
+ // A duplicate id makes the SQLite twin's PRIMARY KEY refuse the whole
50
+ // index, and two nodes behind one id make every answer about either
51
+ // of them ambiguous — so it is a schema error, not a warning.
52
+ else if (ids.has(nn['id'])) push(`node[${i}].id is a duplicate: ${nn['id']}`);
63
53
  else ids.add(nn['id']);
64
54
  if (!KINDS.has(nn['kind'] as string)) push(`node[${i}].kind invalid: ${String(nn['kind'])}`);
65
55
  if (typeof nn['source_file'] !== 'string') push(`node[${i}].source_file must be a string`);
@@ -105,6 +105,13 @@ export const REFERENCE_RELATIONS: ReadonlySet<CodeEdge['relation']> = new Set<Co
105
105
  'uses',
106
106
  'inherits',
107
107
  'tests',
108
+ 'routes-to',
109
+ 'renders',
110
+ 'includes',
111
+ 'reads',
112
+ 'migrates',
113
+ 'persists',
114
+ 'relates',
108
115
  ]);
109
116
 
110
117
  export function isReferenceEdge(e: CodeEdge): boolean {
@@ -38,6 +38,13 @@ export const HOOK_CHUNKS = Object.freeze([
38
38
  outfile: 'dist/hooks/chat_history.chunk.cjs',
39
39
  external: Object.freeze(['yaml']),
40
40
  }),
41
+ // Bound on `instructions_loaded` alone, which the host fires once per
42
+ // loaded file — a chunk keeps that one narrow event off every other
43
+ // dispatch's parse.
44
+ Object.freeze({
45
+ entry: 'src/scripts/hooks/instructions_observer_hook.ts',
46
+ outfile: 'dist/hooks/instructions_observer.chunk.cjs',
47
+ }),
41
48
  ]);
42
49
 
43
50
  /**
@@ -1 +1 @@
1
- {"fingerprint":"30b82f3ba5aa4c971915094f5f9e31647e476211304ac9657075cac72f19a391:109264","body_fingerprint":"1b21abf258fdb3d2684c9562058adf0c33b4b945b36da054f03bc794d63fa430:20850","manifest":{"schema_version":1,"concerns":{"chat-history":{"script":"src/scripts/chat_history.ts","args":["hook-dispatch"],"fail_closed":false,"severity":"advisory","effect":"telemetry","needs_payload_bodies":["result"]},"hot-context":{"script":"src/scripts/hot_context_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"memory"},"handoff-context":{"script":"src/scripts/handoff_context_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"context"},"roadmap-progress":{"script":"src/scripts/roadmap_progress_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"formatting","needs_payload_bodies":["input"]},"onboarding-gate":{"script":"src/scripts/onboarding_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification"},"context-hygiene":{"script":"src/scripts/context_hygiene_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"verify-before-complete":{"script":"src/scripts/before_complete_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","needs_payload_bodies":["input","result"]},"ship-diff-volume":{"script":"src/scripts/hooks/ship_diff_volume_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","needs_payload_bodies":["input"]},"minimal-safe-diff":{"script":"src/scripts/minimal_safe_diff_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","needs_payload_bodies":["input"]},"injection-scan":{"script":"src/scripts/injection_scan_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","needs_payload_bodies":["input","result"]},"block-no-verify":{"script":"src/scripts/hooks/block_no_verify.ts","args":[],"fail_closed":true,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"block-kernel-rule-writes":{"script":"src/scripts/hooks/block_kernel_rule_writes.ts","args":[],"fail_closed":true,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"block-plumbing-writes":{"script":"src/scripts/hooks/block_plumbing_writes.ts","args":[],"fail_closed":true,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"one-question-per-ask":{"script":"src/scripts/hooks/one_question_per_ask_hook.ts","args":[],"fail_closed":false,"severity":"blocking","effect":"permission","tools":["AskUserQuestion","AskQuestion","UserQuestion","ask_user_question","ask_question","user_question"],"needs_payload_bodies":["input"]},"block-speaking-inbox-dir":{"script":"src/scripts/hooks/block_speaking_inbox_dir.ts","args":[],"fail_closed":false,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"block-credential-reads":{"script":"src/scripts/hooks/block_credential_reads.ts","args":[],"fail_closed":false,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"block-config-weakening":{"script":"src/scripts/hooks/block_config_weakening.ts","args":[],"fail_closed":false,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"rtk-wrap":{"script":"src/scripts/hooks/rtk_wrap_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"formatting","needs_payload_bodies":["input"]},"design-slop":{"script":"src/scripts/hooks/design_slop_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","needs_payload_bodies":["input"]},"design-pass":{"script":"src/scripts/hooks/design_pass_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification","needs_payload_bodies":["input"]},"design-pass-stop":{"script":"src/scripts/hooks/design_pass_hook.ts","args":["--slot","stop"],"fail_closed":false,"severity":"advisory","effect":"notification"},"ui-route-nudge":{"script":"src/scripts/hooks/ui_route_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification","needs_payload_bodies":["input"]},"suggestion-capture":{"script":"src/scripts/hooks/suggestion_capture_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"chain-nudge":{"script":"src/scripts/hooks/chain_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification","needs_payload_bodies":["input"]},"code-graph-context":{"script":"src/scripts/hooks/code_graph_context_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"context","tools":["Grep","Glob","Read","Bash"],"needs_payload_bodies":["input"]},"memory-learn":{"script":"src/scripts/memory_learn_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"memory"},"first-run-gate":{"script":"src/scripts/first_run_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification"},"profile-staleness":{"script":"src/scripts/profile_staleness_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification"},"wrapper-freshness":{"script":"src/scripts/wrapper_freshness_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification"},"surface-probe":{"script":"src/scripts/surface_probe_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"team-review-gate":{"script":"src/scripts/team_review_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification"},"pr-url-reminder":{"script":"src/scripts/pr_url_reminder_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification","needs_payload_bodies":["input","result"]},"push-settle":{"script":"src/scripts/hooks/push_settle_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","needs_payload_bodies":["input","result"]},"language-mirror":{"script":"src/scripts/language_mirror_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"context"},"git-authorization":{"script":"src/scripts/git_authorization_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification"},"evidence-independence":{"script":"src/scripts/hooks/evidence_independence.ts","args":[],"fail_closed":false,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"session-canary":{"script":"src/scripts/session_canary_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"context"},"council-availability":{"script":"src/scripts/council_availability_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification"},"telemetry-disclosure":{"script":"src/scripts/telemetry_disclosure_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification"},"telemetry-flush":{"script":"src/scripts/hooks/telemetry_flush_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"self-repair":{"script":"src/scripts/self_repair_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"session-register":{"script":"src/scripts/session_register_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"orchestration-record":{"script":"src/scripts/hooks/orchestration_record_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry","tools":["Agent","Task"],"needs_payload_bodies":["input","result"]},"telemetry-usage":{"script":"src/scripts/hooks/telemetry_usage_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry","tools":["Skill"],"needs_payload_bodies":["input"]},"mcp-usage-observation":{"script":"src/scripts/hooks/mcp_usage_observation_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"delegation-nudge":{"script":"src/scripts/hooks/delegation_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification","nudge_rank":1},"skill-route":{"script":"src/scripts/hooks/skill_route_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"context","nudge_rank":2},"rule-inject":{"script":"src/scripts/hooks/rule_inject_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"context","needs_payload_bodies":["input"]},"obligation-settle":{"script":"src/scripts/hooks/obligation_settle_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification"},"end-review-nudge":{"script":"src/scripts/hooks/end_review_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification","skip_on_refusal_retry":true},"review-baseline":{"script":"src/scripts/hooks/review_baseline_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"turn-end-gate":{"script":"src/scripts/hooks/turn_end_gate_hook.ts","args":[],"fail_closed":false,"severity":"blocking","effect":"permission"},"comment-discipline":{"script":"src/scripts/hooks/comment_discipline_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","tools":["Write","Edit"],"needs_payload_bodies":["input"]},"edit-shape":{"script":"src/scripts/hooks/edit_shape_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","tools":["Write"],"needs_payload_bodies":["input"]},"reread-guard":{"script":"src/scripts/hooks/reread_guard_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","tools":["Read","Edit","Write"],"needs_payload_bodies":["input"]},"memory-recall":{"script":"src/scripts/hooks/memory_recall_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"memory","tools":["Bash","Edit","Write","MultiEdit","str-replace-editor","save-file","launch-process"],"needs_payload_bodies":["input"]},"session-eol":{"script":"src/scripts/hooks/session_eol_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification"},"interruption-ledger":{"script":"src/scripts/hooks/interruption_ledger_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry","skip_on_refusal_retry":true},"subagent-ledger":{"script":"src/scripts/hooks/subagent_ledger_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"tool-result-bytes":{"script":"src/scripts/hooks/tool_result_bytes_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"spawn-guard-shadow":{"script":"src/scripts/hooks/spawn_guard_shadow_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry","tools":["Agent","Task","SendMessage"],"needs_payload_bodies":["input","result"],"payload_fields":["name","subagent_type","to","recipient_kind","agentId"]},"host-rate-marker":{"script":"src/scripts/hooks/host_rate_marker_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"source-first-gate":{"script":"src/scripts/hooks/source_first_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry","needs_payload_bodies":["input"]},"journal-record":{"script":"src/scripts/hooks/journal_record_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"}},"roles":{"worker":{"drop":["delegation-nudge","skill-route","end-review-nudge","review-baseline","council-availability","team-review-gate","self-repair","session-eol","interruption-ledger"]}},"platforms":{"augment":{"ask":"text","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","minimal-safe-diff","profile-staleness","wrapper-freshness","surface-probe","session-register"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"pre_tool_use":["block-no-verify","evidence-independence","block-kernel-rule-writes","block-plumbing-writes","block-config-weakening","block-speaking-inbox-dir","block-credential-reads","rtk-wrap","design-slop","ui-route-nudge","code-graph-context","chain-nudge","ship-diff-volume","reread-guard","memory-recall"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","push-settle","orchestration-record","telemetry-usage","mcp-usage-observation","tool-result-bytes","edit-shape","comment-discipline","design-pass"]},"claude":{"ask":"native","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","minimal-safe-diff","profile-staleness","wrapper-freshness","surface-probe","session-register","review-baseline","journal-record","rule-inject"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush","journal-record"],"stop":["chat-history","verify-before-complete","team-review-gate","end-review-nudge","obligation-settle","turn-end-gate","self-repair","session-register","session-eol","interruption-ledger","roadmap-progress","design-pass-stop","suggestion-capture","journal-record"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","delegation-nudge","skill-route","git-authorization","session-canary","self-repair","session-register","rule-inject","suggestion-capture","journal-record"],"pre_tool_use":["block-no-verify","evidence-independence","block-kernel-rule-writes","block-plumbing-writes","block-config-weakening","block-speaking-inbox-dir","block-credential-reads","one-question-per-ask","rtk-wrap","design-slop","ui-route-nudge","code-graph-context","chain-nudge","ship-diff-volume","reread-guard","spawn-guard-shadow","source-first-gate","memory-recall"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","push-settle","orchestration-record","telemetry-usage","mcp-usage-observation","tool-result-bytes","edit-shape","comment-discipline","language-mirror","design-pass","journal-record","spawn-guard-shadow"],"pre_compact":["language-mirror","rule-inject","journal-record"],"subagent_start":["subagent-ledger","journal-record"],"subagent_stop":["subagent-ledger","journal-record"],"agent_error":["host-rate-marker"]},"cowork":{"ask":"text","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","minimal-safe-diff","profile-staleness","wrapper-freshness","surface-probe","session-register"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"pre_tool_use":["block-no-verify","evidence-independence","block-kernel-rule-writes","block-plumbing-writes","block-config-weakening","block-speaking-inbox-dir","block-credential-reads","rtk-wrap","design-slop","ui-route-nudge","code-graph-context","chain-nudge","ship-diff-volume","reread-guard","spawn-guard-shadow","source-first-gate","memory-recall"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","push-settle","orchestration-record","telemetry-usage","mcp-usage-observation","tool-result-bytes","edit-shape","comment-discipline","design-pass","spawn-guard-shadow"],"subagent_start":["subagent-ledger"],"subagent_stop":["subagent-ledger"]},"cursor":{"ask":"text","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","minimal-safe-diff","profile-staleness","wrapper-freshness","surface-probe","session-register"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","push-settle","orchestration-record","telemetry-usage","mcp-usage-observation","tool-result-bytes","edit-shape","comment-discipline","design-pass"]},"cline":{"ask":"text","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","minimal-safe-diff","profile-staleness","wrapper-freshness","surface-probe","session-register"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","push-settle","orchestration-record","telemetry-usage","mcp-usage-observation","tool-result-bytes","edit-shape","comment-discipline","design-pass"]},"windsurf":{"ask":"text","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","profile-staleness","wrapper-freshness","surface-probe","session-register"],"stop":["chat-history","verify-before-complete","self-repair","session-register","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","language-mirror","git-authorization","session-canary","self-repair","session-register"]},"gemini":{"ask":"text","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","minimal-safe-diff","profile-staleness","wrapper-freshness","surface-probe","session-register"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","push-settle","orchestration-record","telemetry-usage","mcp-usage-observation","tool-result-bytes","edit-shape","comment-discipline","design-pass"]},"copilot":{"ask":"text","fallback_only":true}},"native_event_aliases":{"augment":{"SessionStart":"session_start","SessionEnd":"session_end","Stop":"stop","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use"},"claude":{"SessionStart":"session_start","SessionEnd":"session_end","Stop":"stop","UserPromptSubmit":"user_prompt_submit","PostToolUse":"post_tool_use","PostToolUseFailure":"post_tool_use","PreToolUse":"pre_tool_use","PreCompact":"pre_compact","SubagentStart":"subagent_start","SubagentStop":"subagent_stop","StopFailure":"agent_error"},"cowork":{"SessionStart":"session_start","SessionEnd":"session_end","Stop":"stop","UserPromptSubmit":"user_prompt_submit","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use","PreCompact":"pre_compact","SubagentStart":"subagent_start","SubagentStop":"subagent_stop"},"cursor":{"sessionStart":"session_start","sessionEnd":"session_end","stop":"stop","beforeSubmitPrompt":"user_prompt_submit","postToolUse":"post_tool_use","preToolUse":"pre_tool_use","preCompact":"pre_compact"},"cline":{"TaskStart":"session_start","TaskResume":"session_start","TaskComplete":"session_end","TaskCancel":"stop","UserPromptSubmit":"user_prompt_submit","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use","PreCompact":"pre_compact"},"windsurf":{"pre_user_prompt":"user_prompt_submit","post_cascade_response":"stop","post_setup_worktree":"session_start"},"gemini":{"SessionStart":"session_start","SessionEnd":"session_end","BeforeAgent":"user_prompt_submit","AfterAgent":"stop","BeforeTool":"pre_tool_use","AfterTool":"post_tool_use"}}}}
1
+ {"fingerprint":"42e661504c14c219092fe030a1e89d6b94e28568dc5f6fc3b81ed69d2c653938:110425","body_fingerprint":"3f626590e35e1ad6ebc66c61a96fe5008ace6ff2df53fb4f645d023b69e2945b:21099","manifest":{"schema_version":1,"concerns":{"chat-history":{"script":"src/scripts/chat_history.ts","args":["hook-dispatch"],"fail_closed":false,"severity":"advisory","effect":"telemetry","needs_payload_bodies":["result"]},"hot-context":{"script":"src/scripts/hot_context_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"memory"},"handoff-context":{"script":"src/scripts/handoff_context_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"context"},"roadmap-progress":{"script":"src/scripts/roadmap_progress_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"formatting","needs_payload_bodies":["input"]},"onboarding-gate":{"script":"src/scripts/onboarding_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification"},"context-hygiene":{"script":"src/scripts/context_hygiene_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"verify-before-complete":{"script":"src/scripts/before_complete_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","needs_payload_bodies":["input","result"]},"ship-diff-volume":{"script":"src/scripts/hooks/ship_diff_volume_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","needs_payload_bodies":["input"]},"minimal-safe-diff":{"script":"src/scripts/minimal_safe_diff_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","needs_payload_bodies":["input"]},"injection-scan":{"script":"src/scripts/injection_scan_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","needs_payload_bodies":["input","result"]},"block-no-verify":{"script":"src/scripts/hooks/block_no_verify.ts","args":[],"fail_closed":true,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"block-kernel-rule-writes":{"script":"src/scripts/hooks/block_kernel_rule_writes.ts","args":[],"fail_closed":true,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"block-plumbing-writes":{"script":"src/scripts/hooks/block_plumbing_writes.ts","args":[],"fail_closed":true,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"one-question-per-ask":{"script":"src/scripts/hooks/one_question_per_ask_hook.ts","args":[],"fail_closed":false,"severity":"blocking","effect":"permission","tools":["AskUserQuestion","AskQuestion","UserQuestion","ask_user_question","ask_question","user_question"],"needs_payload_bodies":["input"]},"block-speaking-inbox-dir":{"script":"src/scripts/hooks/block_speaking_inbox_dir.ts","args":[],"fail_closed":false,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"block-credential-reads":{"script":"src/scripts/hooks/block_credential_reads.ts","args":[],"fail_closed":false,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"block-config-weakening":{"script":"src/scripts/hooks/block_config_weakening.ts","args":[],"fail_closed":false,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"rtk-wrap":{"script":"src/scripts/hooks/rtk_wrap_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"formatting","needs_payload_bodies":["input"]},"design-slop":{"script":"src/scripts/hooks/design_slop_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","needs_payload_bodies":["input"]},"design-pass":{"script":"src/scripts/hooks/design_pass_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification","needs_payload_bodies":["input"]},"design-pass-stop":{"script":"src/scripts/hooks/design_pass_hook.ts","args":["--slot","stop"],"fail_closed":false,"severity":"advisory","effect":"notification"},"ui-route-nudge":{"script":"src/scripts/hooks/ui_route_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification","needs_payload_bodies":["input"]},"suggestion-capture":{"script":"src/scripts/hooks/suggestion_capture_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"chain-nudge":{"script":"src/scripts/hooks/chain_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification","needs_payload_bodies":["input"]},"code-graph-context":{"script":"src/scripts/hooks/code_graph_context_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"context","tools":["Grep","Glob","Read","Bash"],"needs_payload_bodies":["input"]},"memory-learn":{"script":"src/scripts/memory_learn_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"memory"},"first-run-gate":{"script":"src/scripts/first_run_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification"},"profile-staleness":{"script":"src/scripts/profile_staleness_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification"},"wrapper-freshness":{"script":"src/scripts/wrapper_freshness_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification"},"surface-probe":{"script":"src/scripts/surface_probe_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"team-review-gate":{"script":"src/scripts/team_review_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification"},"pr-url-reminder":{"script":"src/scripts/pr_url_reminder_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification","needs_payload_bodies":["input","result"]},"push-settle":{"script":"src/scripts/hooks/push_settle_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","needs_payload_bodies":["input","result"]},"language-mirror":{"script":"src/scripts/language_mirror_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"context"},"git-authorization":{"script":"src/scripts/git_authorization_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification"},"evidence-independence":{"script":"src/scripts/hooks/evidence_independence.ts","args":[],"fail_closed":false,"severity":"blocking","effect":"permission","needs_payload_bodies":["input"]},"session-canary":{"script":"src/scripts/session_canary_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"context"},"council-availability":{"script":"src/scripts/council_availability_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification"},"telemetry-disclosure":{"script":"src/scripts/telemetry_disclosure_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification"},"telemetry-flush":{"script":"src/scripts/hooks/telemetry_flush_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"self-repair":{"script":"src/scripts/self_repair_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"session-register":{"script":"src/scripts/session_register_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"orchestration-record":{"script":"src/scripts/hooks/orchestration_record_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry","tools":["Agent","Task"],"needs_payload_bodies":["input","result"]},"telemetry-usage":{"script":"src/scripts/hooks/telemetry_usage_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry","tools":["Skill"],"needs_payload_bodies":["input"]},"mcp-usage-observation":{"script":"src/scripts/hooks/mcp_usage_observation_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"delegation-nudge":{"script":"src/scripts/hooks/delegation_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification","nudge_rank":1},"skill-route":{"script":"src/scripts/hooks/skill_route_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"context","nudge_rank":2},"rule-inject":{"script":"src/scripts/hooks/rule_inject_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"context","needs_payload_bodies":["input"]},"obligation-settle":{"script":"src/scripts/hooks/obligation_settle_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification"},"end-review-nudge":{"script":"src/scripts/hooks/end_review_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification","skip_on_refusal_retry":true},"review-baseline":{"script":"src/scripts/hooks/review_baseline_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"turn-end-gate":{"script":"src/scripts/hooks/turn_end_gate_hook.ts","args":[],"fail_closed":false,"severity":"blocking","effect":"permission"},"comment-discipline":{"script":"src/scripts/hooks/comment_discipline_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","tools":["Write","Edit"],"needs_payload_bodies":["input"]},"edit-shape":{"script":"src/scripts/hooks/edit_shape_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","tools":["Write"],"needs_payload_bodies":["input"]},"reread-guard":{"script":"src/scripts/hooks/reread_guard_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"verification","tools":["Read","Edit","Write"],"needs_payload_bodies":["input"]},"memory-recall":{"script":"src/scripts/hooks/memory_recall_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"memory","tools":["Bash","Edit","Write","MultiEdit","str-replace-editor","save-file","launch-process"],"needs_payload_bodies":["input"]},"session-eol":{"script":"src/scripts/hooks/session_eol_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"notification"},"interruption-ledger":{"script":"src/scripts/hooks/interruption_ledger_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry","skip_on_refusal_retry":true},"instructions-observer":{"script":"src/scripts/hooks/instructions_observer_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"subagent-ledger":{"script":"src/scripts/hooks/subagent_ledger_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"tool-result-bytes":{"script":"src/scripts/hooks/tool_result_bytes_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"spawn-guard-shadow":{"script":"src/scripts/hooks/spawn_guard_shadow_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry","tools":["Agent","Task","SendMessage"],"needs_payload_bodies":["input","result"],"payload_fields":["name","subagent_type","to","recipient_kind","agentId"]},"host-rate-marker":{"script":"src/scripts/hooks/host_rate_marker_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"},"source-first-gate":{"script":"src/scripts/hooks/source_first_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry","needs_payload_bodies":["input"]},"journal-record":{"script":"src/scripts/hooks/journal_record_hook.ts","args":[],"fail_closed":false,"severity":"advisory","effect":"telemetry"}},"roles":{"worker":{"drop":["delegation-nudge","skill-route","end-review-nudge","review-baseline","council-availability","team-review-gate","self-repair","session-eol","interruption-ledger"]}},"platforms":{"augment":{"ask":"text","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","minimal-safe-diff","profile-staleness","wrapper-freshness","surface-probe","session-register"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"pre_tool_use":["block-no-verify","evidence-independence","block-kernel-rule-writes","block-plumbing-writes","block-config-weakening","block-speaking-inbox-dir","block-credential-reads","rtk-wrap","design-slop","ui-route-nudge","code-graph-context","chain-nudge","ship-diff-volume","reread-guard","memory-recall"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","push-settle","orchestration-record","telemetry-usage","mcp-usage-observation","tool-result-bytes","edit-shape","comment-discipline","design-pass"]},"claude":{"ask":"native","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","minimal-safe-diff","profile-staleness","wrapper-freshness","surface-probe","session-register","review-baseline","journal-record","rule-inject"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush","journal-record"],"stop":["chat-history","verify-before-complete","team-review-gate","end-review-nudge","obligation-settle","turn-end-gate","self-repair","session-register","session-eol","interruption-ledger","roadmap-progress","design-pass-stop","suggestion-capture","journal-record"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","delegation-nudge","skill-route","git-authorization","session-canary","self-repair","session-register","rule-inject","suggestion-capture","journal-record"],"pre_tool_use":["block-no-verify","evidence-independence","block-kernel-rule-writes","block-plumbing-writes","block-config-weakening","block-speaking-inbox-dir","block-credential-reads","one-question-per-ask","rtk-wrap","design-slop","ui-route-nudge","code-graph-context","chain-nudge","ship-diff-volume","reread-guard","spawn-guard-shadow","source-first-gate","memory-recall"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","push-settle","orchestration-record","telemetry-usage","mcp-usage-observation","tool-result-bytes","edit-shape","comment-discipline","language-mirror","design-pass","journal-record","spawn-guard-shadow"],"pre_compact":["language-mirror","rule-inject","journal-record"],"subagent_start":["subagent-ledger","journal-record"],"subagent_stop":["subagent-ledger","journal-record"],"agent_error":["host-rate-marker"],"instructions_loaded":["instructions-observer"]},"cowork":{"ask":"text","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","minimal-safe-diff","profile-staleness","wrapper-freshness","surface-probe","session-register"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"pre_tool_use":["block-no-verify","evidence-independence","block-kernel-rule-writes","block-plumbing-writes","block-config-weakening","block-speaking-inbox-dir","block-credential-reads","rtk-wrap","design-slop","ui-route-nudge","code-graph-context","chain-nudge","ship-diff-volume","reread-guard","spawn-guard-shadow","source-first-gate","memory-recall"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","push-settle","orchestration-record","telemetry-usage","mcp-usage-observation","tool-result-bytes","edit-shape","comment-discipline","design-pass","spawn-guard-shadow"],"subagent_start":["subagent-ledger"],"subagent_stop":["subagent-ledger"]},"cursor":{"ask":"text","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","minimal-safe-diff","profile-staleness","wrapper-freshness","surface-probe","session-register"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","push-settle","orchestration-record","telemetry-usage","mcp-usage-observation","tool-result-bytes","edit-shape","comment-discipline","design-pass"]},"cline":{"ask":"text","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","minimal-safe-diff","profile-staleness","wrapper-freshness","surface-probe","session-register"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","push-settle","orchestration-record","telemetry-usage","mcp-usage-observation","tool-result-bytes","edit-shape","comment-discipline","design-pass"]},"windsurf":{"ask":"text","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","profile-staleness","wrapper-freshness","surface-probe","session-register"],"stop":["chat-history","verify-before-complete","self-repair","session-register","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","language-mirror","git-authorization","session-canary","self-repair","session-register"]},"gemini":{"ask":"text","session_start":["chat-history","hot-context","handoff-context","session-canary","council-availability","telemetry-disclosure","first-run-gate","onboarding-gate","verify-before-complete","minimal-safe-diff","profile-staleness","wrapper-freshness","surface-probe","session-register"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","push-settle","orchestration-record","telemetry-usage","mcp-usage-observation","tool-result-bytes","edit-shape","comment-discipline","design-pass"]},"copilot":{"ask":"text","fallback_only":true}},"native_event_aliases":{"augment":{"SessionStart":"session_start","SessionEnd":"session_end","Stop":"stop","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use"},"claude":{"SessionStart":"session_start","SessionEnd":"session_end","Stop":"stop","UserPromptSubmit":"user_prompt_submit","PostToolUse":"post_tool_use","PostToolUseFailure":"post_tool_use","PreToolUse":"pre_tool_use","PreCompact":"pre_compact","SubagentStart":"subagent_start","SubagentStop":"subagent_stop","InstructionsLoaded":"instructions_loaded","StopFailure":"agent_error"},"cowork":{"SessionStart":"session_start","SessionEnd":"session_end","Stop":"stop","UserPromptSubmit":"user_prompt_submit","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use","PreCompact":"pre_compact","SubagentStart":"subagent_start","SubagentStop":"subagent_stop"},"cursor":{"sessionStart":"session_start","sessionEnd":"session_end","stop":"stop","beforeSubmitPrompt":"user_prompt_submit","postToolUse":"post_tool_use","preToolUse":"pre_tool_use","preCompact":"pre_compact"},"cline":{"TaskStart":"session_start","TaskResume":"session_start","TaskComplete":"session_end","TaskCancel":"stop","UserPromptSubmit":"user_prompt_submit","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use","PreCompact":"pre_compact"},"windsurf":{"pre_user_prompt":"user_prompt_submit","post_cascade_response":"stop","post_setup_worktree":"session_start"},"gemini":{"SessionStart":"session_start","SessionEnd":"session_end","BeforeAgent":"user_prompt_submit","AfterAgent":"stop","BeforeTool":"pre_tool_use","AfterTool":"post_tool_use"}}}}