@event4u/agent-config 15.0.0 → 16.1.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +301 -347
- package/CONTRIBUTING.md +9 -0
- package/README.md +2 -2
- package/dist/agent-src/commands/analyze/inbox.md +26 -0
- package/dist/agent-src/commands/analyze/roadmap-repos.md +27 -0
- package/dist/agent-src/commands/challenge-me/closure.md +178 -0
- package/dist/agent-src/commands/challenge-me.md +9 -2
- package/dist/agent-src/commands/council/default.md +25 -5
- package/dist/agent-src/commands/feature/plan.md +28 -1
- package/dist/agent-src/commands/feature/roadmap.md +28 -1
- package/dist/agent-src/commands/implement-ticket.md +27 -0
- package/dist/agent-src/commands/jira-ticket.md +27 -0
- package/dist/agent-src/commands/pr/create.md +29 -0
- package/dist/agent-src/commands/pr/merge.md +60 -9
- package/dist/agent-src/commands/roadmap/ai-council.md +7 -1
- package/dist/agent-src/commands/roadmap/create.md +77 -29
- package/dist/agent-src/commands/roadmap/materialize.md +27 -0
- package/dist/agent-src/commands/roadmap/process-full.md +123 -18
- package/dist/agent-src/commands/team.md +22 -0
- package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
- package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +97 -0
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +15 -15
- package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
- package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
- package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
- package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
- package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
- package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
- package/dist/agent-src/rules/autonomous-execution.md +15 -8
- package/dist/agent-src/rules/delegation-policy.md +23 -45
- package/dist/agent-src/rules/design-fidelity.md +32 -37
- package/dist/agent-src/rules/evaluator-independence.md +43 -49
- package/dist/agent-src/rules/test-first.md +41 -0
- package/dist/agent-src/rules/user-interaction.md +31 -30
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
- package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
- package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
- package/dist/agent-src/skills/database/data/manifest.json +4 -4
- package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
- package/dist/agent-src/skills/design-review/SKILL.md +50 -0
- package/dist/agent-src/skills/error-handling-patterns/SKILL.md +9 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +14 -4
- package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
- package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
- package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
- package/dist/agent-src/templates/agent-settings.md +1 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +188 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
- package/dist/cli-delegate/check_update_banner.js +40 -2
- package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
- package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
- package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
- package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
- package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
- package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-FXYUYU4N.js} +57 -13
- package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-GJCBB2UG.js} +168 -10
- package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-HDVVGPQW.js} +873 -539
- package/dist/cli-delegate/chunk-NUHLI2RX.js +80 -0
- package/dist/cli-delegate/{chunk-HZ52BRGG.js → chunk-P6ZHP5W2.js} +15 -90
- package/dist/cli-delegate/{chunk-TYSY33MD.js → chunk-QJWAUMJW.js} +12 -2
- package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
- package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
- package/dist/cli-delegate/{chunk-2KKXJW25.js → chunk-S3VFXMTM.js} +1 -1
- package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-UNCRJBT3.js} +31 -7
- package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
- package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
- package/dist/cli-delegate/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +1 -1
- package/dist/cli-delegate/cmd_conformance.js +918 -325
- package/dist/cli-delegate/cmd_doctor.js +7 -5
- package/dist/cli-delegate/cmd_explain.js +5 -3
- package/dist/cli-delegate/cmd_export.js +9 -7
- package/dist/cli-delegate/cmd_fleet.js +4 -2
- package/dist/cli-delegate/cmd_handoff.js +6 -6
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +3 -2
- package/dist/cli-delegate/cmd_preflight.js +4 -2
- package/dist/cli-delegate/cmd_prune.js +10 -8
- package/dist/cli-delegate/cmd_route_audit.js +2 -2
- package/dist/cli-delegate/cmd_route_explain.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +4 -4
- package/dist/cli-delegate/cmd_settings_get.js +28 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
- package/dist/cli-delegate/cmd_settings_set.js +14 -1
- package/dist/cli-delegate/cmd_sync.js +10 -8
- package/dist/cli-delegate/cmd_uninstall.js +16 -13
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +12 -10
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +87 -40
- 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 +10 -8
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +8 -6
- package/dist/hooks/dispatch.js +2601 -1073
- package/dist/install/claudeRuleRewrite.js +14 -3
- package/dist/install/conflict.js +60 -10
- package/dist/install/conflictTracker.js +218 -0
- package/dist/install/fsPrimitives.js +54 -0
- package/dist/install/install.mjs +2008 -1481
- package/dist/install/manifestFiles.js +259 -0
- package/dist/install/preflight.js +14 -2
- package/dist/install/preserve.js +157 -0
- package/dist/install/recordedOwnership.js +132 -0
- package/dist/install/txlog.js +11 -2
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1612 -667
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +3 -2
- package/dist/scripts/_lib/fs_atomic.js +167 -0
- package/dist/scripts/_lib/installed_lock.js +1 -1
- package/dist/scripts/_lib/installed_tools.js +460 -0
- package/dist/scripts/_lib/lean_projection_mode.js +3 -2
- package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
- package/dist/server/io/yamlIO.js +50 -0
- package/dist/server/routes/install.js +8 -0
- package/dist/server/routes/wizard.js +9 -3
- package/dist/server/schemas/settings.js +17 -5
- package/dist/shared/missionExecution.js +97 -0
- package/dist/ui/assets/{index-D0jaCRPM.js → index-DH1xc62v.js} +17 -17
- package/dist/ui/index.html +1 -1
- package/docs/MIGRATION.md +103 -0
- package/docs/contracts/settings-classes.md +26 -7
- package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
- package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
- package/docs/guidelines/design-fidelity-routing.md +29 -0
- package/package.json +1 -1
- package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
- package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
- package/src/config/agent-settings.template.yml +139 -27
- package/src/config/auto-rule-budget.json +16 -0
- package/src/config/ci-local-parity.yml +18 -5
- package/src/config/continuity-surface.json +28 -0
- package/src/config/gate-coverage.yml +146 -13
- package/src/config/gate-violation-baselines.json +6 -6
- package/src/config/hook-token-budget.json +2 -0
- package/src/config/host-injection-effect.json +1 -1
- package/src/config/loop-surfaces.yaml +215 -0
- package/src/config/platform-anchor.json +3 -2
- package/src/config/preamble-payload-budget.json +12 -7
- package/src/config/preamble-payload-exceptions.json +28 -0
- package/src/config/release-gate-locality.yml +11 -0
- package/src/config/routing-coverage-seed.json +18 -4
- package/src/config/rule-enforcement-baseline.json +0 -1
- package/src/install/claudeRuleRewrite.ts +16 -2
- package/src/install/conflict.ts +65 -9
- package/src/install/conflictTracker.ts +299 -0
- package/src/install/fsPrimitives.ts +54 -0
- package/src/install/manifestFiles.ts +312 -0
- package/src/install/preflight.ts +16 -2
- package/src/install/preserve.ts +205 -0
- package/src/install/recordedOwnership.ts +149 -0
- package/src/install/txlog.ts +11 -2
- package/src/install/types.ts +19 -4
- package/src/scripts/_cli/cmd_conformance.ts +88 -15
- package/src/scripts/_cli/cmd_doctor.ts +4 -3
- package/src/scripts/_cli/cmd_route_explain.ts +9 -2
- package/src/scripts/_cli/cmd_settings_get.ts +44 -0
- package/src/scripts/_cli/doctor_execution.ts +90 -0
- package/src/scripts/_dispatch.bash +3 -1
- package/src/scripts/_lib/activation_payload.ts +247 -0
- package/src/scripts/_lib/agent_settings.ts +4 -2
- package/src/scripts/_lib/authority_path.ts +102 -0
- package/src/scripts/_lib/base_ref_payload.ts +315 -0
- package/src/scripts/_lib/cascade_base.ts +94 -0
- package/src/scripts/_lib/changelog_eras.ts +7 -2
- package/src/scripts/_lib/continuation_ladder.ts +149 -2
- package/src/scripts/_lib/council_transport.ts +129 -0
- package/src/scripts/_lib/delivery_ready.ts +112 -0
- package/src/scripts/_lib/dropped_decision.ts +142 -0
- package/src/scripts/_lib/forge_protection.ts +267 -0
- package/src/scripts/_lib/hook_platform_keys.ts +27 -0
- package/src/scripts/_lib/installed_lock.ts +1 -1
- package/src/scripts/_lib/json_python_parity.ts +127 -0
- package/src/scripts/_lib/lean_projection_mode.ts +14 -3
- package/src/scripts/_lib/loop_surfaces.ts +382 -0
- package/src/scripts/_lib/measured_payload_ceiling.ts +424 -0
- package/src/scripts/_lib/mission_record.ts +190 -0
- package/src/scripts/_lib/obligation_frequency.ts +217 -0
- package/src/scripts/_lib/obligations.ts +402 -0
- package/src/scripts/_lib/orchestration_record.ts +11 -20
- package/src/scripts/_lib/payload_catalogue_completeness.ts +211 -0
- package/src/scripts/_lib/release_findings_ingest.ts +324 -0
- package/src/scripts/_lib/release_highlights.ts +199 -10
- package/src/scripts/_lib/release_holds.ts +493 -0
- package/src/scripts/_lib/release_migration_gate.ts +184 -0
- package/src/scripts/_lib/release_tag_dispatch.ts +63 -0
- package/src/scripts/_lib/review_baseline.ts +254 -0
- package/src/scripts/_lib/review_skipped_record.ts +61 -1
- package/src/scripts/_lib/rides_along.ts +102 -0
- package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
- package/src/scripts/_lib/standing_bound_ratchet.ts +216 -103
- package/src/scripts/_lib/test_delta.ts +166 -0
- package/src/scripts/_lib/test_provenance.ts +171 -0
- package/src/scripts/_lib/turn_end_refusals.ts +51 -4
- package/src/scripts/_lib/typed_op_grant.ts +246 -0
- package/src/scripts/_lib/typed_op_watch.ts +185 -0
- package/src/scripts/ai_council/config.ts +8 -11
- package/src/scripts/ai_council/fallback_config.ts +5 -3
- package/src/scripts/ai_council/ownership_classes.ts +118 -0
- package/src/scripts/ask_block_census.ts +285 -15
- package/src/scripts/check_beta_review_markers.ts +35 -4
- package/src/scripts/check_enforcement_matrix.ts +624 -0
- package/src/scripts/check_finding_dispositions.ts +167 -16
- package/src/scripts/check_gate_reachability.ts +173 -10
- package/src/scripts/check_generator_sync.ts +954 -0
- package/src/scripts/check_held_object_arrivals.ts +381 -0
- package/src/scripts/check_preamble_payload_budget.ts +474 -124
- package/src/scripts/check_release_highlights.ts +33 -1
- package/src/scripts/check_release_holds.ts +567 -0
- package/src/scripts/check_release_published.ts +1 -1
- package/src/scripts/check_reply_consistency.ts +86 -17
- package/src/scripts/check_rule_projection_integrity.ts +22 -12
- package/src/scripts/check_test_delta.ts +294 -0
- package/src/scripts/check_test_weakening.ts +299 -0
- package/src/scripts/closure_scan.ts +333 -0
- package/src/scripts/compile_hook_manifest.ts +43 -3
- package/src/scripts/council_record_shape.ts +209 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +127 -12
- package/src/scripts/hooks/block_kernel_rule_writes.ts +12 -2
- package/src/scripts/hooks/concern_registry.ts +6 -0
- package/src/scripts/hooks/design_pass_hook.ts +128 -3
- package/src/scripts/hooks/end_review_nudge_hook.ts +101 -21
- package/src/scripts/hooks/evidence_independence.ts +96 -0
- package/src/scripts/hooks/memory_recall_hook.ts +540 -0
- package/src/scripts/hooks/obligation_settle_hook.ts +182 -0
- package/src/scripts/hooks/review_baseline_hook.ts +130 -0
- package/src/scripts/hooks/rule_inject_hook.ts +49 -1
- package/src/scripts/hooks/run_continuation_hook.ts +17 -20
- package/src/scripts/hooks/turn_end_gate_hook.ts +232 -14
- package/src/scripts/hooks_doctor.ts +89 -1
- package/src/scripts/hooks_status.ts +19 -0
- package/src/scripts/install.ts +107 -207
- package/src/scripts/interruption_report.ts +8 -2
- package/src/scripts/janitor.ts +15 -1
- package/src/scripts/lint_decision_classes.ts +464 -0
- package/src/scripts/lint_hook_manifest.ts +11 -0
- package/src/scripts/lint_major_migration_sections.ts +450 -0
- package/src/scripts/lint_plan_risk_register.ts +47 -3
- package/src/scripts/lint_roadmap_blockers.ts +61 -0
- package/src/scripts/lint_roadmap_producers.ts +202 -0
- package/src/scripts/measure_rule_budget.ts +101 -5
- package/src/scripts/measure_turn_end_gate.ts +302 -5
- package/src/scripts/new_roadmap.ts +37 -0
- package/src/scripts/orchestration_record.ts +0 -5
- package/src/scripts/release.ts +66 -162
- package/src/scripts/release_drill.ts +460 -15
- package/src/scripts/release_env.ts +1 -1
- package/src/scripts/release_publication.ts +52 -1
- package/src/scripts/report_enforcement_drift.ts +566 -0
- package/src/scripts/report_held_object_arrivals.ts +456 -0
- package/src/scripts/report_host_injection_effect.ts +3 -11
- package/src/scripts/report_standing_payload_by_host.ts +26 -152
- package/src/scripts/schemas/agent-settings.schema.json +23 -0
- package/src/scripts/schemas/command.schema.json +4 -0
- package/src/scripts/scope_growth.ts +219 -0
- package/src/scripts/sync_agent_settings.ts +140 -18
- package/src/scripts/trigger_eval_grandfather.json +0 -1
- package/src/scripts/ui_conformance_probe.ts +622 -0
- package/src/server/io/yamlIO.ts +48 -0
- package/src/server/schemas/settings.ts +31 -7
- package/src/shared/missionExecution.ts +126 -0
- package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* obligation-settle — the reader the delivered-obligation record never had.
|
|
4
|
+
*
|
|
5
|
+
* The injector has always known which rules it delivered into a session. Until
|
|
6
|
+
* this concern, nothing asked whether the turn that received them discharged
|
|
7
|
+
* any. This is that question, asked at turn-end, and — for now — asked out
|
|
8
|
+
* loud and answered with nothing.
|
|
9
|
+
*
|
|
10
|
+
* SHADOW ONLY. IT REFUSES NOTHING, AND THAT IS NOT A PLACEHOLDER.
|
|
11
|
+
* It computes the verdict a future armed detector would reach, records it, and
|
|
12
|
+
* returns allow on every path. Arming is gated on a bar pre-registered in
|
|
13
|
+
* `docs/CLAIMS.md` BEFORE any code able to refuse exists, so that the bar
|
|
14
|
+
* cannot be chosen to fit the numbers the shadow window happened to produce.
|
|
15
|
+
* The estate already has one blocking turn-end concern and the first-ranked
|
|
16
|
+
* risk against this work is that a second one refuses a clean turn and an
|
|
17
|
+
* operator disables the carrier for good.
|
|
18
|
+
*
|
|
19
|
+
* THE TOUCHED SET COMES FROM THE DIFF, NOT FROM TOOL EVENTS.
|
|
20
|
+
* A tool event carries the path the tool was given. A shell heredoc writing
|
|
21
|
+
* three files reports one Bash call and no paths at all, so a detector reading
|
|
22
|
+
* tool events is blind to exactly the turns that wrote the most. `git diff
|
|
23
|
+
* --numstat HEAD` plus `git ls-files --others` sees the tree as it actually
|
|
24
|
+
* is, which is the only description that cannot be evaded by how the write was
|
|
25
|
+
* spelled. The helpers are the end-review concern's own, imported rather than
|
|
26
|
+
* re-derived.
|
|
27
|
+
*
|
|
28
|
+
* IT REFUSES ONLY ON CLASSES THAT COULD HAVE A DISCHARGE.
|
|
29
|
+
* `hook`, `validator` and `test` name carriers that run and leave traces.
|
|
30
|
+
* `observer`, `instruction-only` and `none` do not, so demanding a discharge
|
|
31
|
+
* for one would demand evidence nobody can produce — and 69 of 97 delivered
|
|
32
|
+
* rules on the frozen corpus are class `none`.
|
|
33
|
+
*
|
|
34
|
+
* IT IS SILENT WHILE A SUBAGENT DISPATCH IS OPEN.
|
|
35
|
+
* Three of the turn-end gate's six detectors are gated this way and three are
|
|
36
|
+
* not; the difference is whether the finding could be about work a dispatch is
|
|
37
|
+
* still doing. This one could — a subagent writing UI files is the canonical
|
|
38
|
+
* case — so it takes the gated side.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import path from 'node:path';
|
|
42
|
+
import process from 'node:process';
|
|
43
|
+
|
|
44
|
+
import { may_refuse_on } from '../_lib/obligation_frequency.js';
|
|
45
|
+
import { appendShadow, readDelivered, readDischarged, stamp } from '../_lib/obligations.js';
|
|
46
|
+
import { loadRouter, matchTierRules } from '../_lib/rule_injection.js';
|
|
47
|
+
import { gitNumstatRows, isDocPath, untrackedNonDocFiles } from './end_review_nudge_hook.js';
|
|
48
|
+
import { openRecordStats } from './subagent_ledger_hook.js';
|
|
49
|
+
|
|
50
|
+
const EXIT_ALLOW = 0;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Every non-doc path this turn wrote, tracked or not.
|
|
54
|
+
*
|
|
55
|
+
* Doc paths are excluded on the same grounds the end-review concern excludes
|
|
56
|
+
* them: a markdown edit is not the kind of change whose obligations this
|
|
57
|
+
* detector can speak to, and including them would make almost every turn a
|
|
58
|
+
* candidate.
|
|
59
|
+
*/
|
|
60
|
+
export function touchedPaths(cwd: string): string[] {
|
|
61
|
+
const tracked = gitNumstatRows(cwd)
|
|
62
|
+
.map((r) => r.path)
|
|
63
|
+
.filter((p) => p !== '' && !isDocPath(p));
|
|
64
|
+
const untracked = untrackedNonDocFiles(cwd);
|
|
65
|
+
return [...new Set([...tracked, ...untracked])];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface SettleVerdict {
|
|
69
|
+
/** Non-doc paths this turn wrote. */
|
|
70
|
+
readonly touched: string[];
|
|
71
|
+
/** Delivered rules whose path triggers match something touched. */
|
|
72
|
+
readonly candidates: string[];
|
|
73
|
+
/** Candidates with no recorded discharge, and a class that could have one. */
|
|
74
|
+
readonly missing: string[];
|
|
75
|
+
/** True when a subagent dispatch is open — every other field is then advisory only. */
|
|
76
|
+
readonly dispatchOpen: boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* What an armed detector would conclude, computed without refusing anything.
|
|
81
|
+
*
|
|
82
|
+
* Exported whole rather than as a boolean so the shadow row, the tests and any
|
|
83
|
+
* later armed path all read the SAME computation. A detector whose shadow
|
|
84
|
+
* measurement and live behaviour come from different code measures nothing.
|
|
85
|
+
*/
|
|
86
|
+
export function computeVerdict(root: string, session: string): SettleVerdict {
|
|
87
|
+
const dispatchOpen = (() => {
|
|
88
|
+
try {
|
|
89
|
+
return openRecordStats(root).open_count > 0;
|
|
90
|
+
} catch {
|
|
91
|
+
// An unreadable ledger is not a reason to change the verdict.
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
})();
|
|
95
|
+
|
|
96
|
+
const touched = touchedPaths(root);
|
|
97
|
+
if (touched.length === 0) {
|
|
98
|
+
return { touched, candidates: [], missing: [], dispatchOpen };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const delivered = readDelivered(root, session);
|
|
102
|
+
if (delivered.length === 0) {
|
|
103
|
+
return { touched, candidates: [], missing: [], dispatchOpen };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let pathMatched = new Set<string>();
|
|
107
|
+
try {
|
|
108
|
+
const router = loadRouter(root);
|
|
109
|
+
// Empty prompt, so only PATH triggers can fire. A turn's file writes
|
|
110
|
+
// are not a restatement of the user's request and must not re-fire
|
|
111
|
+
// keyword rules — the same reasoning the injector applies on its own
|
|
112
|
+
// tool slot.
|
|
113
|
+
pathMatched = new Set(matchTierRules(router, '', touched, null).map((m) => m.id));
|
|
114
|
+
} catch {
|
|
115
|
+
return { touched, candidates: [], missing: [], dispatchOpen };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const candidates = delivered
|
|
119
|
+
.filter((r) => pathMatched.has(r.rule) && may_refuse_on(r.cls))
|
|
120
|
+
.map((r) => r.rule);
|
|
121
|
+
|
|
122
|
+
const discharged = new Set(readDischarged(root, session).map((r) => r.rule));
|
|
123
|
+
const missing = candidates.filter((id) => !discharged.has(id));
|
|
124
|
+
return { touched, candidates: [...new Set(candidates)], missing: [...new Set(missing)] , dispatchOpen };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Whether this reading should force a continuation, were anything armed.
|
|
129
|
+
*
|
|
130
|
+
* An UNCHANGED missing set on a second reading means the previous nudge moved
|
|
131
|
+
* nothing, so nudging again would loop rather than progress. The obligations
|
|
132
|
+
* stay OPEN — that is the whole point, and it is the side of the source set's
|
|
133
|
+
* self-contradiction this ships. A budget running out is a fact about the
|
|
134
|
+
* budget; letting it write a policy verdict is how an unmet obligation becomes
|
|
135
|
+
* a satisfied one with nobody deciding, and an exhausted budget is exactly the
|
|
136
|
+
* moment the obligation is least likely to have been met.
|
|
137
|
+
*
|
|
138
|
+
* There is therefore no `waived` state anywhere in this file, and no code path
|
|
139
|
+
* that writes one.
|
|
140
|
+
*/
|
|
141
|
+
export function shouldContinue(attempt: number): boolean {
|
|
142
|
+
return attempt <= 1;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function main(): number {
|
|
146
|
+
const root = process.cwd();
|
|
147
|
+
const session = (process.env['CLAUDE_CODE_SESSION_ID'] ?? '').trim();
|
|
148
|
+
if (session === '') return EXIT_ALLOW;
|
|
149
|
+
|
|
150
|
+
let verdict: SettleVerdict;
|
|
151
|
+
try {
|
|
152
|
+
verdict = computeVerdict(root, session);
|
|
153
|
+
} catch {
|
|
154
|
+
// A detector that cannot compute has nothing to say. It never has a
|
|
155
|
+
// refusal to withhold, so there is no safe-vs-unsafe choice here.
|
|
156
|
+
return EXIT_ALLOW;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Silent while a dispatch is open: the files may be a subagent's, still
|
|
160
|
+
// being written, and a shadow row claiming otherwise would poison the very
|
|
161
|
+
// corpus the arming decision is measured against.
|
|
162
|
+
if (verdict.dispatchOpen) return EXIT_ALLOW;
|
|
163
|
+
if (verdict.missing.length === 0) return EXIT_ALLOW;
|
|
164
|
+
|
|
165
|
+
const attempt = appendShadow(root, session, verdict.missing, stamp());
|
|
166
|
+
if (attempt === 0) return EXIT_ALLOW;
|
|
167
|
+
|
|
168
|
+
// ONE line for the whole missing set, never one per obligation — the same
|
|
169
|
+
// aggregation the row itself uses, for the same reason.
|
|
170
|
+
process.stderr.write(
|
|
171
|
+
`obligation-settle (shadow, refusing nothing): ${verdict.missing.length} `
|
|
172
|
+
+ `obligation(s) with no recorded discharge after writing `
|
|
173
|
+
+ `${verdict.touched.length} file(s): ${verdict.missing.join(', ')}`
|
|
174
|
+
+ `${shouldContinue(attempt) ? '' : ' — unchanged since the last reading; still open'}\n`,
|
|
175
|
+
);
|
|
176
|
+
return EXIT_ALLOW;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Bundle-safety: never auto-run when inlined into an esbuild bundle.
|
|
180
|
+
if (process.argv[1] !== undefined && path.basename(process.argv[1]).startsWith('obligation_settle')) {
|
|
181
|
+
process.exit(main());
|
|
182
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* review-baseline — `session_start` concern
|
|
4
|
+
* (road-to-a-question-that-survives-the-turn Phase 2).
|
|
5
|
+
*
|
|
6
|
+
* WHAT IT DOES, and it is the whole of it: on session start, record how many
|
|
7
|
+
* non-doc lines were ALREADY dirty, and what HEAD was. `end-review-nudge`
|
|
8
|
+
* subtracts that at `stop` so a session is charged for its own mutation instead
|
|
9
|
+
* of for whatever the working tree happened to be carrying.
|
|
10
|
+
*
|
|
11
|
+
* THE DEFECT IT CLOSES. `end_review_nudge_hook`'s own header names it: a dirty
|
|
12
|
+
* tree that predates the session is counted as the session's, and the reason it
|
|
13
|
+
* was not fixed was "no such baseline exists anywhere a `stop` concern can read
|
|
14
|
+
* it". That was true of a phase with a one-concern scope, and stale by the time
|
|
15
|
+
* it mattered — `session_start` is bound on seven hosts carrying fourteen
|
|
16
|
+
* concerns. The measured consequence of leaving it: on a branch deliberately
|
|
17
|
+
* accumulating work, the nudge read 1,771 lines of two crashed sessions'
|
|
18
|
+
* uncommitted work and fired every turn. An advisory that always fires is noise,
|
|
19
|
+
* and noise is worse than silence because it trains the reader to skip the one
|
|
20
|
+
* that mattered.
|
|
21
|
+
*
|
|
22
|
+
* MEASURED WITH THE READER'S OWN INSTRUMENT. `totalNonDocMutatedLinesWithMeasure`
|
|
23
|
+
* is imported from the nudge rather than reimplemented, so the two sides cannot
|
|
24
|
+
* drift on what a "non-doc mutated line" is. A second counting implementation
|
|
25
|
+
* would produce a baseline in one unit and a measurement in another, and the
|
|
26
|
+
* subtraction would be silently wrong rather than visibly broken.
|
|
27
|
+
*
|
|
28
|
+
* CAPTURE ONLY. It never emits context, never warns, and always returns 0. A
|
|
29
|
+
* `session_start` concern that could refuse would turn an unreadable git
|
|
30
|
+
* directory into a session that cannot start, which is a far worse failure than
|
|
31
|
+
* the noise it is fixing.
|
|
32
|
+
*
|
|
33
|
+
* NO PATHS, NO FINGERPRINTS. The record is two numbers and a timestamp. A
|
|
34
|
+
* path-level change set was the shape the source draft proposed; it is a bigger
|
|
35
|
+
* object with a bigger privacy surface, and nothing in the nudge consumes one.
|
|
36
|
+
* The file cannot hold a path, a diff or a file body by construction, which is
|
|
37
|
+
* the same PII-exclusion-by-construction the telemetry record keeps.
|
|
38
|
+
*/
|
|
39
|
+
import * as fs from 'node:fs';
|
|
40
|
+
import * as path from 'node:path';
|
|
41
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
42
|
+
|
|
43
|
+
import {
|
|
44
|
+
baselineStateFile,
|
|
45
|
+
currentHeadSha,
|
|
46
|
+
type ReviewBaseline,
|
|
47
|
+
} from '../_lib/review_baseline.js';
|
|
48
|
+
import { deriveSessionKey, totalNonDocMutatedLinesWithMeasure } from './end_review_nudge_hook.js';
|
|
49
|
+
import { unwrap, type JsonObject, type JsonValue } from './envelope.js';
|
|
50
|
+
import { readHookStdin } from './hook_stdin.js';
|
|
51
|
+
import { atomic_write_json } from './state_io.js';
|
|
52
|
+
|
|
53
|
+
const EXIT_OK = 0;
|
|
54
|
+
|
|
55
|
+
export function buildBaseline(workspaceRoot: string, now: Date = new Date()): ReviewBaseline {
|
|
56
|
+
const measured = totalNonDocMutatedLinesWithMeasure(workspaceRoot);
|
|
57
|
+
return {
|
|
58
|
+
head_sha: currentHeadSha(workspaceRoot),
|
|
59
|
+
baseline_lines: measured.lines,
|
|
60
|
+
// Carried, not discarded. Past `UNTRACKED_FILE_CAP` the count is a
|
|
61
|
+
// synthetic `THRESHOLD + 1 + tracked` chosen to be over the bar rather
|
|
62
|
+
// than to be true, and subtracting that from a later exact count is
|
|
63
|
+
// arithmetic over two different quantities. The consumer refuses the
|
|
64
|
+
// subtraction when the two measures disagree; it can only do that if the
|
|
65
|
+
// writer says which one it took.
|
|
66
|
+
measure: measured.measure,
|
|
67
|
+
written_at: now.toISOString(),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function main(): number {
|
|
72
|
+
let envelope: JsonObject;
|
|
73
|
+
let payload: JsonObject;
|
|
74
|
+
try {
|
|
75
|
+
[envelope, payload] = unwrap(readHookStdin(), 'claude');
|
|
76
|
+
} catch {
|
|
77
|
+
return EXIT_OK;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const event = String(envelope['event'] ?? '');
|
|
81
|
+
if (event !== '' && event !== 'session_start') return EXIT_OK;
|
|
82
|
+
|
|
83
|
+
const workspaceRoot =
|
|
84
|
+
String((envelope['workspace_root'] as JsonValue | undefined) ?? '').trim() || process.cwd();
|
|
85
|
+
|
|
86
|
+
// A baseline is written ONCE per session. A host that fires session_start
|
|
87
|
+
// twice — Cline maps both TaskStart and TaskResume onto it — must not
|
|
88
|
+
// re-baseline mid-session: the second write would fold everything the
|
|
89
|
+
// session had already changed into its own baseline and the nudge would go
|
|
90
|
+
// permanently quiet for that session. Over-writing is the silent failure
|
|
91
|
+
// direction, so it is the one guarded.
|
|
92
|
+
const sessionKey = deriveSessionKey(envelope, payload);
|
|
93
|
+
const file = baselineStateFile(workspaceRoot, sessionKey);
|
|
94
|
+
try {
|
|
95
|
+
if (fs.existsSync(file)) return EXIT_OK;
|
|
96
|
+
} catch {
|
|
97
|
+
return EXIT_OK;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
// `atomic_write_json` is already `AGENT_CONFIG_REPLAY`-aware, so a
|
|
102
|
+
// fixture-replay run leaves no baseline behind.
|
|
103
|
+
atomic_write_json(file, buildBaseline(workspaceRoot) as unknown as JsonObject);
|
|
104
|
+
} catch {
|
|
105
|
+
// An unwritable state dir degrades to today's behaviour — the nudge
|
|
106
|
+
// reads `absent` and does not subtract. Never a failed session start.
|
|
107
|
+
}
|
|
108
|
+
return EXIT_OK;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Bundle-safety: never auto-run when inlined into an esbuild bundle, where
|
|
112
|
+
// every module shares the bundle's `import.meta.url`.
|
|
113
|
+
declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
|
|
114
|
+
function _isCliEntry(): boolean {
|
|
115
|
+
if (typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__) return false;
|
|
116
|
+
if (process.argv[1] === undefined) return false;
|
|
117
|
+
const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
|
118
|
+
if (import.meta.url === argvUrl) return true;
|
|
119
|
+
try {
|
|
120
|
+
const here = fs.realpathSync(fileURLToPath(import.meta.url));
|
|
121
|
+
const argv = fs.realpathSync(path.resolve(process.argv[1]));
|
|
122
|
+
return here === argv;
|
|
123
|
+
} catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (_isCliEntry()) {
|
|
129
|
+
process.exit(main());
|
|
130
|
+
}
|
|
@@ -11,7 +11,14 @@
|
|
|
11
11
|
* the 36.2 %-against-48 % run at `docs/CLAIMS.md:188-189`. Whatever that
|
|
12
12
|
* instrument's flaws — and ADR-202 closed it — it points one way.
|
|
13
13
|
*
|
|
14
|
-
* SHIPPED ON FOR CLAUDE CODE SINCE ADR-
|
|
14
|
+
* SHIPPED ON FOR CLAUDE CODE SINCE ADR-267 — the delivery-default record, and
|
|
15
|
+
* the citation is corrected here (2026-09-13) from ADR-265, which decides
|
|
16
|
+
* something else entirely: whether the iron-law reserve is activated, answered
|
|
17
|
+
* no, with the verifier kept inside the change. Nothing in it ships a
|
|
18
|
+
* projection mode. ADR-267 `delivery-default-for-claude-code` is the record
|
|
19
|
+
* that turns this concern on for this host, and it is the one whose
|
|
20
|
+
* `review_trigger` fires if a rule stops arriving. Previously corrected
|
|
21
|
+
* 2026-09-08 (R2 finding
|
|
15
22
|
* 4), because this paragraph read "DEFAULT OFF, AND OFF MEANS ZERO BYTES …
|
|
16
23
|
* under every shipped default it emits nothing … there is no measured emission
|
|
17
24
|
* to register yet" and all four clauses became false in the same change that
|
|
@@ -94,6 +101,8 @@ import {
|
|
|
94
101
|
normalizeLeanProjectionMode,
|
|
95
102
|
resolveLeanProjectionHosts,
|
|
96
103
|
} from '../_lib/lean_projection_mode.js';
|
|
104
|
+
import { enforcement_class_from_frontmatter } from '../_lib/obligation_frequency.js';
|
|
105
|
+
import { appendDelivered, stamp, type DeliveredRow } from '../_lib/obligations.js';
|
|
97
106
|
import {
|
|
98
107
|
loadRuleBody,
|
|
99
108
|
loadRouter,
|
|
@@ -237,6 +246,44 @@ export interface Injection {
|
|
|
237
246
|
body: string;
|
|
238
247
|
}
|
|
239
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Record what this fire delivered, so something can later ask whether the turn
|
|
251
|
+
* discharged it.
|
|
252
|
+
*
|
|
253
|
+
* The write happens HERE and not in a later slot because this is the only
|
|
254
|
+
* place that knows the answer. By the time a turn ends, the selection that
|
|
255
|
+
* produced these ids is gone — the router would have to be re-run against a
|
|
256
|
+
* prompt nobody kept, which is a different computation that could disagree.
|
|
257
|
+
*
|
|
258
|
+
* EMITTER RECORD, NEVER A COMPLIANCE ONE. A row says a body was emitted into
|
|
259
|
+
* this slot. It does not say the model read it, and the ledger's own header
|
|
260
|
+
* carries the council lock that forbids reading it that way.
|
|
261
|
+
*
|
|
262
|
+
* Best-effort by construction: every failure path inside `appendDelivered`
|
|
263
|
+
* returns rather than throws, and this wrapper adds one more guard so a
|
|
264
|
+
* ledger problem can never turn a successful injection into a failed hook.
|
|
265
|
+
* A missed reading is cheaper than a refused turn, and this concern has no
|
|
266
|
+
* refusal to offer anyway.
|
|
267
|
+
*/
|
|
268
|
+
export function recordDelivered(root: string, session: string, ruleIds: string[]): number {
|
|
269
|
+
try {
|
|
270
|
+
const now = stamp();
|
|
271
|
+
const rows: DeliveredRow[] = ruleIds.map((id) => {
|
|
272
|
+
const body = loadRuleBody(root, id);
|
|
273
|
+
return {
|
|
274
|
+
rule: id,
|
|
275
|
+
// A rule with no projected body declares nothing this can read,
|
|
276
|
+
// which is `none` — the same answer as a body declaring none.
|
|
277
|
+
cls: body === null ? 'none' : enforcement_class_from_frontmatter(body),
|
|
278
|
+
at: now,
|
|
279
|
+
};
|
|
280
|
+
});
|
|
281
|
+
return appendDelivered(root, session, rows);
|
|
282
|
+
} catch {
|
|
283
|
+
return 0;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
240
287
|
/**
|
|
241
288
|
* Build the injection for one event, or `null` for silence.
|
|
242
289
|
*
|
|
@@ -371,6 +418,7 @@ export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
|
371
418
|
|
|
372
419
|
for (const id of injection.rules) seen.add(id);
|
|
373
420
|
writeSeen(root, session, seen);
|
|
421
|
+
recordDelivered(root, session, injection.rules);
|
|
374
422
|
|
|
375
423
|
process.stdout.write(
|
|
376
424
|
`${JSON.stringify({
|
|
@@ -185,6 +185,7 @@ import { premiseMoved, readContextObservation } from '../_lib/context_observatio
|
|
|
185
185
|
import { RUN_TERMINAL_VOCABULARY_VERSION } from '../_lib/outcome_vocabularies.js';
|
|
186
186
|
import {
|
|
187
187
|
HALT_ACTIONS,
|
|
188
|
+
buildLedgerEvent, parseDeliveryState, type DeliveryState,
|
|
188
189
|
ladder,
|
|
189
190
|
MAX_ITERATIONS,
|
|
190
191
|
parseHaltStamp,
|
|
@@ -322,6 +323,8 @@ export interface RunState {
|
|
|
322
323
|
* observed one, which is never a disagreement (`premiseMoved`).
|
|
323
324
|
*/
|
|
324
325
|
context_fingerprint?: string;
|
|
326
|
+
/** DELIVERY position (8.1), written by the run; absent is no hold. */
|
|
327
|
+
delivery?: DeliveryState;
|
|
325
328
|
}
|
|
326
329
|
|
|
327
330
|
/**
|
|
@@ -651,6 +654,10 @@ function readState(file: string): RunState | null {
|
|
|
651
654
|
if (o['inert_reported'] === true) {
|
|
652
655
|
rec.inert_reported = true;
|
|
653
656
|
}
|
|
657
|
+
// Round-tripped for the same reason `history_source` is: without this the
|
|
658
|
+
// 8.1 hold is dead code that reads as shipped.
|
|
659
|
+
const dlv = parseDeliveryState(o['delivery']);
|
|
660
|
+
if (dlv !== undefined) rec.delivery = dlv;
|
|
654
661
|
// Round-tripped for the same reason `history_source` is: dropped, the
|
|
655
662
|
// premise would be re-recorded every fire from the newest observation, so
|
|
656
663
|
// the two sides could never differ and the rung would be dead code.
|
|
@@ -1384,14 +1391,14 @@ export function main(): number {
|
|
|
1384
1391
|
const observed = readContextObservation(workspaceRoot)?.fingerprint ?? null;
|
|
1385
1392
|
const premiseInvalidated = premiseMoved(state.context_fingerprint, observed);
|
|
1386
1393
|
|
|
1394
|
+
const delivery = parseDeliveryState(state.delivery) ?? null;
|
|
1387
1395
|
const action = ladder(
|
|
1388
|
-
state,
|
|
1389
|
-
scan.open,
|
|
1390
|
-
Date.now(),
|
|
1396
|
+
state, scan.open, Date.now(),
|
|
1391
1397
|
scan.blocked,
|
|
1392
1398
|
undefined,
|
|
1393
1399
|
unavailable,
|
|
1394
1400
|
premiseInvalidated,
|
|
1401
|
+
delivery,
|
|
1395
1402
|
);
|
|
1396
1403
|
|
|
1397
1404
|
if (action !== 'engage') {
|
|
@@ -1408,23 +1415,13 @@ export function main(): number {
|
|
|
1408
1415
|
const alreadyBlocked =
|
|
1409
1416
|
action === 'blocked' && eventAlreadyLogged(workspaceRoot, runId, slug, 'blocked');
|
|
1410
1417
|
if (!alreadyStamped && !alreadyBlocked) {
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
blocked: scan.blocked,
|
|
1419
|
-
// A ledger line is a persisted shape carrying a `RunTerminalState`,
|
|
1420
|
-
// so it names the vocabulary version it was written against: a
|
|
1421
|
-
// reader meeting an unknown value can then tell a newer writer
|
|
1422
|
-
// from a corrupt row.
|
|
1423
|
-
terminal_state: terminalStateFor(action),
|
|
1424
|
-
terminal_vocabulary_version: RUN_TERMINAL_VOCABULARY_VERSION,
|
|
1425
|
-
at: new Date().toISOString(),
|
|
1426
|
-
...roots,
|
|
1427
|
-
});
|
|
1418
|
+
const line = buildLedgerEvent(
|
|
1419
|
+
{ action, runId, slug, turn: turnOrdinal, iterations: state.iterations,
|
|
1420
|
+
open: scan.open, blocked: scan.blocked, delivery },
|
|
1421
|
+
RUN_TERMINAL_VOCABULARY_VERSION,
|
|
1422
|
+
new Date(),
|
|
1423
|
+
);
|
|
1424
|
+
appendEvent(workspaceRoot, { ...line, ...roots } as JsonObject);
|
|
1428
1425
|
}
|
|
1429
1426
|
if (action === 'complete') {
|
|
1430
1427
|
// Only a COMPLETION clears the state, so a later run on the same
|