@event4u/agent-config 14.17.0 → 14.19.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 (184) 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 +207 -77
  5. package/README.md +72 -150
  6. package/dist/agent-src/README.md +1 -1
  7. package/dist/agent-src/commands/agent-handoff.md +2 -2
  8. package/dist/agent-src/commands/pr/merge.md +35 -44
  9. package/dist/agent-src/commands/review/changes.md +19 -19
  10. package/dist/agent-src/commands/review.md +2 -2
  11. package/dist/agent-src/contexts/execution/host-capability-manifest.md +30 -3
  12. package/dist/agent-src/rules/autonomous-execution.md +1 -1
  13. package/dist/agent-src/rules/git-history-discipline.md +2 -2
  14. package/dist/agent-src/rules/guidelines.md +1 -1
  15. package/dist/agent-src/rules/session-canary.md +2 -2
  16. package/dist/agent-src/rules/token-efficiency.md +1 -1
  17. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +2 -0
  18. package/dist/agent-src/skills/adversarial-review/SKILL.md +0 -8
  19. package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +27 -1
  20. package/dist/agent-src/skills/canvas-design/SKILL.md +1 -1
  21. package/dist/agent-src/skills/code-intelligence/SKILL.md +60 -12
  22. package/dist/agent-src/skills/complexity-first-planning/SKILL.md +27 -0
  23. package/dist/agent-src/skills/design-intelligence/SKILL.md +1 -1
  24. package/dist/agent-src/skills/design-intelligence/data/app-interface.csv +1 -1
  25. package/dist/agent-src/skills/design-intelligence/data/motion.csv +17 -17
  26. package/dist/agent-src/skills/design-intelligence/data/ux-guidelines.csv +2 -2
  27. package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +17 -0
  28. package/dist/agent-src/skills/design-intelligence/references/design-rules-checklist.md +4 -4
  29. package/dist/agent-src/skills/design-review/SKILL.md +3 -1
  30. package/dist/agent-src/skills/design-variations/SKILL.md +1 -1
  31. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +1 -1
  32. package/dist/agent-src/skills/fe-design/SKILL.md +32 -5
  33. package/dist/agent-src/skills/fe-design/references/craft-floor.md +7 -0
  34. package/dist/agent-src/skills/fe-design/references/design-patterns.md +47 -2
  35. package/dist/agent-src/skills/feature-planning/SKILL.md +34 -0
  36. package/dist/agent-src/skills/git-workflow/SKILL.md +37 -12
  37. package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +176 -0
  38. package/dist/agent-src/skills/laravel-mail/SKILL.md +44 -0
  39. package/dist/agent-src/skills/laravel-scheduling/SKILL.md +3 -1
  40. package/dist/agent-src/skills/project-analyzer/SKILL.md +0 -6
  41. package/dist/agent-src/skills/react-native-setup/SKILL.md +3 -3
  42. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
  43. package/dist/agent-src/skills/sequential-thinking/SKILL.md +18 -8
  44. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  45. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +40 -0
  46. package/dist/agent-src/skills/ui-apply-generic/SKILL.md +1 -1
  47. package/dist/agent-src/skills/ui-component-architect/SKILL.md +1 -1
  48. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +29 -2
  49. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +53 -8
  50. package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +3 -1
  51. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  52. package/dist/cli/commands/recordTriggerEval.js +2 -2
  53. package/dist/cli/main.js +1 -1
  54. package/dist/cli-delegate/{chunk-AKJGUQNO.js → chunk-GZTMRYCN.js} +405 -341
  55. package/dist/cli-delegate/{chunk-7RXIKF62.js → chunk-IDHUQIXP.js} +283 -132
  56. package/dist/cli-delegate/cmd_adr_effective.js +318 -81
  57. package/dist/cli-delegate/cmd_conformance.js +749 -669
  58. package/dist/cli-delegate/cmd_doctor.js +1 -1
  59. package/dist/cli-delegate/cmd_export.js +1 -1
  60. package/dist/cli-delegate/cmd_handoff.js +1 -1
  61. package/dist/cli-delegate/cmd_prune.js +1 -1
  62. package/dist/cli-delegate/cmd_route_audit.js +1 -1
  63. package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
  64. package/dist/cli-delegate/cmd_sync.js +1 -1
  65. package/dist/cli-delegate/cmd_uninstall.js +1 -1
  66. package/dist/cli-delegate/cmd_validate.js +1 -1
  67. package/dist/discovery/deprecation-report.md +1 -1
  68. package/dist/discovery/discovery-manifest.json +39 -38
  69. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  70. package/dist/discovery/discovery-manifest.summary.md +1 -1
  71. package/dist/discovery/dormancy-report.md +1 -1
  72. package/dist/discovery/orphan-report.md +1 -1
  73. package/dist/discovery/packs.json +2 -2
  74. package/dist/discovery/trust-report.md +1 -1
  75. package/dist/discovery/workspaces.json +2 -2
  76. package/dist/hooks/dispatch.js +2764 -2439
  77. package/dist/install/install.mjs +588 -527
  78. package/dist/mcp/registry-manifest.json +2 -2
  79. package/dist/mcp/server.json +2 -2
  80. package/dist/mcp/server.mjs +1381 -1138
  81. package/dist/scripts/_lib/model_tier.js +1 -1
  82. package/dist/shared/settingsConsent.js +1 -1
  83. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +1 -1
  84. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  85. package/docs/guidelines/design-antipatterns.md +87 -64
  86. package/package.json +1 -1
  87. package/src/agent-src/README.md +1 -1
  88. package/src/config/ci-local-parity.yml +6 -0
  89. package/src/config/conformance-claim-baseline.json +9 -0
  90. package/src/config/gate-coverage.yml +170 -0
  91. package/src/config/gate-violation-baselines.json +4 -4
  92. package/src/config/release-gate-locality.yml +141 -0
  93. package/src/scripts/_cli/cmd_doctor.ts +2 -2
  94. package/src/scripts/_cli/cmd_handoff.ts +1 -1
  95. package/src/scripts/_cli/cmd_update.ts +1 -1
  96. package/src/scripts/_cli/cmd_versions.ts +1 -1
  97. package/src/scripts/_lib/adr_frontmatter.ts +44 -0
  98. package/src/scripts/_lib/consequence_objects.ts +152 -0
  99. package/src/scripts/_lib/exec_evidence.ts +7 -0
  100. package/src/scripts/_lib/global_deploy_inventory.ts +2 -2
  101. package/src/scripts/_lib/host_permission_checks.ts +251 -0
  102. package/src/scripts/_lib/model_tier.ts +1 -1
  103. package/src/scripts/_lib/obligation_frequency.ts +2 -2
  104. package/src/scripts/_lib/release_highlights.ts +265 -1
  105. package/src/scripts/_lib/release_material.ts +129 -0
  106. package/src/scripts/_lib/repeated_run.ts +77 -0
  107. package/src/scripts/_lib/runtime_journal.ts +26 -3
  108. package/src/scripts/_lib/runtime_wiring_checks.ts +12 -1
  109. package/src/scripts/_lib/session_register.ts +11 -6
  110. package/src/scripts/_lib/sqlite_schema.ts +22 -0
  111. package/src/scripts/_lib/surface.ts +86 -0
  112. package/src/scripts/_lib/tamper_vocabulary.ts +242 -0
  113. package/src/scripts/_lib/ui_authority.ts +116 -0
  114. package/src/scripts/_lib/unified_diff.ts +121 -0
  115. package/src/scripts/adr/regenerate_index.ts +54 -1
  116. package/src/scripts/adr_cite_check.ts +71 -15
  117. package/src/scripts/ai_council/clients.ts +1 -1
  118. package/src/scripts/autonomy_friction_corpus.ts +229 -0
  119. package/src/scripts/build_proof.ts +15 -2
  120. package/src/scripts/check_beta_review_markers.ts +141 -17
  121. package/src/scripts/check_enforcement_coverage.ts +1 -1
  122. package/src/scripts/check_enforcement_denominator.ts +121 -11
  123. package/src/scripts/check_finding_dispositions.ts +239 -6
  124. package/src/scripts/check_gate_coverage.ts +96 -0
  125. package/src/scripts/check_installed_hooks_fresh.ts +381 -0
  126. package/src/scripts/check_release_highlights.ts +99 -14
  127. package/src/scripts/check_skill_activation_claim.ts +179 -0
  128. package/src/scripts/check_suppression_hygiene.ts +11 -0
  129. package/src/scripts/check_trigger_evals.ts +1 -1
  130. package/src/scripts/code_graph/build.ts +230 -33
  131. package/src/scripts/code_graph/extract.ts +168 -33
  132. package/src/scripts/code_graph/types.ts +37 -2
  133. package/src/scripts/code_graph/validate.ts +12 -1
  134. package/src/scripts/conformance_scan.ts +45 -4
  135. package/src/scripts/detect_silent_catch.ts +403 -0
  136. package/src/scripts/detect_verification_tampering.ts +538 -0
  137. package/src/scripts/explain_run.ts +1 -1
  138. package/src/scripts/generate_capability_matrix.ts +1 -1
  139. package/src/scripts/git_authorization_hook.ts +228 -18
  140. package/src/scripts/hook_manifest.json +1 -1
  141. package/src/scripts/hook_manifest.yaml +47 -30
  142. package/src/scripts/hooks/augment-dispatcher.sh +1 -1
  143. package/src/scripts/hooks/block_no_verify.ts +3 -3
  144. package/src/scripts/hooks/category_a.ts +291 -0
  145. package/src/scripts/hooks/cline-dispatcher.sh +1 -1
  146. package/src/scripts/hooks/concern_registry.ts +2 -2
  147. package/src/scripts/hooks/cursor-dispatcher.sh +11 -5
  148. package/src/scripts/hooks/dispatch_hook.ts +17 -22
  149. package/src/scripts/hooks/evidence_independence.ts +21 -2
  150. package/src/scripts/hooks/{block_unauthorized_git.ts → git_command_classifier.ts} +175 -364
  151. package/src/scripts/hooks/host_lowering.ts +215 -0
  152. package/src/scripts/hooks/host_lowering.yaml +185 -0
  153. package/src/scripts/hooks/host_semantics.ts +105 -37
  154. package/src/scripts/hooks/journal_record_hook.ts +10 -0
  155. package/src/scripts/hooks/permission_gate.ts +117 -0
  156. package/src/scripts/hooks/push_settle_hook.ts +250 -0
  157. package/src/scripts/hooks/ship_diff_volume_hook.ts +1 -1
  158. package/src/scripts/hooks_status.ts +12 -10
  159. package/src/scripts/install-hooks.sh +134 -1
  160. package/src/scripts/install.ts +53 -82
  161. package/src/scripts/install_anthropic_key.sh +2 -2
  162. package/src/scripts/language_mirror_hook.ts +27 -5
  163. package/src/scripts/lint_design_antipattern_parity.ts +80 -0
  164. package/src/scripts/lint_evidence_artifacts.ts +45 -1
  165. package/src/scripts/lint_hook_manifest.ts +87 -3
  166. package/src/scripts/lint_motion_authority_drift.ts +731 -0
  167. package/src/scripts/lint_readme_size.ts +158 -12
  168. package/src/scripts/lint_reasoning_fixtures.ts +216 -0
  169. package/src/scripts/lint_skill_trigger_contradiction.ts +161 -0
  170. package/src/scripts/measure_sibling_naming.ts +150 -0
  171. package/src/scripts/redact_hook_capture.ts +1 -1
  172. package/src/scripts/release.ts +31 -143
  173. package/src/scripts/release_drill.ts +71 -1
  174. package/src/scripts/release_publication.ts +243 -3
  175. package/src/scripts/release_verify.ts +220 -0
  176. package/src/scripts/report_skill_activation.ts +319 -7
  177. package/src/scripts/schemas/review-findings.schema.json +6 -1
  178. package/src/scripts/schemas/skill.schema.json +12 -2
  179. package/src/scripts/self_review_gate.ts +10 -1
  180. package/src/scripts/session_canary_hook.ts +1 -1
  181. package/src/scripts/session_register_hook.ts +4 -3
  182. package/src/scripts/trigger_eval_grandfather.json +5 -1
  183. package/src/scripts/validate_agent_settings.ts +1 -1
  184. package/src/shared/settingsConsent.ts +1 -1
@@ -36,6 +36,37 @@ export interface RawEdge {
36
36
  enclosingClassId?: string;
37
37
  /** scope token for `Scope::m()` scoped calls (a class name / self / static). */
38
38
  scopeName?: string;
39
+ /**
40
+ * `imports` only: the module specifier as written (`'./types.js'`,
41
+ * `'node:path'`), stripped of its quotes.
42
+ *
43
+ * Before this field the extractor read the import CLAUSE and threw the
44
+ * `from` away, so the build pass had nothing to resolve against but a
45
+ * repo-wide same-name table — which is how `import * as path from
46
+ * 'node:path'` came to bind to this repository's own `path()` function in
47
+ * `query.ts`, at `EXTRACTED`, in four of eleven files. The specifier is the
48
+ * one piece of evidence that says WHERE the name came from, and it is in
49
+ * the source text; discarding it turned a syntactic fact into a guess.
50
+ *
51
+ * Absent for PHP `use`, which names a fully-qualified symbol rather than a
52
+ * module path — those still resolve by name, and the build pass labels them
53
+ * accordingly.
54
+ */
55
+ moduleSpecifier?: string;
56
+ /**
57
+ * `imports` only: which import form produced this binding. `namespace`
58
+ * (`import * as x`) and `default` bind the MODULE (or its default export),
59
+ * never a named export, so neither may be matched against a same-named
60
+ * symbol inside the target file.
61
+ */
62
+ importKind?: 'namespace' | 'named' | 'default';
63
+ /**
64
+ * `imports` only: the LOCAL binding name, when it differs from
65
+ * `targetName` (`import { foo as bar }` → targetName `foo`, localName
66
+ * `bar`). The file's scope is keyed on the local name; the target file is
67
+ * searched for the remote one.
68
+ */
69
+ localName?: string;
39
70
  }
40
71
 
41
72
  export interface FileExtract {
@@ -245,29 +276,101 @@ function extractPhp(root: TsNode, file: string, out: FileExtract): void {
245
276
 
246
277
  // ── TypeScript / JavaScript ───────────────────────────────────────────────────
247
278
 
279
+ /**
280
+ * Node types that open a new function or class scope. A `const` inside one of
281
+ * these is a LOCAL variable, not a module-level declaration, and emitting a
282
+ * node for it would put every loop counter in the symbol table — the
283
+ * count-inflation failure `2.1` is explicitly not allowed to cause.
284
+ */
285
+ const TS_SCOPE_INTRODUCERS = new Set([
286
+ 'arrow_function',
287
+ 'function_expression',
288
+ 'function_declaration',
289
+ 'generator_function',
290
+ 'generator_function_declaration',
291
+ 'method_definition',
292
+ 'class_declaration',
293
+ 'class',
294
+ 'class_body',
295
+ ]);
296
+
248
297
  function extractTsJs(root: TsNode, file: string, out: FileExtract): void {
249
298
  const fileId = file;
250
299
 
251
- const walk = (n: TsNode, classId: string | null, scopeId: string): void => {
300
+ const walk = (n: TsNode, classId: string | null, scopeId: string, topLevel: boolean): void => {
301
+ // A declaration is module-level until the walk descends through a
302
+ // function or class scope; from there down it is a local.
303
+ const inner = topLevel && !TS_SCOPE_INTRODUCERS.has(n.type);
252
304
  switch (n.type) {
253
305
  case 'import_statement': {
306
+ const src = n.childForFieldName('source');
307
+ // The specifier node is a `string`; its text carries the quotes.
308
+ const specifier = src ? src.text.replace(/^['"`]/, '').replace(/['"`]$/, '') : undefined;
254
309
  const clause = firstNamed(n, new Set(['import_clause']));
310
+ const emitImport = (
311
+ remote: string,
312
+ local: string,
313
+ importKind: 'namespace' | 'named' | 'default',
314
+ ): void => {
315
+ out.rawEdges.push({
316
+ sourceId: fileId,
317
+ relation: 'imports',
318
+ targetName: baseName(remote),
319
+ confidenceHint: 'EXTRACTED',
320
+ ...(specifier === undefined ? {} : { moduleSpecifier: specifier }),
321
+ importKind,
322
+ ...(local === remote ? {} : { localName: local }),
323
+ });
324
+ };
255
325
  if (clause) {
256
- for (const spec of namedChildren(clause)) {
257
- for (const idn of [spec, ...namedChildren(spec)])
258
- if (idn.type === 'identifier' || idn.type === 'import_specifier') {
259
- const nm = idn.type === 'import_specifier' ? nameField(idn) ?? idn.text : idn.text;
260
- out.rawEdges.push({
261
- sourceId: fileId,
262
- relation: 'imports',
263
- targetName: baseName(nm),
264
- confidenceHint: 'EXTRACTED',
265
- });
326
+ for (const child of namedChildren(clause)) {
327
+ if (child.type === 'namespace_import') {
328
+ // `import * as path from 'node:path'` — the binding
329
+ // is the MODULE. Matching `path` against a local
330
+ // symbol of the same name is the false edge 1.1
331
+ // exists to remove.
332
+ const idn = firstNamed(child, new Set(['identifier']));
333
+ if (idn) emitImport(idn.text, idn.text, 'namespace');
334
+ } else if (child.type === 'named_imports') {
335
+ for (const sp of namedChildren(child)) {
336
+ if (sp.type !== 'import_specifier') continue;
337
+ const nameNode = sp.childForFieldName('name');
338
+ const aliasNode = sp.childForFieldName('alias');
339
+ const remote = nameNode ? nameNode.text : sp.text;
340
+ emitImport(remote, aliasNode ? aliasNode.text : remote, 'named');
266
341
  }
342
+ } else if (child.type === 'identifier') {
343
+ emitImport(child.text, child.text, 'default');
344
+ }
267
345
  }
268
346
  }
269
347
  return;
270
348
  }
349
+ // `const`, `type`, `interface`, `enum` — the shapes the v2 corpus
350
+ // asks `references` questions about (`EXT_LANG` is a const,
351
+ // `SettingsClass` a type alias). The extractor used to emit none of
352
+ // them, which is the named mechanical cause of `references` recall
353
+ // 0.333 against grep's 1.000.
354
+ //
355
+ // The body is not walked: a type or interface position holds no
356
+ // call sites, and an enum body holds member names rather than
357
+ // symbols this graph answers questions about.
358
+ case 'interface_declaration':
359
+ case 'type_alias_declaration':
360
+ case 'enum_declaration': {
361
+ const nmNode = n.childForFieldName('name') ?? firstNamed(n, NAME_TYPES);
362
+ const nm = nmNode ? nmNode.text : '<anon>';
363
+ const kind =
364
+ n.type === 'interface_declaration'
365
+ ? 'interface'
366
+ : n.type === 'type_alias_declaration'
367
+ ? 'type'
368
+ : 'enum';
369
+ const id = `${file}#${nm}`;
370
+ out.nodes.push({ id, label: nm, kind, source_file: file, source_location: loc(n) });
371
+ out.rawEdges.push({ sourceId: fileId, relation: 'member', targetName: nm, confidenceHint: 'EXTRACTED' });
372
+ return;
373
+ }
271
374
  case 'class_declaration': {
272
375
  const nm = nameField(n) ?? '<anon>';
273
376
  const id = `${file}#${nm}`;
@@ -280,7 +383,7 @@ function extractTsJs(root: TsNode, file: string, out: FileExtract): void {
280
383
  if (idn.type === 'identifier' || idn.type === 'type_identifier')
281
384
  out.inherits.push({ classId: id, parentName: baseName(idn.text) });
282
385
  const body = n.childForFieldName('body');
283
- if (body) for (const c of namedChildren(body)) walk(c, id, id);
386
+ if (body) for (const c of namedChildren(body)) walk(c, id, id, false);
284
387
  return;
285
388
  }
286
389
  case 'method_definition': {
@@ -294,7 +397,7 @@ function extractTsJs(root: TsNode, file: string, out: FileExtract): void {
294
397
  confidenceHint: 'EXTRACTED',
295
398
  });
296
399
  const body = n.childForFieldName('body');
297
- if (body) for (const c of namedChildren(body)) walk(c, classId, id);
400
+ if (body) for (const c of namedChildren(body)) walk(c, classId, id, false);
298
401
  return;
299
402
  }
300
403
  case 'function_declaration': {
@@ -303,7 +406,7 @@ function extractTsJs(root: TsNode, file: string, out: FileExtract): void {
303
406
  out.nodes.push({ id, label: nm, kind: 'function', source_file: file, source_location: loc(n) });
304
407
  out.rawEdges.push({ sourceId: fileId, relation: 'member', targetName: nm, confidenceHint: 'EXTRACTED' });
305
408
  const body = n.childForFieldName('body');
306
- if (body) for (const c of namedChildren(body)) walk(c, classId, id);
409
+ if (body) for (const c of namedChildren(body)) walk(c, classId, id, false);
307
410
  return;
308
411
  }
309
412
  // Modern TS declares most of its functions as bindings, not as
@@ -313,17 +416,58 @@ function extractTsJs(root: TsNode, file: string, out: FileExtract): void {
313
416
  // 170-vs-13,428 TS/PHP symbol gap: the six handled kinds simply do
314
417
  // not cover the dominant declaration form.
315
418
  //
316
- // Scoped deliberately to bindings whose value IS a function. A
317
- // `const x = 3` is data, not a symbol this graph answers questions
318
- // about, and emitting a node per constant would inflate the count
319
- // without improving recall — which is the cosmetic-improvement
320
- // failure the phase falsifier exists to catch.
419
+ // The scoping note that stood here said a `const x = 3` is "data,
420
+ // not a symbol this graph answers questions about", and that
421
+ // emitting a node per constant would raise the count without
422
+ // improving recall. **The v2 benchmark falsified the first half and
423
+ // the second half no longer follows.** Two of its three
424
+ // `references` questions probe exactly this shape — `EXT_LANG` (a
425
+ // const) and `SettingsClass` (a type alias) — and the class scored
426
+ // recall 0.333 against grep's 1.000 because neither had a node to
427
+ // resolve to. The count-inflation worry was real and is answered by
428
+ // SCOPE rather than by refusal: a binding earns a node only at
429
+ // MODULE level (`topLevel`), so a loop counter inside a function
430
+ // body still gets none. The reason is kept rather than deleted,
431
+ // because a reversed decision whose reason disappears gets re-taken.
321
432
  case 'lexical_declaration':
322
433
  case 'variable_declaration': {
323
434
  for (const d of namedChildren(n)) {
324
435
  if (d.type !== 'variable_declarator') continue;
325
436
  const value = d.childForFieldName('value');
326
- if (!value || (value.type !== 'arrow_function' && value.type !== 'function_expression')) {
437
+ const nmNode = d.childForFieldName('name');
438
+ const isFn =
439
+ value !== null &&
440
+ value !== undefined &&
441
+ (value.type === 'arrow_function' || value.type === 'function_expression');
442
+ const isClassExpr = value?.type === 'class';
443
+ if (!isFn) {
444
+ // Only a plain `identifier` name: a destructuring
445
+ // pattern (`const { a, b } = x`) binds several names and
446
+ // none of them is a declaration this graph can id.
447
+ if (topLevel && nmNode?.type === 'identifier') {
448
+ const nm = nmNode.text;
449
+ const id = classId ? `${classId}::${nm}` : `${file}#${nm}`;
450
+ out.nodes.push({
451
+ id,
452
+ label: nm,
453
+ // `const Widget = class {}` IS constructible;
454
+ // calling it a constant would let the build pass
455
+ // reject a `new Widget()` that is perfectly real.
456
+ kind: isClassExpr ? 'class' : 'constant',
457
+ source_file: file,
458
+ source_location: loc(d),
459
+ });
460
+ out.rawEdges.push({
461
+ sourceId: classId ?? fileId,
462
+ relation: 'member',
463
+ targetName: nm,
464
+ confidenceHint: 'EXTRACTED',
465
+ });
466
+ }
467
+ // Non-function declarators still need walking for their
468
+ // calls — returning early on the whole declaration
469
+ // silently drops call edges from initialisers.
470
+ walk(d, classId, scopeId, inner);
327
471
  continue;
328
472
  }
329
473
  const nm = nameField(d) ?? '<anon>';
@@ -353,16 +497,7 @@ function extractTsJs(root: TsNode, file: string, out: FileExtract): void {
353
497
  // can iterate children because a `statement_block` is never
354
498
  // the interesting node; here it often is.
355
499
  const body = value.childForFieldName('body');
356
- if (body) walk(body, classId, id);
357
- }
358
- // Non-function declarators still need walking for their calls.
359
- for (const d of namedChildren(n)) {
360
- if (d.type !== 'variable_declarator') continue;
361
- const value = d.childForFieldName('value');
362
- if (value && (value.type === 'arrow_function' || value.type === 'function_expression')) {
363
- continue;
364
- }
365
- walk(d, classId, scopeId);
500
+ if (body) walk(body, classId, id, false);
366
501
  }
367
502
  return;
368
503
  }
@@ -388,7 +523,7 @@ function extractTsJs(root: TsNode, file: string, out: FileExtract): void {
388
523
  confidenceHint: 'EXTRACTED',
389
524
  });
390
525
  const body = value.childForFieldName('body');
391
- if (body) walk(body, classId, id);
526
+ if (body) walk(body, classId, id, false);
392
527
  return;
393
528
  }
394
529
  break;
@@ -481,11 +616,11 @@ function extractTsJs(root: TsNode, file: string, out: FileExtract): void {
481
616
  default:
482
617
  break;
483
618
  }
484
- for (const c of namedChildren(n)) walk(c, classId, scopeId);
619
+ for (const c of namedChildren(n)) walk(c, classId, scopeId, inner);
485
620
  };
486
621
 
487
622
  out.nodes.push({ id: fileId, label: file, kind: 'file', source_file: file, source_location: [] });
488
- walk(root, null, fileId);
623
+ walk(root, null, fileId, true);
489
624
  }
490
625
 
491
626
  /**
@@ -21,7 +21,20 @@ export interface CodeNode {
21
21
  /** Path-qualified from day one: `<relpath>#<symbol>` (collision-free). */
22
22
  id: string;
23
23
  label: string;
24
- kind: 'file' | 'class' | 'interface' | 'trait' | 'function' | 'method' | 'skipped';
24
+ /**
25
+ * `constant` / `type` / `enum` were added by
26
+ * `road-to-the-graph-that-lies-confidently` 2.1: the v2 benchmark's
27
+ * `references` class probes a const (`EXT_LANG`) and a type alias
28
+ * (`SettingsClass`), and neither had a node to resolve to. `interface` is
29
+ * reused rather than forked — a TS interface and a PHP interface are the
30
+ * same thing to a caller.
31
+ *
32
+ * The kind is also a CAPABILITY claim the build pass reads: a `constant` is
33
+ * never callable and never constructible, so a bare `foo()` may not resolve
34
+ * to one by name. `const Widget = class {}` is emitted as `class` for
35
+ * exactly that reason.
36
+ */
37
+ kind: 'file' | 'class' | 'interface' | 'trait' | 'function' | 'method' | 'constant' | 'type' | 'enum' | 'skipped';
25
38
  source_file: string;
26
39
  /** 1-based [startLine, startCol, endLine, endCol]; [] for synthetic nodes. */
27
40
  source_location: readonly number[];
@@ -74,11 +87,33 @@ export interface CodeGraph {
74
87
  grammar_abi: number;
75
88
  /** per-confidence-class edge counts, so the honesty split is visible. */
76
89
  edge_confidence_counts: Record<EdgeConfidence, number>;
90
+ /**
91
+ * Edges the build pass REFUSED to emit, by reason. Present so a smaller
92
+ * edge total cannot be read as a better graph without saying what was
93
+ * dropped: an engine that quietly deletes its worst edges publishes a
94
+ * flattering confidence ratio and no evidence.
95
+ *
96
+ * `dynamic_no_candidate` — a dynamic member call (`x.push()`) whose method
97
+ * name matches NO method declared in this repository. The edge could only
98
+ * ever have pointed at a `symbol:` pseudo-node, which is not a node, so no
99
+ * query verb could reach it: `affected` and `path` resolve their seeds
100
+ * against real nodes. It was 257 of 660 edges on this engine's own source.
101
+ */
102
+ suppressed_edge_counts: { dynamic_no_candidate: number };
77
103
  nodes: CodeNode[];
78
104
  edges: CodeEdge[];
79
105
  }
80
106
 
81
- export const SCHEMA_VERSION = 1;
107
+ /**
108
+ * Bumped 1 → 2 by `road-to-the-graph-that-lies-confidently`: `CodeNode.kind`
109
+ * gained three members, `CodeGraph` gained `suppressed_edge_counts`, and the
110
+ * per-file extract sidecar gained the import specifier. A cached sidecar
111
+ * written at v1 carries raw edges with no `moduleSpecifier`, which would make
112
+ * `--update` resolve imports the old (name-lookup) way for every unchanged
113
+ * file — a silently mixed graph. `readSidecar` refuses a version mismatch, so
114
+ * the bump is what makes that impossible rather than merely unlikely.
115
+ */
116
+ export const SCHEMA_VERSION = 2;
82
117
 
83
118
  /** Deterministic per-file byte cap — files above this become a SKIPPED node. */
84
119
  export const MAX_FILE_BYTES = 1_000_000;
@@ -6,7 +6,18 @@ import type { CodeGraph } from './types.js';
6
6
 
7
7
  const RELATIONS = new Set(['calls', 'imports', 'uses', 'inherits', 'member']);
8
8
  const CONFIDENCES = new Set(['EXTRACTED', 'INFERRED', 'AMBIGUOUS']);
9
- const KINDS = new Set(['file', 'class', 'interface', 'trait', 'function', 'method', 'skipped']);
9
+ const KINDS = new Set([
10
+ 'file',
11
+ 'class',
12
+ 'interface',
13
+ 'trait',
14
+ 'function',
15
+ 'method',
16
+ 'constant',
17
+ 'type',
18
+ 'enum',
19
+ 'skipped',
20
+ ]);
10
21
 
11
22
  export interface ValidateResult {
12
23
  ok: boolean;
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * language-pin ← language_mirror_hook.classify
15
15
  * git-authorization ← git_authorization_hook.classifyAuthorization
16
- * + block_unauthorized_git.commandOp / BLOCK_OPS
16
+ * + git_command_classifier.commandOp / BLOCK_OPS
17
17
  * vacuous-evidence ← before_complete_hook.isVacuousOutput / isCiPoll / pendingCount
18
18
  * evidence-steering ← evidence_independence.isEvaluationPrompt / preloadedVerdict
19
19
  * completion-claim ← turn_end_gate_hook.detectCompletionClaim (round 7)
@@ -53,8 +53,13 @@ import { entryText, isSidechain } from "./_lib/transcript_entry.js";
53
53
 
54
54
  import { classify } from "./language_mirror_hook.js";
55
55
  import { isSyntheticPrompt } from "./_lib/prompt_shape.js";
56
- import { classifyAuthorization, type GitOp } from "./git_authorization_hook.js";
57
- import { BLOCK_OPS, commandOp } from "./hooks/block_unauthorized_git.js";
56
+ import {
57
+ classifyAuthorization,
58
+ extractObjects,
59
+ OBJECT_REQUIRED_OPS,
60
+ type GitOp,
61
+ } from "./git_authorization_hook.js";
62
+ import { BLOCK_OPS, commandObject, commandOp } from "./hooks/git_command_classifier.js";
58
63
  import { isVacuousOutput, isCiPoll, pendingCount } from "./before_complete_hook.js";
59
64
  // Round 7 § 1.5 — the SAME predicate `turn-end-gate` refuses on, imported rather
60
65
  // than reimplemented, so the measured rate and the gate cannot disagree.
@@ -579,6 +584,8 @@ export function scanSession(sessionId: string, lines: string[]): SessionReport {
579
584
  let pinned: "de" | "en" | null = null;
580
585
  let pendingPoll: { at: string; cmd: string } | null = null;
581
586
  let authorized = new Set<GitOp>();
587
+ // Per-op objects the current turn's prompt named. Reset with `authorized`.
588
+ let authorizedObjects = new Map<GitOp, Set<string>>();
582
589
  let sawPending = false;
583
590
  let evaluationsThisTurn = 0;
584
591
  // Round-5 provenance for language violations. The round-5 audit could only
@@ -723,7 +730,15 @@ export function scanSession(sessionId: string, lines: string[]): SessionReport {
723
730
  if (c.language !== "und") {
724
731
  pinned = c.language;
725
732
  }
726
- authorized = new Set(classifyAuthorization(ut).authorized);
733
+ const classified = classifyAuthorization(ut);
734
+ authorized = new Set(classified.authorized);
735
+ // Step 2.2 — the objects the turn NAMED, per operation. Rebuilt on every
736
+ // counted user turn for the same reason `authorized` is: an authorization
737
+ // for one table does not carry into the next prompt any more than the
738
+ // operation does.
739
+ authorizedObjects = new Map(
740
+ classified.authorized.map((op) => [op, new Set(extractObjects(op, ut))]),
741
+ );
727
742
  sawPending = false;
728
743
  evaluationsThisTurn = 0;
729
744
  pendingPoll = null;
@@ -811,6 +826,32 @@ export function scanSession(sessionId: string, lines: string[]): SessionReport {
811
826
  at,
812
827
  detail: `irreversible \`${op}\` with no authorization in the turn's prompt: ${cmd.slice(0, 110)}`,
813
828
  });
829
+ } else if (op !== null && authorized.has(op) && OBJECT_REQUIRED_OPS.has(op)) {
830
+ // Step 2.2 — the operation WAS authorized and ran against something
831
+ // the turn never named. An authorization for one table is not an
832
+ // authorization for another, and until the ledger could carry an
833
+ // object this was indistinguishable from a clean turn.
834
+ //
835
+ // This REPORTS and refuses nothing — ADR-254 removed the enforcing
836
+ // reader, and the shape it preserved is a count after the fact.
837
+ //
838
+ // A command naming NO object produces no finding: `commandObject`
839
+ // returns null there, and a guessed object would manufacture a
840
+ // violation out of a parse failure. Under-reporting is the safe
841
+ // direction for a measurement.
842
+ const object = commandObject(op, cmd);
843
+ const named = authorizedObjects.get(op);
844
+ if (object !== null && !(named?.has(object) ?? false)) {
845
+ report.violations.push({
846
+ check: "git-authorization",
847
+ session: sessionId,
848
+ at,
849
+ detail:
850
+ `\`${op}\` authorized, but its object \`${object}\` is not one the ` +
851
+ `turn's prompt named (${named && named.size > 0 ? [...named].join(", ") : "no object named"}): ` +
852
+ cmd.slice(0, 110),
853
+ });
854
+ }
814
855
  }
815
856
  if (isCiPoll(cmd)) {
816
857
  // The RESULT of this call arrives on the NEXT entry, and that entry is