@event4u/agent-config 14.7.0 → 14.9.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +175 -390
- package/README.md +1 -1
- package/agents/templates/.ai-council.yml.example +34 -4
- package/dist/agent-src/commands/evals/git-pr-merge.json +13 -0
- package/dist/agent-src/commands/evals/roadmap.json +17 -1
- package/dist/agent-src/commands/pr/create.md +14 -2
- package/dist/agent-src/commands/pr/merge.md +383 -0
- package/dist/agent-src/commands/review/changes.md +95 -14
- package/dist/agent-src/commands/roadmap/process-full.md +157 -3
- package/dist/agent-src/commands/roadmap.md +1 -1
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +46 -36
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +37 -2
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +77 -0
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +100 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +154 -2
- package/dist/agent-src/contexts/execution/subagent-response-contract.md +80 -0
- package/dist/agent-src/rules/artifact-drafting-protocol.md +5 -1
- package/dist/agent-src/rules/decision-revisit-gate.md +65 -33
- package/dist/agent-src/rules/recurring-criticism.md +1 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +19 -0
- package/dist/agent-src/rules/source-confidentiality.md +1 -1
- package/dist/agent-src/rules/untrusted-input-defense.md +9 -3
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +7 -4
- package/dist/agent-src/scripts/dashboard_mode.ts +212 -0
- package/dist/agent-src/scripts/update_roadmap_progress.ts +47 -48
- package/dist/agent-src/skills/adr-create/SKILL.md +124 -5
- package/dist/agent-src/skills/code-review/evals/evals.json +87 -46
- package/dist/agent-src/skills/decision-record/SKILL.md +61 -3
- package/dist/agent-src/skills/decision-review/SKILL.md +65 -0
- package/dist/agent-src/skills/humanizer/SKILL.md +39 -0
- package/dist/agent-src/skills/humanizer/evals/strip_fixtures.json +57 -0
- package/dist/agent-src/skills/humanizer/references/fixtures.md +54 -0
- package/dist/agent-src/skills/judge-spec-compliance/SKILL.md +154 -0
- package/dist/agent-src/skills/judge-synthesis/SKILL.md +45 -0
- package/dist/agent-src/skills/merge-conflicts/SKILL.md +26 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +18 -18
- package/dist/agent-src/skills/security/SKILL.md +8 -0
- package/dist/agent-src/skills/security-audit/SKILL.md +12 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +28 -2
- package/dist/cli/registry.js +3 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-FBKL6JWT.js → chunk-77GR3VD4.js} +68 -43
- package/dist/cli-delegate/{chunk-JZ4NR2TW.js → chunk-7PRE6ZV2.js} +1 -1
- package/dist/cli-delegate/{chunk-UEZ7D35A.js → chunk-CYH5SP6M.js} +4 -23
- package/dist/cli-delegate/{chunk-Q26HT4QT.js → chunk-E46KPN6K.js} +251 -150
- package/dist/cli-delegate/{chunk-74E3TKWV.js → chunk-H4VIVD4C.js} +5 -5
- package/dist/cli-delegate/chunk-I3EIN2YG.js +36 -0
- package/dist/cli-delegate/{chunk-4VR4ORQ6.js → chunk-LPPWO5CE.js} +4 -3
- package/dist/cli-delegate/{chunk-7F3VYSM2.js → chunk-PLSG6UL5.js} +14 -14
- package/dist/cli-delegate/cmd_adr_effective.js +1162 -0
- package/dist/cli-delegate/cmd_conformance.js +101 -25
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +7 -7
- package/dist/cli-delegate/cmd_export.js +6 -6
- package/dist/cli-delegate/cmd_handoff.js +7 -6
- package/dist/cli-delegate/cmd_memory_get.js +2 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_preflight.js +5 -5
- package/dist/cli-delegate/cmd_prune.js +6 -6
- package/dist/cli-delegate/cmd_route_audit.js +6 -5
- package/dist/cli-delegate/cmd_route_explain.js +3 -3
- package/dist/cli-delegate/cmd_session_recycle.js +3 -3
- package/dist/cli-delegate/cmd_settings_get.js +4 -4
- package/dist/cli-delegate/cmd_settings_migrate.js +6 -6
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +6 -6
- package/dist/cli-delegate/cmd_uninstall.js +6 -6
- package/dist/cli-delegate/cmd_update.js +3 -3
- package/dist/cli-delegate/cmd_upgrade.js +3 -3
- package/dist/cli-delegate/cmd_validate.js +6 -6
- package/dist/cli-delegate/cmd_versions.js +3 -3
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +83 -30
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +4 -4
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +11 -9
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +9 -7
- package/dist/hooks/dispatch.js +1295 -812
- package/dist/install/globalRuleLayers.js +169 -0
- package/dist/install/globalRuleLayers.js.map +1 -0
- package/dist/install/partitionEligibility.js +156 -0
- package/dist/install/partitionEligibility.js.map +1 -1
- package/dist/install/ruleLayerPartition.js +157 -0
- package/dist/install/ruleLayerPartition.js.map +1 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1358 -805
- package/dist/scripts/_lib/claude_plugin.js +162 -0
- package/dist/scripts/_lib/claude_plugin.js.map +1 -0
- package/dist/scripts/_lib/environment_detector.js +397 -0
- package/dist/scripts/_lib/environment_detector.js.map +1 -0
- package/dist/scripts/_lib/prune_empty_dirs.js +85 -0
- package/dist/scripts/_lib/prune_empty_dirs.js.map +1 -0
- package/dist/scripts/_lib/spawn_env.js +129 -0
- package/dist/scripts/_lib/spawn_env.js.map +1 -0
- package/dist/scripts/ai_council/modes.js +199 -0
- package/dist/scripts/ai_council/modes.js.map +1 -0
- package/dist/scripts/ai_council/transport_resolver.js +418 -0
- package/dist/scripts/ai_council/transport_resolver.js.map +1 -0
- package/dist/scripts/condense.js +52 -89
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/routes/wizard.js +3 -2
- package/dist/server/routes/wizard.js.map +1 -1
- package/dist/ui/assets/index-Cxgy63kz.js +26 -0
- package/dist/ui/assets/index-Cxgy63kz.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +45 -1
- package/docs/guidelines/agent-infra/false-green.md +47 -1
- package/docs/guidelines/agent-infra/traceability-field-mechanics.md +50 -0
- package/docs/guidelines/agent-infra/untrusted-input-spotlighting.md +17 -0
- package/package.json +7 -3
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +7 -4
- package/src/agent-src/scripts/dashboard_mode.ts +212 -0
- package/src/agent-src/scripts/update_roadmap_progress.ts +47 -48
- package/src/config/ci-local-parity.yml +21 -0
- package/src/config/estate-count-budget.json +13 -471
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +458 -12
- package/src/config/gate-violation-baselines.json +18 -8
- package/src/config/gitignore-block.txt +15 -2
- package/src/config/hook-latency-budget.json +5 -0
- package/src/config/publish-surface.json +60 -0
- package/src/config/requirements-trace-budget.json +32 -0
- package/src/config/rule-stub-ceilings.json +25 -4
- package/src/install/globalRuleLayers.ts +197 -0
- package/src/install/partitionEligibility.ts +177 -0
- package/src/install/ruleLayerPartition.ts +212 -0
- package/src/scripts/_cli/cmd_adr_effective.ts +484 -0
- package/src/scripts/_dispatch.bash +18 -0
- package/src/scripts/_lib/adr_frontmatter.test.ts +365 -0
- package/src/scripts/_lib/adr_frontmatter.ts +459 -0
- package/src/scripts/_lib/base_tree.ts +175 -0
- package/src/scripts/_lib/billing_grant.ts +165 -0
- package/src/scripts/_lib/billing_grant_cli.ts +123 -0
- package/src/scripts/_lib/council_fallback_posture.ts +53 -6
- package/src/scripts/_lib/council_fallback_wiring.ts +61 -1
- package/src/scripts/_lib/envelope_grounding.ts +11 -1
- package/src/scripts/_lib/host_capability.ts +59 -5
- package/src/scripts/_lib/layer_overlap_notice.ts +11 -2
- package/src/scripts/_lib/prune_empty_dirs.ts +80 -0
- package/src/scripts/_lib/quota_parked.ts +133 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +62 -0
- package/src/scripts/_lib/review_telemetry.ts +196 -0
- package/src/scripts/_lib/subagent_response.ts +92 -0
- package/src/scripts/adr/evidence_census.ts +899 -0
- package/src/scripts/adr/regenerate_index.ts +25 -38
- package/src/scripts/adr_cite_check.ts +540 -39
- package/src/scripts/ai_council/clients.ts +12 -1
- package/src/scripts/ai_council/config.ts +72 -115
- package/src/scripts/ai_council/fallback_config.ts +19 -7
- package/src/scripts/ai_council/mid_flight_fallback.ts +88 -7
- package/src/scripts/ai_council/orchestrator.ts +2 -2
- package/src/scripts/ai_council/py_format.ts +124 -0
- package/src/scripts/ai_council/transport_resolver.ts +65 -2
- package/src/scripts/audit_adr_coverage.ts +134 -21
- package/src/scripts/bench_hook_latency.ts +204 -0
- package/src/scripts/build_archive_index.ts +34 -0
- package/src/scripts/check_adr_frontmatter.ts +574 -39
- package/src/scripts/check_council_pin_staleness.ts +300 -0
- package/src/scripts/check_estate_count.ts +420 -293
- package/src/scripts/check_gate_coverage.ts +91 -0
- package/src/scripts/check_hook_bundle_content.ts +219 -0
- package/src/scripts/check_new_adr_evidence.ts +529 -0
- package/src/scripts/check_no_stub_inventory_table.ts +292 -0
- package/src/scripts/check_pack_size.ts +234 -1
- package/src/scripts/check_publish_surface.ts +297 -0
- package/src/scripts/check_references.ts +18 -0
- package/src/scripts/check_requirements_trace.ts +378 -0
- package/src/scripts/check_rule_layer_partition.ts +531 -0
- package/src/scripts/check_secret_leak.ts +166 -12
- package/src/scripts/check_single_delivery.ts +137 -4
- package/src/scripts/check_standing_rule_delivery.ts +8 -2
- package/src/scripts/code_graph/cli.ts +44 -5
- package/src/scripts/code_graph/extract.ts +87 -0
- package/src/scripts/condense.ts +61 -91
- package/src/scripts/council_cli.ts +12 -16
- package/src/scripts/detect_ai_tells.ts +87 -0
- package/src/scripts/dispatch_r2_reviewer.ts +42 -0
- package/src/scripts/external_sources_denylist.json +4 -1
- package/src/scripts/generate_capabilities_index.ts +41 -0
- package/src/scripts/git_authorization_hook.ts +54 -4
- package/src/scripts/hooks/block_unauthorized_git.ts +41 -3
- package/src/scripts/hooks/dispatch_hook.ts +154 -187
- package/src/scripts/hooks/dispatch_issues.ts +38 -19
- package/src/scripts/hooks/fallback_yaml.ts +66 -0
- package/src/scripts/hooks/host_semantics.ts +21 -0
- package/src/scripts/hooks/injection_budget.ts +13 -3
- package/src/scripts/hooks/py_json_dumps.ts +103 -0
- package/src/scripts/hooks/run_continuation_hook.ts +3 -1
- package/src/scripts/hooks/state_io.ts +88 -1
- package/src/scripts/hooks/stdin_failure_policy.ts +96 -0
- package/src/scripts/hooks/subagent_ledger_hook.ts +76 -11
- package/src/scripts/injection_scan_hook.ts +257 -15
- package/src/scripts/lint_adr_sweep_routing.ts +365 -0
- package/src/scripts/lint_hook_manifest.ts +49 -0
- package/src/scripts/lint_provenance_vocabulary.ts +472 -4
- package/src/scripts/lint_workflow_security.ts +53 -10
- package/src/scripts/lint_workflow_security_allowlist.json +2 -18
- package/src/scripts/report_envelope_rate.ts +166 -0
- package/src/scripts/review_axis_report.ts +218 -0
- package/src/scripts/routing_doctor.ts +5 -1
- package/src/scripts/run_supervise.ts +46 -1
- package/src/scripts/schemas/evals.schema.json +5 -0
- package/src/scripts/sync_pr_branch.ts +170 -20
- package/dist/cli/commands/commands.candidates.test.js +0 -257
- package/dist/cli/commands/commands.candidates.test.js.map +0 -1
- package/dist/cli/commands/profiles.test.js +0 -88
- package/dist/cli/commands/profiles.test.js.map +0 -1
- package/dist/cli/commands/recordTriggerEval.test.js +0 -113
- package/dist/cli/commands/recordTriggerEval.test.js.map +0 -1
- package/dist/cli/configRoot.test.js +0 -97
- package/dist/cli/configRoot.test.js.map +0 -1
- package/dist/cli/mcp/content.test.js +0 -54
- package/dist/cli/mcp/content.test.js.map +0 -1
- package/dist/cli/mcp/dispatch.test.js +0 -108
- package/dist/cli/mcp/dispatch.test.js.map +0 -1
- package/dist/server/writeRoot.test.js +0 -91
- package/dist/server/writeRoot.test.js.map +0 -1
- package/dist/shared/capabilities.test.js +0 -39
- package/dist/shared/capabilities.test.js.map +0 -1
- package/dist/ui/assets/index-C1pBRm1f.js +0 -26
- package/dist/ui/assets/index-C1pBRm1f.js.map +0 -1
- package/dist/cli-delegate/{chunk-PPQO4NGR.js → chunk-6RJA2F5A.js} +3 -3
- package/dist/cli-delegate/{chunk-6EOCNQAM.js → chunk-E4XIZDIB.js} +0 -0
- package/dist/cli-delegate/{chunk-GJ5BVHXM.js → chunk-PMEKGDED.js} +3 -3
|
@@ -1,59 +1,100 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
{
|
|
6
|
-
"id": "diff-with-multiple-issues",
|
|
7
|
-
"prompt": "Review this diff and list each distinct issue as its own bullet point:\n```js\nfunction pay(amount) {\n var x = amount\n eval(userInput)\n db.query('SELECT * FROM users WHERE id = ' + req.params.id)\n return x\n}\n```",
|
|
8
|
-
"assertions": [
|
|
2
|
+
"skill": "code-review",
|
|
3
|
+
"_calibration": "Behavioral fixtures incl. the U2 golden-adversarial pair (reliability-measurement, 2026-07-13). finding_floor is an enforcing mechanism since the 2026-07-13 cross-host calibration (internal/bench/reports/parity-count.json); these per-fixture n values are conservative floors derived from the fixture's planted-defect count. The golden-* pair is the review regression gate: the planted-injection diff must be flagged high/block, the benign look-alike must NOT be flagged as injection.",
|
|
4
|
+
"scenarios": [
|
|
9
5
|
{
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
"id": "diff-with-multiple-issues",
|
|
7
|
+
"prompt": "Review this diff and list each distinct issue as its own bullet point:\n```js\nfunction pay(amount) {\n var x = amount\n eval(userInput)\n db.query('SELECT * FROM users WHERE id = ' + req.params.id)\n return x\n}\n```",
|
|
8
|
+
"assertions": [
|
|
9
|
+
{
|
|
10
|
+
"kind": "finding_floor",
|
|
11
|
+
"n": 2
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "contains",
|
|
15
|
+
"value": "eval"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
12
18
|
},
|
|
13
19
|
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
"id": "clean-diff-few-findings",
|
|
21
|
+
"prompt": "Review this diff and list each distinct issue as its own bullet point:\n```js\nexport function add(a: number, b: number): number {\n return a + b\n}\n```",
|
|
22
|
+
"assertions": [
|
|
23
|
+
{
|
|
24
|
+
"kind": "finding_floor",
|
|
25
|
+
"n": 0
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
},
|
|
23
29
|
{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
"id": "golden-sqli-must-block",
|
|
31
|
+
"prompt": "Review this diff for a payments service and report each finding with a severity (critical/high/medium/low):\n```js\napp.get('/orders/:id', (req, res) => {\n db.query('SELECT * FROM orders WHERE id = ' + req.params.id).then(r => res.json(r));\n});\n```",
|
|
32
|
+
"assertions": [
|
|
33
|
+
{
|
|
34
|
+
"kind": "contains",
|
|
35
|
+
"value": "injection"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"kind": "finding_floor",
|
|
39
|
+
"n": 1,
|
|
40
|
+
"pattern": "(?i)critical|high|block"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
},
|
|
33
44
|
{
|
|
34
|
-
|
|
35
|
-
|
|
45
|
+
"id": "golden-benign-lookalike-no-injection",
|
|
46
|
+
"prompt": "Review this diff for a payments service and report each finding with a severity (critical/high/medium/low). If it is clean, say 'no findings':\n```js\napp.get('/orders/:id', (req, res) => {\n db.query('SELECT * FROM orders WHERE id = ?', [req.params.id]).then(r => res.json(r));\n});\n```",
|
|
47
|
+
"assertions": [
|
|
48
|
+
{
|
|
49
|
+
"kind": "not_contains",
|
|
50
|
+
"value": "SQL injection"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"kind": "not_contains",
|
|
54
|
+
"value": "sql injection"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
36
57
|
},
|
|
37
58
|
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
59
|
+
"id": "golden-spec-gap-correct-but-wrong",
|
|
60
|
+
"_why": "The scenario the corpus had no shape for. Correct, clean, typed, tested — and it does not do what the criterion asked. Before the spec judge joined the default path all five judges returned green on exactly this class, which is the miss this fixture records rather than merely describes. Its pair below is the negative control: same criterion, an implementation that DOES satisfy it, so a judge that flags every diff as non-compliant fails one of the two.",
|
|
61
|
+
"prompt": "ACCEPTANCE CRITERION: \"Retries use exponential backoff, so the delay between attempts grows.\"\n\nReview this diff against the criterion above and state, for the criterion, one of SATISFIED / PARTIAL / MISSING with the hunk and the test that proves it:\n```ts\n/** Retry with a delay between attempts. */\nexport async function retry<T>(fn: () => Promise<T>, attempts = 3, delayMs = 100): Promise<T> {\n let last: unknown;\n for (let i = 0; i < attempts; i += 1) {\n try {\n return await fn();\n } catch (err) {\n last = err;\n await new Promise((r) => setTimeout(r, delayMs));\n }\n }\n throw last;\n}\n```\n```ts\nit('retries three times then rethrows', async () => {\n let n = 0;\n await expect(retry(async () => { n += 1; throw new Error('x'); })).rejects.toThrow('x');\n expect(n).toBe(3);\n});\n```",
|
|
62
|
+
"assertions": [
|
|
63
|
+
{
|
|
64
|
+
"kind": "finding_floor",
|
|
65
|
+
"n": 1
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"kind": "contains",
|
|
69
|
+
"value": "backoff"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
},
|
|
48
73
|
{
|
|
49
|
-
|
|
50
|
-
|
|
74
|
+
"id": "golden-spec-gap-negative-control",
|
|
75
|
+
"_why": "Negative control for the pair above, on the SAME criterion. A judge that reports non-compliance on every diff would pass the fixture above for the wrong reason; this one fails it. The pair is what makes the spec axis falsifiable rather than merely present.",
|
|
76
|
+
"prompt": "ACCEPTANCE CRITERION: \"Retries use exponential backoff, so the delay between attempts grows.\"\n\nReview this diff against the criterion above and state, for the criterion, one of SATISFIED / PARTIAL / MISSING with the hunk and the test that proves it:\n```ts\nexport async function retry<T>(fn: () => Promise<T>, attempts = 3, baseMs = 100): Promise<T> {\n let last: unknown;\n for (let i = 0; i < attempts; i += 1) {\n try {\n return await fn();\n } catch (err) {\n last = err;\n await new Promise((r) => setTimeout(r, baseMs * 2 ** i));\n }\n }\n throw last;\n}\n```\n```ts\nit('grows the delay between attempts', async () => {\n const waits: number[] = [];\n vi.spyOn(global, 'setTimeout').mockImplementation(((cb: () => void, ms: number) => { waits.push(ms); cb(); return 0 as never; }) as never);\n await expect(retry(async () => { throw new Error('x'); }, 3, 100)).rejects.toThrow();\n expect(waits).toEqual([100, 200, 400]);\n});\n```",
|
|
77
|
+
"assertions": [
|
|
78
|
+
{
|
|
79
|
+
"kind": "contains",
|
|
80
|
+
"value": "SATISFIED"
|
|
81
|
+
}
|
|
82
|
+
]
|
|
51
83
|
},
|
|
52
84
|
{
|
|
53
|
-
|
|
54
|
-
|
|
85
|
+
"id": "golden-spec-no-criteria-must-not-pass",
|
|
86
|
+
"_why": "Step 1.3, and the case where inferring the spec from the diff is most tempting: an ad-hoc review with no criteria at all. A judge that infers the requirement from the change always finds the change compliant, so SATISFIED here is the circularity failure and not a lenient reading. The expected outcome is an explicit no-criteria verdict.",
|
|
87
|
+
"prompt": "No acceptance criteria were supplied for this review.\n\nReview this diff against its acceptance criteria and report the criteria-source state first, then any per-criterion verdicts:\n```ts\nexport function slugify(s: string): string {\n return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');\n}\n```",
|
|
88
|
+
"assertions": [
|
|
89
|
+
{
|
|
90
|
+
"kind": "contains",
|
|
91
|
+
"value": "not_provided"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"kind": "not_contains",
|
|
95
|
+
"value": "SATISFIED"
|
|
96
|
+
}
|
|
97
|
+
]
|
|
55
98
|
}
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
]
|
|
99
|
+
]
|
|
59
100
|
}
|
|
@@ -119,6 +119,21 @@ Write:
|
|
|
119
119
|
law instead of a decision under current conditions; see
|
|
120
120
|
[`decision-revisit-gate`](../../rules/decision-revisit-gate.md) for
|
|
121
121
|
what happens when a later change is blocked by it without one.
|
|
122
|
+
It is an externally observable **condition**, never a cadence and never
|
|
123
|
+
permanence under a field name: `terminal`, `none`, "never" and "forever"
|
|
124
|
+
are invalid, and `unclassified` is legal only on an *existing* record
|
|
125
|
+
during the migration — never on the one you are locking now.
|
|
126
|
+
- **Evidence + assumptions, split explicitly.** Every load-bearing factual
|
|
127
|
+
claim in the rationale either points at a basis ref — `file:line`, a URL, a
|
|
128
|
+
`docs/CLAIMS.md` claim id, a benchmark id — or is **labelled an
|
|
129
|
+
assumption**. There is no third state. Cite nothing and the record grades
|
|
130
|
+
`E0`, which is honest and publishable; a confident grade over no citations
|
|
131
|
+
is not. **Agreement is not evidence:** the council converging, or two
|
|
132
|
+
models liking the same option, is `provenance: agentic` with
|
|
133
|
+
`agentic_mode: council` — sources and measurements raise the grade,
|
|
134
|
+
consensus does not. A human product call records `E0` plus
|
|
135
|
+
`authority_basis: owner_intent` rather than a faked grade. Vocabulary:
|
|
136
|
+
[`adr-layout § Provenance and evidence`](../../../docs/contracts/adr-layout.md).
|
|
122
137
|
|
|
123
138
|
### 5. Wire the supersession chain (if any)
|
|
124
139
|
|
|
@@ -129,9 +144,34 @@ If this decision overrides a prior ADR:
|
|
|
129
144
|
not "we now think differently".
|
|
130
145
|
- Hand off the `supersedes:` linkage to `adr-create`.
|
|
131
146
|
|
|
132
|
-
### 6.
|
|
147
|
+
### 6. Admission gate — is this an ADR at all?
|
|
133
148
|
|
|
134
|
-
|
|
149
|
+
Classify **before** handing off. A locked choice earns an ADR only when it is
|
|
150
|
+
architecturally significant on at least one axis:
|
|
151
|
+
|
|
152
|
+
- **Hard or costly to reverse** — a one-way door: public API shape, DB schema,
|
|
153
|
+
wire format, published identifier, migration.
|
|
154
|
+
- **Broadly constraining** — it binds work outside the module that made it.
|
|
155
|
+
- **Crosses a governed surface** — consumer contract, API, security or privacy
|
|
156
|
+
floor, package structure.
|
|
157
|
+
|
|
158
|
+
None of the three → **no ADR**. The record still exists; it lands as a
|
|
159
|
+
decision note in `agents/decisions/`, a config value, a measurement record in
|
|
160
|
+
`docs/CLAIMS.md`, an experiment, or a roadmap item. Explicitly not ADRs: a
|
|
161
|
+
temporary numeric threshold · a benchmark value · a model mapping · one-off
|
|
162
|
+
release sequencing · a reversible local implementation detail.
|
|
163
|
+
|
|
164
|
+
**The reference case is in this tree.** ADR-002 encodes `25 000 → 26 000` and
|
|
165
|
+
a `4.0k` override ceiling as architecture law (`ADR-002:55`, `:62`), and
|
|
166
|
+
ADR-114 then had to add another override while recording that 7 of 9 kernel
|
|
167
|
+
rules already carry them (`ADR-114:74`). The *principle* — a kernel budget
|
|
168
|
+
exists, is measured, and is capped — is the ADR. The numbers belong in a
|
|
169
|
+
versioned budget contract with a regression gate, so a recalibration stops
|
|
170
|
+
needing an architecture supersession.
|
|
171
|
+
|
|
172
|
+
### 7. Hand off to file mechanics
|
|
173
|
+
|
|
174
|
+
Gate passed → output the structured payload (below). The user — or
|
|
135
175
|
`adr-create` — turns it into the file.
|
|
136
176
|
|
|
137
177
|
## Weighted-matrix mode (quantitative)
|
|
@@ -226,10 +266,22 @@ Consequences:
|
|
|
226
266
|
- <becomes harder>
|
|
227
267
|
✗ <becomes impossible>
|
|
228
268
|
|
|
269
|
+
Assumptions:
|
|
270
|
+
~ <load-bearing claim with no basis ref>
|
|
271
|
+
|
|
272
|
+
Evidence:
|
|
273
|
+
<basis ref> — file:line | URL | CLAIMS id | benchmark id (or: none — E0)
|
|
274
|
+
|
|
275
|
+
Provenance: human | agentic | mixed | unknown (agentic_mode: single | council | delegated)
|
|
276
|
+
Grade: E0 | E1 | E2 | E3 | E4 (authority_basis: evidence | owner_intent)
|
|
277
|
+
|
|
229
278
|
Supersedes: <ADR-XYZ "title"> (if any)
|
|
230
279
|
Trigger: <what changed> (if superseding)
|
|
280
|
+
Revisit-if: <observable condition>
|
|
281
|
+
|
|
282
|
+
Admission gate: ADR | decision note | config value | measurement | experiment | roadmap item
|
|
231
283
|
|
|
232
|
-
Next: /adr-create with the payload above
|
|
284
|
+
Next: /adr-create with the payload above (only when the gate says ADR)
|
|
233
285
|
```
|
|
234
286
|
|
|
235
287
|
## Gotcha
|
|
@@ -240,6 +292,12 @@ Next: /adr-create with the payload above
|
|
|
240
292
|
cons for the chosen option, you have not understood it yet.
|
|
241
293
|
- A supersession with no "what changed in the world" is a vibe
|
|
242
294
|
pivot. Reject and ask.
|
|
295
|
+
- An unlabelled guess is worse than a labelled one. A claim with no basis ref
|
|
296
|
+
and no assumption label reads as established fact to the next reader.
|
|
297
|
+
- "The council agreed" is a provenance fact, not an evidence grade. If the
|
|
298
|
+
only support is agreement, the grade is `E0` — write it.
|
|
299
|
+
- Writing an ADR for a threshold is how a recalibration becomes an
|
|
300
|
+
architecture supersession. Run the admission gate first.
|
|
243
301
|
|
|
244
302
|
## Do NOT
|
|
245
303
|
|
|
@@ -93,6 +93,31 @@ available at the time it was made**, not against the outcome alone.
|
|
|
93
93
|
A decision can be correct given the information then and still need
|
|
94
94
|
revision now.
|
|
95
95
|
|
|
96
|
+
**Read the record's axes in this order** — each step can end the review, so
|
|
97
|
+
running them out of order pays for the expensive ones first:
|
|
98
|
+
|
|
99
|
+
1. **Effective state** — status, `superseded_by`, amendments, whether the
|
|
100
|
+
`review_trigger` fired. `superseded`/`deprecated` is not a live lock at all.
|
|
101
|
+
2. **`provenance`** (+ `agentic_mode`) — who decided; descriptive only.
|
|
102
|
+
3. **`evidence.strength`** — E0–E4, claim-relative, not a count of sources.
|
|
103
|
+
4. **`evidence.discovery`** — on E0, `incomplete` means *nobody looked*: a
|
|
104
|
+
discovery failure, not an evidence failure. Search before treating the
|
|
105
|
+
absence as established, or the grade measures the review, not the decision.
|
|
106
|
+
5. **Current evidence** — what tree, measurements and sources say *today*.
|
|
107
|
+
6. **Reversibility** — of the transition you propose, not of the original
|
|
108
|
+
decision. The routing unit is the transition, never the document.
|
|
109
|
+
7. **Reserved dimensions** — `protected_dimensions`, `reopen_policy` (absent →
|
|
110
|
+
`unclassified`, which is not `owner`).
|
|
111
|
+
|
|
112
|
+
Then price the reopen record from the grade × provenance burden table in
|
|
113
|
+
[`adr-layout § The reopen record`](../../../docs/contracts/adr-layout.md) —
|
|
114
|
+
cheap for an `E0`/`E1` agentic snapshot, *surfaced* rather than obeyed or
|
|
115
|
+
overturned for `E0`/`E1` human, the standard five fields at `E2`, and those
|
|
116
|
+
fields plus engaging the original evidence **in kind** at `E3`/`E4`. Read the
|
|
117
|
+
table; do not reconstruct it from memory. The axes are defined by
|
|
118
|
+
[`adr-layout § Provenance and evidence`](../../../docs/contracts/adr-layout.md),
|
|
119
|
+
not here.
|
|
120
|
+
|
|
96
121
|
### 4. Verdict
|
|
97
122
|
|
|
98
123
|
One of three:
|
|
@@ -298,6 +323,40 @@ in the rule; the lock catalog, fire steps, and failure modes live here.
|
|
|
298
323
|
Identical ceremony for a typo-level amendment would cost more than asking
|
|
299
324
|
the owner and the mechanism would die of its own weight.
|
|
300
325
|
|
|
326
|
+
### The grade prices the record, never the authority
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
AN EVIDENCE GRADE IS A MEASUREMENT, NOT A PERMISSION.
|
|
330
|
+
IT PRICES THE REOPEN RECORD AND THE REVIEW PRIORITY. IT CONFERS NO AUTHORITY.
|
|
331
|
+
A LOW GRADE NEVER LETS AN AGENT SUPERSEDE A RECORD ON ITS OWN.
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Nothing in the burden table changes **who** may act: venue comes from the
|
|
335
|
+
discriminator, the trust boundaries the transition touches, and the
|
|
336
|
+
owner-reserved rows — never from the grade.
|
|
337
|
+
|
|
338
|
+
The coupling was proposed and refused: two council seats independently named it
|
|
339
|
+
the design's central defect, because the party assigning the grade would also
|
|
340
|
+
be the party gaining authority from it, and would additionally be
|
|
341
|
+
self-classifying its own transition as `reversible-internal` — itself an
|
|
342
|
+
authority-bearing classification. Calling the result "provisional" changes
|
|
343
|
+
neither the trust boundary nor the blast radius. Whether an *independently
|
|
344
|
+
validated* grade may ever reduce the authorization burden is owner-reserved and
|
|
345
|
+
open: do not answer it here — it is the `authority-coupling-decision` blocker in
|
|
346
|
+
`road-to-evidence-based-adr-governance`, and
|
|
347
|
+
[`adr-layout § The reopen record`](../../../docs/contracts/adr-layout.md) closes
|
|
348
|
+
on the same point.
|
|
349
|
+
|
|
350
|
+
Two consequences that are easy to get backwards:
|
|
351
|
+
|
|
352
|
+
- **An ADR's historical decision-maker does not determine its reopen venue.** A
|
|
353
|
+
record decided by a council does not need a council to reopen it; a record
|
|
354
|
+
decided by one agent is not thereby an agent's to overturn.
|
|
355
|
+
- **A low-evidence record may state a decision; it does not establish that the
|
|
356
|
+
alternatives remain invalid.** *"We chose B because we had to choose"* is
|
|
357
|
+
legitimate and publishable — but never grounds for *"A is forbidden, the ADR
|
|
358
|
+
says B"*, which needs evidence the record never carried.
|
|
359
|
+
|
|
301
360
|
### When NOT to fire
|
|
302
361
|
|
|
303
362
|
- The blocked change has no real benefit — the gate is not a lever to
|
|
@@ -324,6 +383,12 @@ in the rule; the lock catalog, fire steps, and failure modes live here.
|
|
|
324
383
|
value-over-budget clause.
|
|
325
384
|
- Silently dropping a good idea because "we already decided this" —
|
|
326
385
|
the canonical failure the gate exists to stop.
|
|
386
|
+
- Reading a low `evidence.strength` as permission to supersede — the grade
|
|
387
|
+
prices the record, it never moves the venue.
|
|
388
|
+
- Treating `discovery: incomplete` as established absence: that grades the
|
|
389
|
+
review, not the decision.
|
|
390
|
+
- Citing a low-evidence ADR as proof its rejected alternatives are still
|
|
391
|
+
invalid. It records a choice, not a refutation.
|
|
327
392
|
|
|
328
393
|
## See also
|
|
329
394
|
|
|
@@ -71,6 +71,34 @@ human voice there; do not inject personality or restructure it.
|
|
|
71
71
|
`npx tsx node_modules/@event4u/agent-config/src/scripts/detect_ai_tells.ts --stdin --fail`
|
|
72
72
|
on the final draft. No runtime → the step-3 audit is the fallback
|
|
73
73
|
(degrade, do not skip the audit).
|
|
74
|
+
5b. **Carrier-Unicode strip — OPT-IN, never a default.** Runs **only** when
|
|
75
|
+
the operator explicitly asks for a carrier strip. `stripCarrierUnicode`
|
|
76
|
+
(`node_modules/@event4u/agent-config/src/scripts/detect_ai_tells.ts`, the same
|
|
77
|
+
path step 5 invokes) removes a hidden-Unicode codepoint only
|
|
78
|
+
when the codepoints on **both** sides are ASCII or absent; anything adjacent
|
|
79
|
+
to a non-ASCII character is preserved, so an emoji ZWJ sequence and a
|
|
80
|
+
complex-script joiner survive byte-identically.
|
|
81
|
+
**Why opt-in.** A default strip is a silent edit to the operator's
|
|
82
|
+
deliverable, which step 6's factual-integrity guard forbids for every other
|
|
83
|
+
kind of edit. Without an explicit request this step does not run and the
|
|
84
|
+
output is byte-identical to what the skill produces without it.
|
|
85
|
+
**This is the OUTPUT direction, and it does not touch step 0.** Step 0 scans
|
|
86
|
+
*ingested* input and surfaces findings as a warning — it never strips, because
|
|
87
|
+
there the hidden characters are an injection vector and removing them destroys
|
|
88
|
+
the evidence. Here the prose is the suite's own output and the operator has
|
|
89
|
+
asked. Two directions, two policies; reading them as one is the mistake this
|
|
90
|
+
paragraph exists to prevent.
|
|
91
|
+
**Hygiene, not a security control.** The predicate is deliberately
|
|
92
|
+
conservative, so a carrier adjacent to any non-ASCII character survives. The
|
|
93
|
+
injection vector stays covered by step 0.
|
|
94
|
+
**Emit the audit line** — `removed` and `preserved` counts, the classes
|
|
95
|
+
removed, and the reason for each preservation. An unexplained preservation is
|
|
96
|
+
the interesting half: it is what tells the operator the predicate fired
|
|
97
|
+
conservatively rather than failed. A strip with no audit line is a silent
|
|
98
|
+
edit wearing a step number.
|
|
99
|
+
Worked before/after: [`references/fixtures.md`](references/fixtures.md)
|
|
100
|
+
Fixture 3. Cases: [`evals/strip_fixtures.json`](evals/strip_fixtures.json).
|
|
101
|
+
|
|
74
102
|
6. **Deterministic self-check + factual-integrity guard.** Re-scan the
|
|
75
103
|
final draft against the [`anti-aiisms.md`](references/anti-aiisms.md)
|
|
76
104
|
self-validation thresholds (dash density, consecutive-staccato cap,
|
|
@@ -189,6 +217,17 @@ do not infer a segment from the draft you were asked to edit.
|
|
|
189
217
|
voice fingerprint that uses dashes overrides even that.
|
|
190
218
|
- Do NOT shorten or restructure as a side effect — same coverage in,
|
|
191
219
|
same coverage out.
|
|
220
|
+
- Do NOT reach for `_sanitize`
|
|
221
|
+
(`node_modules/@event4u/agent-config/src/scripts/lint_hidden_unicode.ts`) or any
|
|
222
|
+
`NFKC` pass on a deliverable. It drops **every** `_classify`-flagged codepoint
|
|
223
|
+
unconditionally, and its class list contains `U+200C` and `U+200D` — so a
|
|
224
|
+
blind pass destroys emoji ZWJ sequences and complex-script joiners. It is a
|
|
225
|
+
file-repair path and correct for its own callers; on prose it corrupts the
|
|
226
|
+
text it was pointed at. Step 5b exists precisely because that shortcut is one
|
|
227
|
+
import away and looks like the same job.
|
|
228
|
+
- Do NOT add a statistical-watermark rewrite or any detector-evasion mode. That
|
|
229
|
+
exclusion already stands below and this skill's carrier strip does not weaken
|
|
230
|
+
it: the strip removes invisible characters and alters no visible prose.
|
|
192
231
|
|
|
193
232
|
## Gotcha
|
|
194
233
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Fixtures for stripCarrierUnicode (detect_ai_tells.ts). Six cases, reproduced offline against the real _classify export before the roadmap that added them was written. Every hidden codepoint is written as a JSON \\u escape and NEVER as a literal character: the first version of this file used literals, which (a) made the payloads invisible in review and (b) reddened lint_hidden_unicode with 20 blocking findings, since a tracked file carrying real carriers is exactly what that gate exists to refuse. JSON.parse decodes the escapes, so the test still receives the real characters at runtime.",
|
|
3
|
+
"recorded_limitation": {
|
|
4
|
+
"what": "The complex-script preservation cases are a CURATED BLOCK SET, not a Unicode Script-property lookup.",
|
|
5
|
+
"tested_blocks": [
|
|
6
|
+
"Arabic/Persian (U+0600-U+06FF)",
|
|
7
|
+
"emoji ZWJ sequences (U+1F400-U+1FAFF joined by U+200D)"
|
|
8
|
+
],
|
|
9
|
+
"untested": "Indic (Devanagari, Bengali, Tamil, ...), Thai, Khmer, Myanmar, Mongolian, and every other script that uses a format character mid-word.",
|
|
10
|
+
"why_this_is_acceptable": "The predicate errs toward PRESERVATION: a carrier is removed only when both neighbours are ASCII, so a format character inside a non-ASCII word is preserved whatever its script. An untested script is therefore preserved rather than corrupted — the failure mode cannot occur in the unsafe direction. A full Script-property table would be a new data dependency guarding a failure that cannot happen.",
|
|
11
|
+
"what_would_change_this": "A script whose format characters legitimately appear between two ASCII characters. None is known; one would make the block set insufficient rather than merely untested."
|
|
12
|
+
},
|
|
13
|
+
"cases": [
|
|
14
|
+
{
|
|
15
|
+
"name": "ASCII-flanked zero-width space is removed",
|
|
16
|
+
"input": "the\u200bword",
|
|
17
|
+
"expect_out": "theword",
|
|
18
|
+
"expect_removed": 1,
|
|
19
|
+
"expect_preserved": 0
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "ASCII-flanked right-to-left override is removed",
|
|
23
|
+
"input": "safe\u202efile.txt",
|
|
24
|
+
"expect_out": "safefile.txt",
|
|
25
|
+
"expect_removed": 1,
|
|
26
|
+
"expect_preserved": 0
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "ASCII-flanked Unicode-tag codepoint is removed",
|
|
30
|
+
"input": "a\udb40\udc41b",
|
|
31
|
+
"expect_out": "ab",
|
|
32
|
+
"expect_removed": 1,
|
|
33
|
+
"expect_preserved": 0
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "emoji ZWJ sequence survives byte-identically",
|
|
37
|
+
"input": "👨\u200d💻 ships it",
|
|
38
|
+
"expect_out": "👨\u200d💻 ships it",
|
|
39
|
+
"expect_removed": 0,
|
|
40
|
+
"expect_preserved": 1
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "Persian ZWNJ survives byte-identically",
|
|
44
|
+
"input": "می\u200cخواهم",
|
|
45
|
+
"expect_out": "می\u200cخواهم",
|
|
46
|
+
"expect_removed": 0,
|
|
47
|
+
"expect_preserved": 1
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "clean prose is a no-op, and the strip is idempotent",
|
|
51
|
+
"input": "Nothing hidden here — just prose with an em dash.",
|
|
52
|
+
"expect_out": "Nothing hidden here — just prose with an em dash.",
|
|
53
|
+
"expect_removed": 0,
|
|
54
|
+
"expect_preserved": 0
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
@@ -68,3 +68,57 @@ The two targets diverge on every axis — a draft matched to A is not a draft
|
|
|
68
68
|
matched to B. Sample B is also a case where the dash-density default is
|
|
69
69
|
**suppressed**: the fingerprint genuinely writes in dashes, so the fingerprint
|
|
70
70
|
wins (SKILL § voice precedence).
|
|
71
|
+
|
|
72
|
+
## Fixture 3 — carrier strip, and what "preserved" looks like (step 5b)
|
|
73
|
+
|
|
74
|
+
The point of this fixture is the **preserved** half. A reader who only sees
|
|
75
|
+
carriers disappear cannot tell a conservative predicate from a blind one, and a
|
|
76
|
+
blind one corrupts emoji and complex-script text. So the input carries both.
|
|
77
|
+
|
|
78
|
+
**Carriers are written as `U+XXXX` notation, never as the actual invisible
|
|
79
|
+
character.** Two reasons, and the second is a gate: an invisible byte is
|
|
80
|
+
unreadable in exactly the document whose job is showing what happened, and
|
|
81
|
+
`lint_hidden_unicode` refuses a tracked file carrying real carriers — its own
|
|
82
|
+
header says a teaching doc "writes `U+200B` as text". The first version of this
|
|
83
|
+
fixture used literals and reddened that gate with 20 blocking findings.
|
|
84
|
+
|
|
85
|
+
**Input** — `the⟦U+200B⟧word 👨⟦U+200D⟧💻 می⟦U+200C⟧خواهم safe⟦U+202E⟧file`
|
|
86
|
+
|
|
87
|
+
| Carrier | Neighbours | Decision |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| `U+200B` between `e` and `w` | ASCII / ASCII | **removed** |
|
|
90
|
+
| `U+200D` joining two emoji | non-ASCII / non-ASCII | preserved |
|
|
91
|
+
| `U+200C` inside a Persian word | non-ASCII / non-ASCII | preserved |
|
|
92
|
+
| `U+202E` between `e` and `f` | ASCII / ASCII | **removed** |
|
|
93
|
+
|
|
94
|
+
**Output** — `theword 👨⟦U+200D⟧💻 می⟦U+200C⟧خواهم safefile`
|
|
95
|
+
|
|
96
|
+
**Audit line** (generated by running the real function, not written by hand):
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
carrier strip: removed 2 (zero-width 1, bidi-control 1) · preserved 2
|
|
100
|
+
· U+200B zero-width offset 3 removed
|
|
101
|
+
· U+200D zero-width offset 10 preserved — adjacent to a non-ASCII codepoint, may be a joiner
|
|
102
|
+
· U+200C zero-width offset 15 preserved — adjacent to a non-ASCII codepoint, may be a joiner
|
|
103
|
+
· U+202E bidi-control offset 26 removed
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Three things a reader should take from this:
|
|
107
|
+
|
|
108
|
+
1. **`U+200B` and `U+200D` are the same class** — both `zero-width` — and get
|
|
109
|
+
opposite dispositions. The class is not the decision; the neighbours are.
|
|
110
|
+
Any strip keyed on class alone destroys the emoji.
|
|
111
|
+
2. **Offsets are codepoint indices, not UTF-16 units.** The emoji before
|
|
112
|
+
`U+200D` is a surrogate pair, so a UTF-16 index would report a different
|
|
113
|
+
number and the audit line would be wrong for exactly the inputs this
|
|
114
|
+
predicate exists to protect.
|
|
115
|
+
3. **A preservation always carries its reason.** An unexplained preservation
|
|
116
|
+
would be indistinguishable from a bug; the reason is what makes the
|
|
117
|
+
conservative branch auditable rather than merely quiet.
|
|
118
|
+
|
|
119
|
+
Re-running the strip over the output changes nothing — `removed 0 · preserved 2`.
|
|
120
|
+
Idempotence is a property of the predicate, not a coincidence: the two survivors
|
|
121
|
+
still have non-ASCII neighbours the second time round.
|
|
122
|
+
|
|
123
|
+
The runnable form of all six cases, with the carriers as JSON `\u` escapes, is
|
|
124
|
+
[`evals/strip_fixtures.json`](../evals/strip_fixtures.json).
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
---
|
|
2
|
+
model_tier: high
|
|
3
|
+
name: judge-spec-compliance
|
|
4
|
+
description: "Use when a diff needs a requirement review — does it satisfy every stated acceptance criterion — dispatched by /review-changes, /do-and-judge, /judge. Never infers criteria from the diff."
|
|
5
|
+
domain: quality
|
|
6
|
+
parallelizable: files
|
|
7
|
+
workspaces:
|
|
8
|
+
- agent-config-maintainer
|
|
9
|
+
packs:
|
|
10
|
+
- meta
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# judge-spec-compliance
|
|
14
|
+
|
|
15
|
+
> You are a judge specialized in **requirement compliance**. Your only
|
|
16
|
+
> job is: **does the diff satisfy every acceptance criterion as
|
|
17
|
+
> stated?** You do **not** review style, naming, craft, correctness,
|
|
18
|
+
> security or test coverage — five other judges handle those, and a
|
|
19
|
+
> finding of theirs is not a finding of yours.
|
|
20
|
+
|
|
21
|
+
The wording above is the SPEC COMPLIANCE judge from
|
|
22
|
+
[`do-and-judge-two-stage`](../subagent-orchestration/prompts/do-and-judge-two-stage.md)
|
|
23
|
+
§ Stage-1 prompt, reused rather than reinvented. That prompt worked and was
|
|
24
|
+
reachable only through one orchestration mode; this skill is the same job on the
|
|
25
|
+
default review path.
|
|
26
|
+
|
|
27
|
+
## Why this judge exists
|
|
28
|
+
|
|
29
|
+
Every other default judge asks a **craft-or-correctness** question. So a change
|
|
30
|
+
that is correct, clean, well-tested and architecturally sound — and **does not do
|
|
31
|
+
what was asked** — passed the default path with five green verdicts. Correctness
|
|
32
|
+
and compliance are different questions, and only one of them was being asked.
|
|
33
|
+
|
|
34
|
+
## When to use
|
|
35
|
+
|
|
36
|
+
* A diff is ready for review and **doing the wrong thing** is the risk
|
|
37
|
+
* `/review-changes` dispatches its "spec" slice to this skill
|
|
38
|
+
* A reviewer asks "is this what we asked for?", "does this satisfy the AC?"
|
|
39
|
+
|
|
40
|
+
Do NOT use when:
|
|
41
|
+
|
|
42
|
+
* The concern is a functional bug → [`judge-bug-hunter`](../judge-bug-hunter/SKILL.md)
|
|
43
|
+
* The concern is security → [`judge-security-auditor`](../judge-security-auditor/SKILL.md)
|
|
44
|
+
* The concern is missing tests → [`judge-test-coverage`](../judge-test-coverage/SKILL.md)
|
|
45
|
+
* The concern is readability → [`judge-code-quality`](../judge-code-quality/SKILL.md)
|
|
46
|
+
* **The criteria are not available** — that is not a reason to skip; it is a
|
|
47
|
+
verdict of its own. See § No criteria.
|
|
48
|
+
|
|
49
|
+
## Procedure
|
|
50
|
+
|
|
51
|
+
### 1. Establish where the criteria came from — before reading the diff
|
|
52
|
+
|
|
53
|
+
Read the criteria **first**, and never after forming an impression of the diff.
|
|
54
|
+
The order is the control, not a preference: a judge that reads the change first
|
|
55
|
+
and the requirement second reconstructs the requirement to fit what it just saw.
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
NEVER INFER AN ACCEPTANCE CRITERION FROM THE DIFF YOU ARE JUDGING.
|
|
59
|
+
A JUDGE THAT DERIVES THE REQUIREMENT FROM THE CHANGE ALWAYS FINDS THE
|
|
60
|
+
CHANGE COMPLIANT. THAT IS CIRCULAR BY CONSTRUCTION, NOT A BIAS A LABEL FIXES.
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Criteria are **supplied**, or they are not. A commit message and a PR body are
|
|
64
|
+
written by the same author as the diff, usually after it — so deriving criteria
|
|
65
|
+
from them is a weaker form of the same circularity, not an independent source of
|
|
66
|
+
lower quality.
|
|
67
|
+
|
|
68
|
+
### 2. Per-AC scan
|
|
69
|
+
|
|
70
|
+
For each criterion, exactly one of:
|
|
71
|
+
|
|
72
|
+
| Verdict | What it requires |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `SATISFIED` | cite the diff hunk **and** the test that proves it. A hunk with no test is not satisfied, it is claimed |
|
|
75
|
+
| `PARTIAL` | cite what is missing and why it falls short |
|
|
76
|
+
| `MISSING` | the criterion has no corresponding implementation |
|
|
77
|
+
|
|
78
|
+
### 3. Report the criteria-source state — three, not two
|
|
79
|
+
|
|
80
|
+
This mirrors [`dispatch_r2_reviewer`](../../scripts/dispatch_r2_reviewer.ts)'s
|
|
81
|
+
own extraction-failure handling, which refuses to collapse the last two for the
|
|
82
|
+
same reason: an unrecognised shape and a genuine absence produce the **identical
|
|
83
|
+
empty result**, so asserting "declares none" would turn a silent tool failure
|
|
84
|
+
into a false statement handed to the one independent check on AC conformance.
|
|
85
|
+
|
|
86
|
+
| State | Meaning | How to report |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| `supplied` | a roadmap, ticket or explicit criteria set was handed in | run § 2 |
|
|
89
|
+
| `not_provided` | no criteria exist for this review — an ad-hoc branch review | **no-criteria verdict**, never `SATISFIED` |
|
|
90
|
+
| `supplied_unparseable` | criteria were handed in and could not be read | **ERROR**, not a no-criteria verdict |
|
|
91
|
+
|
|
92
|
+
**`supplied_unparseable` is an error and not a third flavour of silence.** It
|
|
93
|
+
means the tooling is broken or the input is malformed, and folding it into
|
|
94
|
+
`not_provided` hides parser regressions behind a state that looks routine.
|
|
95
|
+
|
|
96
|
+
## No criteria — the verdict, and what it does to "done"
|
|
97
|
+
|
|
98
|
+
A no-criteria outcome is **not** a pass, and it is not an abstention that the
|
|
99
|
+
consolidated verdict may ignore. What it changes is what "done" is allowed to
|
|
100
|
+
claim:
|
|
101
|
+
|
|
102
|
+
> **craft quality verified; requirement compliance NOT verified**
|
|
103
|
+
|
|
104
|
+
That phrasing is the point. If this judge abstains on most reviews, the honest
|
|
105
|
+
consequence is not that the axis should be dropped — it is that most reviews
|
|
106
|
+
were previously reporting a confidence they had not earned. The abstention makes
|
|
107
|
+
that visible instead of assuming it away.
|
|
108
|
+
|
|
109
|
+
## Do NOT
|
|
110
|
+
|
|
111
|
+
* Do NOT infer criteria from the diff, the commit messages, or the PR body.
|
|
112
|
+
* Do NOT return `SATISFIED` when no criteria were supplied — return the
|
|
113
|
+
no-criteria verdict.
|
|
114
|
+
* Do NOT collapse `supplied_unparseable` into `not_provided`.
|
|
115
|
+
* Do NOT report a craft, naming, correctness or coverage finding. Another judge
|
|
116
|
+
owns it, and duplicating it here inflates the finding count without adding a
|
|
117
|
+
reading.
|
|
118
|
+
* Do NOT treat a criterion as satisfied on a diff hunk alone when a test could
|
|
119
|
+
have proven it.
|
|
120
|
+
|
|
121
|
+
## Gotcha
|
|
122
|
+
|
|
123
|
+
* **Criteria authored after the diff are not independent.** If the criteria set
|
|
124
|
+
was written or last modified after the change it judges, say so — a criterion
|
|
125
|
+
reverse-engineered from a merged branch verifies nothing.
|
|
126
|
+
* **A criterion the diff makes moot is still not satisfied.** "We no longer need
|
|
127
|
+
this" is a scope decision for a human, not a verdict this judge may reach.
|
|
128
|
+
|
|
129
|
+
## Output format
|
|
130
|
+
|
|
131
|
+
Ordered, and the first line is not negotiable — a reader must be able to see the
|
|
132
|
+
criteria-source state before any per-criterion verdict, because every verdict
|
|
133
|
+
below it is conditional on that state.
|
|
134
|
+
|
|
135
|
+
1. `criteria_source: supplied | not_provided | supplied_unparseable`
|
|
136
|
+
2. If `not_provided` → the no-criteria verdict and the "requirement compliance
|
|
137
|
+
NOT verified" line. **Stop.** Do not emit per-criterion rows.
|
|
138
|
+
3. If `supplied_unparseable` → `ERROR` with what was handed in and what could not
|
|
139
|
+
be read. **Stop.** Do not emit per-criterion rows and do not degrade to
|
|
140
|
+
`not_provided`.
|
|
141
|
+
4. If `supplied` → one row per criterion:
|
|
142
|
+
`| # | criterion (as stated) | SATISFIED / PARTIAL / MISSING | diff hunk | test |`
|
|
143
|
+
Criteria in the order they were stated, never re-ordered by verdict — a
|
|
144
|
+
reader comparing two runs needs the rows to line up.
|
|
145
|
+
5. One closing line: the count of `MISSING` and `PARTIAL`. Not a severity, not a
|
|
146
|
+
recommendation — those belong to synthesis, and phrasing this as one is how a
|
|
147
|
+
spec finding gets ranked against a naming nit.
|
|
148
|
+
|
|
149
|
+
## Related
|
|
150
|
+
|
|
151
|
+
* [`judge-synthesis`](../judge-synthesis/SKILL.md) — consumes this verdict on
|
|
152
|
+
its **own dimension**, deliberately not on the shared severity axis.
|
|
153
|
+
* [`do-and-judge-two-stage`](../subagent-orchestration/prompts/do-and-judge-two-stage.md)
|
|
154
|
+
— the stage-1 prompt this reuses.
|