@event4u/agent-config 14.21.0 → 14.23.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 +164 -187
- package/CREDITS.md +37 -0
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/inbox.md +380 -5
- package/dist/agent-src/commands/analyze/repo.md +4 -2
- package/dist/agent-src/commands/pr/merge.md +47 -38
- package/dist/agent-src/commands/roadmap/next.md +8 -0
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +69 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -5
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +12 -0
- package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
- package/dist/agent-src/rules/notes-first-reasoning.md +36 -25
- package/dist/agent-src/rules/token-efficiency.md +26 -3
- package/dist/agent-src/scripts/_cli_entry.ts +51 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +120 -27
- package/dist/agent-src/scripts/roadmap_gates.ts +4 -20
- package/dist/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/dist/agent-src/scripts/stubs_due.ts +4 -18
- package/dist/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/dist/agent-src/skills/code-intelligence/SKILL.md +82 -1
- package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +9 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +52 -2
- package/dist/agent-src/templates/agent-settings.md +0 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +1 -1
- package/dist/cli-delegate/_cli_entry.js +8 -0
- package/dist/cli-delegate/_lib/pin_resolver.js +170 -0
- package/dist/cli-delegate/archival_sweep.js +8 -0
- package/dist/cli-delegate/archive_completed_roadmaps.js +542 -0
- package/dist/cli-delegate/check_update_banner.js +923 -0
- package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-2KKXJW25.js} +23 -9
- package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
- package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
- package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
- package/dist/cli-delegate/{chunk-4FS4WRXP.js → chunk-IAI3XL43.js} +15 -3
- package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
- package/dist/cli-delegate/{chunk-643Z7FIC.js → chunk-KCNTUSPI.js} +7 -1
- package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
- package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
- package/dist/cli-delegate/{chunk-YS6XTLEK.js → chunk-TYSY33MD.js} +56 -6
- package/dist/cli-delegate/chunk-ULET4BYS.js +328 -0
- package/dist/cli-delegate/chunk-YDQSHTD7.js +187 -0
- package/dist/cli-delegate/chunk-YWVXLR44.js +48 -0
- package/dist/cli-delegate/chunk-ZZQBUHFK.js +273 -0
- package/dist/cli-delegate/cmd_conformance.js +2 -2
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +45 -13
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_session_recycle.js +4 -14
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/cli-delegate/dashboard_mode.js +16 -0
- package/dist/cli-delegate/gate_budget.js +24 -0
- package/dist/cli-delegate/gate_execute.js +25 -0
- package/dist/cli-delegate/guarded_baseline.js +18 -0
- package/dist/cli-delegate/resume_probe.js +22 -0
- package/dist/cli-delegate/roadmap_gates.js +638 -0
- package/dist/cli-delegate/roadmap_set_step.js +173 -0
- package/dist/cli-delegate/stub_queue.js +47 -0
- package/dist/cli-delegate/stubs_due.js +38 -0
- package/dist/cli-delegate/ticket_bundles.js +59 -0
- package/dist/cli-delegate/update_roadmap_progress.js +60 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +13 -13
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +1813 -946
- package/dist/install/claudeLayerCarriage.js +237 -0
- package/dist/install/claudePathsPlan.js +79 -0
- package/dist/install/hostLayerFingerprint.js +1 -1
- package/dist/install/install.mjs +21 -3
- package/dist/install/partitionEligibility.js +231 -117
- package/dist/install/ruleLayerPartition.js +10 -7
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +10870 -7641
- package/dist/scripts/_lib/lean_projection_mode.js +108 -0
- package/dist/scripts/_lib/router_match.js +243 -0
- package/dist/scripts/_lib/rule_injection.js +216 -0
- package/dist/scripts/condense.js +52 -45
- package/dist/scripts/project_thin_rules.js +139 -19
- package/dist/server/schemas/settings.js +16 -3
- package/docs/MIGRATION.md +105 -1
- package/docs/contracts/settings-classes.md +40 -8
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
- package/docs/guidelines/agent-infra/notes-horizon-mechanics.md +90 -0
- package/llms.txt +1 -1
- package/package.json +7 -4
- package/src/agent-src/scripts/_cli_entry.ts +51 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +120 -27
- package/src/agent-src/scripts/roadmap_gates.ts +4 -20
- package/src/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/src/agent-src/scripts/stubs_due.ts +4 -18
- package/src/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/src/config/agent-settings.template.yml +47 -1
- package/src/config/continuity-surface.json +516 -0
- package/src/config/estate-count-budget.json +1 -1
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +29 -8
- package/src/config/gate-violation-baselines.json +8 -8
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/hook-token-budget.json +7 -6
- package/src/config/host-injection-effect.json +73 -0
- package/src/config/packed-binary-manifest.json +33 -0
- package/src/config/preamble-payload-budget.json +112 -105
- package/src/config/publish-surface.json +4 -2
- package/src/config/routing-coverage-seed.json +9 -2
- package/src/install/claudeLayerCarriage.ts +273 -0
- package/src/install/claudePathsPlan.ts +76 -0
- package/src/install/hostLayerFingerprint.ts +1 -1
- package/src/install/partitionEligibility.ts +261 -127
- package/src/install/ruleLayerPartition.ts +19 -23
- package/src/scripts/_cli/cmd_session_recycle.ts +2 -25
- package/src/scripts/_cli/handoff_sessions.ts +14 -44
- package/src/scripts/_dispatch.bash +77 -27
- package/src/scripts/_lib/auto_dispatch.ts +2 -2
- package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
- package/src/scripts/_lib/continuity_slot.ts +295 -0
- package/src/scripts/_lib/continuity_surface.ts +296 -0
- package/src/scripts/_lib/continuity_writer.ts +253 -0
- package/src/scripts/_lib/corpus_manifest.ts +26 -9
- package/src/scripts/_lib/hook_settings.ts +86 -1
- package/src/scripts/_lib/injection_effect.ts +151 -0
- package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
- package/src/scripts/_lib/lean_projection_mode.ts +127 -0
- package/src/scripts/_lib/packed_binary_predicate.ts +262 -0
- package/src/scripts/_lib/pin_resolver.ts +13 -0
- package/src/scripts/_lib/recycle_envelope_paths.ts +109 -21
- package/src/scripts/_lib/regression_neighbourhood.ts +159 -22
- package/src/scripts/_lib/retired_status.ts +74 -0
- package/src/scripts/_lib/session_eol.ts +44 -0
- package/src/scripts/_lib/session_index_trust.ts +346 -0
- package/src/scripts/_lib/skill_catalogue.ts +156 -21
- package/src/scripts/_lib/standing_bound_ratchet.ts +154 -0
- package/src/scripts/_lib/subagent_capsule.ts +117 -8
- package/src/scripts/_lib/vendored_grammar_upstream.ts +280 -0
- package/src/scripts/ai_council/api_transport.ts +5 -0
- package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
- package/src/scripts/ai_council/prompts.ts +9 -0
- package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
- package/src/scripts/audit_mcp_tools.ts +69 -25
- package/src/scripts/build_mcp_catalog.ts +24 -1
- package/src/scripts/capture_skill_catalogue.ts +24 -11
- package/src/scripts/check_candidate_lines.ts +499 -0
- package/src/scripts/check_continuity_surface.ts +475 -0
- package/src/scripts/check_dependency_floors.ts +13 -6
- package/src/scripts/check_estate_count.ts +40 -79
- package/src/scripts/check_host_tree_parity.ts +291 -0
- package/src/scripts/check_pack_size.ts +262 -23
- package/src/scripts/check_preamble_payload_budget.ts +136 -7
- package/src/scripts/check_roadmap_trackable.ts +19 -16
- package/src/scripts/check_rule_layer_partition.ts +54 -34
- package/src/scripts/check_rule_projection_integrity.ts +102 -2
- package/src/scripts/check_single_delivery.ts +97 -31
- package/src/scripts/check_update_banner.ts +13 -0
- package/src/scripts/ci_settle.ts +205 -5
- package/src/scripts/code_graph/build.ts +217 -15
- package/src/scripts/code_graph/cli.ts +334 -15
- package/src/scripts/code_graph/detect.ts +38 -0
- package/src/scripts/code_graph/extract.ts +11 -0
- package/src/scripts/code_graph/grammar_probe.ts +43 -0
- package/src/scripts/code_graph/loader.ts +88 -14
- package/src/scripts/code_graph/query.ts +144 -11
- package/src/scripts/code_graph/resolution_tiers.ts +238 -0
- package/src/scripts/code_graph/sqlite_store.ts +262 -6
- package/src/scripts/code_graph/types.ts +117 -3
- package/src/scripts/code_graph/validate.ts +26 -1
- package/src/scripts/code_graph/verbs.ts +590 -0
- package/src/scripts/condense.ts +54 -42
- package/src/scripts/corpus_manifest.ts +5 -5
- package/src/scripts/council_cli.ts +11 -12
- package/src/scripts/count_intent_disagreement.ts +426 -0
- package/src/scripts/handoff_context_hook.ts +118 -1
- package/src/scripts/hook_effect_doctor.ts +82 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +87 -12
- package/src/scripts/hooks/block_speaking_inbox_dir.ts +182 -16
- package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
- package/src/scripts/hooks/code_graph_context_hook.ts +201 -0
- package/src/scripts/hooks/concern_registry.ts +4 -2
- package/src/scripts/hooks/host_semantics.ts +1 -1
- package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
- package/src/scripts/hooks/push_settle_hook.ts +3 -2
- package/src/scripts/hooks/rule_inject_hook.ts +108 -21
- package/src/scripts/hooks/session_eol_hook.ts +120 -2
- package/src/scripts/hooks/skill_route_hook.ts +11 -4
- package/src/scripts/hot_context_hook.ts +62 -7
- package/src/scripts/inbox_source_census.ts +322 -0
- package/src/scripts/install-hooks.sh +82 -0
- package/src/scripts/{lint_carrier_integrity.ts → lint_deferral_integrity.ts} +60 -214
- package/src/scripts/lint_plan_risk_register.ts +13 -17
- package/src/scripts/lint_roadmap_blockers.ts +164 -1
- package/src/scripts/mcp_server/consumer_tool_catalog.json +152 -2
- package/src/scripts/mcp_server/graph_tools.ts +294 -0
- package/src/scripts/mcp_server/path_util.ts +31 -0
- package/src/scripts/mcp_server/suggest_skill.ts +72 -0
- package/src/scripts/mcp_server/tool_catalog_source.ts +7 -1
- package/src/scripts/mcp_server/tools.ts +5 -69
- package/src/scripts/media/lib/adapter-contract.md +7 -2
- package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
- package/src/scripts/model_rule_injection.ts +61 -4
- package/src/scripts/project_thin_rules.ts +149 -16
- package/src/scripts/rdp_candidates_delta.ts +276 -0
- package/src/scripts/rdp_quality_eval.ts +217 -19
- package/src/scripts/release_mix_taxonomy.json +3 -2
- package/src/scripts/report_host_injection_effect.ts +189 -0
- package/src/scripts/report_layer_overlap.ts +41 -0
- package/src/scripts/report_skill_menu_census.ts +341 -0
- package/src/scripts/report_standing_payload_by_host.ts +650 -0
- package/src/scripts/schemas/agent-settings.schema.json +7 -1
- package/src/scripts/session_memory_index.ts +81 -8
- package/src/scripts/skill_tools/score_skill_relevance.ts +70 -6
- package/src/scripts/skill_tools/suggest_skill_for_task.ts +37 -3
- package/src/server/schemas/settings.ts +23 -4
- package/src/vendor/grammars/README.md +86 -0
- package/src/vendor/grammars/tree-sitter-javascript.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-php.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-typescript.wasm +0 -0
- package/src/scripts/_lib/carrier_status.ts +0 -118
- package/src/scripts/_lib/estate_carrier_cases.ts +0 -72
- package/src/scripts/hooks/code_graph_nudge_hook.ts +0 -191
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `check_continuity_surface` — the continuity estate walks DOWN, and a new
|
|
4
|
+
* continuity mechanism pays for itself.
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS EXISTS
|
|
7
|
+
* ---------------
|
|
8
|
+
* Twelve archived roadmaps built this package's continuity layers and not one
|
|
9
|
+
* retired the layer before it. `road-to-one-continuity-record` recorded that as
|
|
10
|
+
* its own Risk 1 — "the record is added and nothing is retired" — and stated
|
|
11
|
+
* its success as five numbers rather than a sentence:
|
|
12
|
+
*
|
|
13
|
+
* public continuity commands: 0
|
|
14
|
+
* session resume pickers: 0
|
|
15
|
+
* persistent continuity artefacts: 1
|
|
16
|
+
* continuity schemas: 1
|
|
17
|
+
* normal-path manual actions: 0
|
|
18
|
+
*
|
|
19
|
+
* A number nobody derives is a number nobody can be wrong about. Until this
|
|
20
|
+
* gate there was no command in the tree that produced those five figures: a
|
|
21
|
+
* grep for `end-state` over `src/scripts/` returned one unrelated hit, and a
|
|
22
|
+
* grep for `continuity` returned implementation prose and an unrelated
|
|
23
|
+
* AI-video seam. So "re-derived by command rather than asserted" was an
|
|
24
|
+
* obligation with no instrument.
|
|
25
|
+
*
|
|
26
|
+
* WHY IT PUBLISHES AN INVENTORY AND NOT ONLY FIVE NUMBERS
|
|
27
|
+
* ------------------------------------------------------
|
|
28
|
+
* The AI council of 2026-09-07 (2 seats, both present) found the failure mode
|
|
29
|
+
* before it happened: the five numbers are gameable by NAMING. Position 3 can
|
|
30
|
+
* be made to read `1` with nothing retired, simply by classifying an artefact
|
|
31
|
+
* as something other than "continuity". Both seats required the inventory rule
|
|
32
|
+
* to be behavioural instead —
|
|
33
|
+
*
|
|
34
|
+
* Any persisted state written for later session/run recovery,
|
|
35
|
+
* verification, or context restoration must be listed, whether or not it
|
|
36
|
+
* is labeled "continuity."
|
|
37
|
+
*
|
|
38
|
+
* — and required exclusions to stay legitimate AND stay visible. This gate
|
|
39
|
+
* therefore DISCOVERS its artefact corpus from behaviour (every distinct leaf
|
|
40
|
+
* under `agents/runtime/state/` that any source file names) rather than from
|
|
41
|
+
* vocabulary, requires the committed inventory to dispose of every one of them,
|
|
42
|
+
* and prints the exclusions with their reasons on every run — including the one
|
|
43
|
+
* the council named as the worked example, `checkpoints/`.
|
|
44
|
+
*
|
|
45
|
+
* THREE CHECKS, ONE GATE
|
|
46
|
+
* ----------------------
|
|
47
|
+
* T1 — inventory validity. Every discovered leaf is disposed of, every row's
|
|
48
|
+
* `locus` still resolves, every row carries a reason. A retirement is performed
|
|
49
|
+
* by DELETING a row, so a row left standing over a deleted surface reddens: the
|
|
50
|
+
* count cannot claim a subtraction the tree did not make.
|
|
51
|
+
*
|
|
52
|
+
* T2 — the ratchet. No axis may read HIGHER than it reads on the base ref. The
|
|
53
|
+
* floor is measured, not stored, from the base ref's own copy of the inventory
|
|
54
|
+
* — a single file, so `git show` rather than a materialised subtree, the same
|
|
55
|
+
* reasoning `check_estate_count` records for its concern axis. A count BELOW
|
|
56
|
+
* the floor is a drawdown and passes.
|
|
57
|
+
*
|
|
58
|
+
* T3 — the rule is stated where the check fails. The roadmap requires that a
|
|
59
|
+
* reader who trips this gate learns the rule without going to find it, so the
|
|
60
|
+
* failure text carries it verbatim rather than a pointer.
|
|
61
|
+
*
|
|
62
|
+
* WHAT THIS GATE DELIBERATELY DOES NOT DO
|
|
63
|
+
* ---------------------------------------
|
|
64
|
+
* It does not fail when an axis is above target. The targets are the roadmap's
|
|
65
|
+
* destination, not this gate's admission criterion: failing on them would red
|
|
66
|
+
* the tree on every branch until the whole retirement lands, which is the
|
|
67
|
+
* "gate that fails its own tree on debt the landing change did not cause"
|
|
68
|
+
* this repository reverts rather than adopts. Distance-to-target is REPORTED
|
|
69
|
+
* on every run and ratcheted downward; closing it is the roadmap's job.
|
|
70
|
+
*
|
|
71
|
+
* ./scripts-run src/scripts/check_continuity_surface
|
|
72
|
+
* ./scripts-run src/scripts/check_continuity_surface --json
|
|
73
|
+
* ./scripts-run src/scripts/check_continuity_surface --base origin/main
|
|
74
|
+
* ./scripts-run src/scripts/check_continuity_surface --self-test
|
|
75
|
+
*/
|
|
76
|
+
import { spawnSync } from 'node:child_process';
|
|
77
|
+
import * as fs from 'node:fs';
|
|
78
|
+
import * as os from 'node:os';
|
|
79
|
+
import * as path from 'node:path';
|
|
80
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
81
|
+
|
|
82
|
+
import {
|
|
83
|
+
AXES,
|
|
84
|
+
INVENTORY_POSIX,
|
|
85
|
+
TARGETS,
|
|
86
|
+
countAxes,
|
|
87
|
+
discoverStateLeaves,
|
|
88
|
+
readInventory,
|
|
89
|
+
validateInventory,
|
|
90
|
+
type Axis,
|
|
91
|
+
type Inventory,
|
|
92
|
+
type InventoryRow,
|
|
93
|
+
type SurfaceCounts,
|
|
94
|
+
} from './_lib/continuity_surface.js';
|
|
95
|
+
import { resolveBaseRef } from './_lib/ratchet_base_ref.js';
|
|
96
|
+
import { runSelfTest, type SelfTestCase } from './_lib/gate_self_test.js';
|
|
97
|
+
import { DeadScopeError, reportScanned } from './_lib/scan_scope.js';
|
|
98
|
+
|
|
99
|
+
const GATE = 'check_continuity_surface';
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Built by concatenation on purpose. The self-test below has to write fixture
|
|
103
|
+
* files that LOOK like real state paths, and a contiguous literal would be
|
|
104
|
+
* discovered by this gate's own walk — the gate would then red on its own
|
|
105
|
+
* fixtures. Excluding this file from the walk was the other option and is
|
|
106
|
+
* strictly worse: an excluded file is a blind spot, and the one file guaranteed
|
|
107
|
+
* to be edited by whoever wants to weaken this gate is this one.
|
|
108
|
+
*/
|
|
109
|
+
const _STATE_PREFIX = 'agents/runtime/' + 'state/';
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The rule the ratchet encodes, verbatim from the parent roadmap. Printed at
|
|
113
|
+
* the point of failure, per that roadmap's own verify line: "the rule is stated
|
|
114
|
+
* where the check fails, not only here".
|
|
115
|
+
*/
|
|
116
|
+
const RULE =
|
|
117
|
+
'A new continuity mechanism may be introduced only if it replaces an existing one, or\n' +
|
|
118
|
+
'demonstrably covers a capability the one record cannot.';
|
|
119
|
+
|
|
120
|
+
function repoRootFrom(start: string): string {
|
|
121
|
+
let cur = path.resolve(start);
|
|
122
|
+
for (;;) {
|
|
123
|
+
if (fs.existsSync(path.join(cur, INVENTORY_POSIX))) return cur;
|
|
124
|
+
const parent = path.dirname(cur);
|
|
125
|
+
if (parent === cur) return path.resolve(start);
|
|
126
|
+
cur = parent;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function gitShow(repoRoot: string, ref: string, rel: string): string | null {
|
|
131
|
+
const res = spawnSync('git', ['show', `${ref}:${rel}`], {
|
|
132
|
+
cwd: repoRoot,
|
|
133
|
+
encoding: 'utf-8',
|
|
134
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
135
|
+
});
|
|
136
|
+
if (res.status !== 0 || typeof res.stdout !== 'string') return null;
|
|
137
|
+
return res.stdout;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function countsAt(repoRoot: string, ref: string): SurfaceCounts | null {
|
|
141
|
+
const raw = gitShow(repoRoot, ref, INVENTORY_POSIX);
|
|
142
|
+
if (raw === null) return null;
|
|
143
|
+
try {
|
|
144
|
+
const parsed = JSON.parse(raw) as { rows?: unknown };
|
|
145
|
+
const rows = Array.isArray(parsed.rows) ? (parsed.rows as InventoryRow[]) : [];
|
|
146
|
+
return countAxes({ rows });
|
|
147
|
+
} catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function renderInventory(inv: Inventory, write: (s: string) => void): void {
|
|
153
|
+
for (const axis of AXES) {
|
|
154
|
+
const rows = inv.rows.filter((r) => r.axis === axis);
|
|
155
|
+
const counted = rows.filter((r) => r.disposition === 'counted');
|
|
156
|
+
const excluded = rows.filter((r) => r.disposition === 'excluded');
|
|
157
|
+
write(`\n ${axis} — counted ${String(counted.length)}, target ${String(TARGETS[axis])}\n`);
|
|
158
|
+
for (const row of counted) {
|
|
159
|
+
write(` ▸ ${row.id} (${row.locus})\n`);
|
|
160
|
+
}
|
|
161
|
+
if (excluded.length > 0) {
|
|
162
|
+
write(` excluded (${String(excluded.length)}) — visible by contract, never silent:\n`);
|
|
163
|
+
for (const row of excluded) {
|
|
164
|
+
write(` · ${row.id} (${row.locus})\n`);
|
|
165
|
+
write(` ${row.reason}\n`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface RunOptions {
|
|
172
|
+
repoRoot?: string;
|
|
173
|
+
baseRef?: string | null;
|
|
174
|
+
json?: boolean;
|
|
175
|
+
quiet?: boolean;
|
|
176
|
+
write?: (s: string) => void;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function run(opts: RunOptions = {}): number {
|
|
180
|
+
const write = opts.write ?? ((s: string) => void process.stdout.write(s));
|
|
181
|
+
const repoRoot = opts.repoRoot ?? repoRootFrom(process.cwd());
|
|
182
|
+
|
|
183
|
+
let inv: Inventory;
|
|
184
|
+
try {
|
|
185
|
+
inv = readInventory(repoRoot);
|
|
186
|
+
} catch (exc) {
|
|
187
|
+
write(`❌ ${GATE}: cannot read ${INVENTORY_POSIX} — ${String(exc)}\n`);
|
|
188
|
+
return 1;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const discovered = discoverStateLeaves(repoRoot);
|
|
192
|
+
try {
|
|
193
|
+
reportScanned(
|
|
194
|
+
{
|
|
195
|
+
gate: GATE,
|
|
196
|
+
scanned: discovered.length + inv.rows.length,
|
|
197
|
+
units: 'inventory row(s) + discovered state leaf/leaves',
|
|
198
|
+
roots: [INVENTORY_POSIX, 'src/scripts', 'src/agent-src/scripts', 'src/cli'],
|
|
199
|
+
},
|
|
200
|
+
(chunk: string) => {
|
|
201
|
+
write(String(chunk));
|
|
202
|
+
return true;
|
|
203
|
+
},
|
|
204
|
+
);
|
|
205
|
+
} catch (exc) {
|
|
206
|
+
if (exc instanceof DeadScopeError) {
|
|
207
|
+
write(`❌ ${exc.message}\n`);
|
|
208
|
+
return 1;
|
|
209
|
+
}
|
|
210
|
+
throw exc;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const findings = validateInventory(repoRoot, inv, discovered);
|
|
214
|
+
const counts = countAxes(inv);
|
|
215
|
+
|
|
216
|
+
const baseRef = opts.baseRef === undefined ? resolveBaseRef(repoRoot) : opts.baseRef;
|
|
217
|
+
const floor = baseRef === null ? null : countsAt(repoRoot, baseRef);
|
|
218
|
+
const grew: Array<{ axis: Axis; floor: number; live: number }> = [];
|
|
219
|
+
if (floor !== null) {
|
|
220
|
+
for (const axis of AXES) {
|
|
221
|
+
if (counts[axis] > floor[axis]) {
|
|
222
|
+
grew.push({ axis, floor: floor[axis], live: counts[axis] });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (opts.json === true) {
|
|
228
|
+
write(
|
|
229
|
+
JSON.stringify(
|
|
230
|
+
{
|
|
231
|
+
gate: GATE,
|
|
232
|
+
counts,
|
|
233
|
+
targets: TARGETS,
|
|
234
|
+
base_ref: baseRef,
|
|
235
|
+
floor,
|
|
236
|
+
grew,
|
|
237
|
+
findings,
|
|
238
|
+
discovered_leaves: discovered.length,
|
|
239
|
+
inventory_rows: inv.rows.length,
|
|
240
|
+
},
|
|
241
|
+
null,
|
|
242
|
+
2,
|
|
243
|
+
) + '\n',
|
|
244
|
+
);
|
|
245
|
+
return findings.length > 0 || grew.length > 0 ? 1 : 0;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (opts.quiet !== true) {
|
|
249
|
+
write(`\n${GATE}: the five end-state numbers, derived from ${INVENTORY_POSIX}\n`);
|
|
250
|
+
renderInventory(inv, write);
|
|
251
|
+
write('\n ');
|
|
252
|
+
write(AXES.map((a) => String(counts[a])).join(' / '));
|
|
253
|
+
write(' (target ');
|
|
254
|
+
write(AXES.map((a) => String(TARGETS[a])).join(' / '));
|
|
255
|
+
write(')\n');
|
|
256
|
+
const remaining = AXES.filter((a) => counts[a] !== TARGETS[a]);
|
|
257
|
+
if (remaining.length === 0) {
|
|
258
|
+
write(' ✅ every axis is at target.\n');
|
|
259
|
+
} else {
|
|
260
|
+
write(
|
|
261
|
+
` · ${String(remaining.length)} axis/axes above target — reported, not failed: ` +
|
|
262
|
+
'the targets are the roadmap destination, not this gate\'s admission criterion.\n',
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
if (floor === null) {
|
|
266
|
+
write(
|
|
267
|
+
` · no floor: ${baseRef === null ? 'no base ref resolved' : `${baseRef} carries no ${INVENTORY_POSIX}`} — ` +
|
|
268
|
+
'the ratchet did not run this time.\n',
|
|
269
|
+
);
|
|
270
|
+
} else {
|
|
271
|
+
write(` · floor from ${String(baseRef)}: ${AXES.map((a) => String(floor[a])).join(' / ')}\n`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (findings.length === 0 && grew.length === 0) {
|
|
276
|
+
if (opts.quiet !== true) {
|
|
277
|
+
write(`✅ ${GATE}: inventory describes the tree, and no axis grew.\n`);
|
|
278
|
+
}
|
|
279
|
+
return 0;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (grew.length > 0) {
|
|
283
|
+
write(`\n❌ ${GATE}: a continuity axis grew above the base ref.\n\n`);
|
|
284
|
+
for (const g of grew) {
|
|
285
|
+
write(` ${g.axis}: ${String(g.floor)} → ${String(g.live)}\n`);
|
|
286
|
+
}
|
|
287
|
+
write('\n The rule this ratchet encodes:\n\n');
|
|
288
|
+
for (const line of RULE.split('\n')) write(` ${line}\n`);
|
|
289
|
+
write(
|
|
290
|
+
'\n So: retire one in the same change (delete its row — that is the subtraction),\n' +
|
|
291
|
+
' or state in the row\'s `reason` which capability the one record cannot cover.\n',
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (findings.length > 0) {
|
|
296
|
+
write(`\n❌ ${GATE}: ${String(findings.length)} inventory finding(s).\n\n`);
|
|
297
|
+
for (const f of findings) {
|
|
298
|
+
write(` [${f.kind}] ${f.detail}\n`);
|
|
299
|
+
}
|
|
300
|
+
write(
|
|
301
|
+
`\n The inventory is ${INVENTORY_POSIX}. It is behavioural by contract: every\n` +
|
|
302
|
+
' persisted-state leaf under agents/runtime/state/ that any source names must be\n' +
|
|
303
|
+
' disposed of there, because a count keyed on the WORD "continuity" is gameable by\n' +
|
|
304
|
+
' renaming — which is the defect this gate exists to make impossible.\n',
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
return 1;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function _mkRepo(dir: string, rows: unknown[]): void {
|
|
311
|
+
fs.mkdirSync(path.join(dir, 'src', 'config'), { recursive: true });
|
|
312
|
+
fs.mkdirSync(path.join(dir, 'src', 'scripts'), { recursive: true });
|
|
313
|
+
fs.writeFileSync(
|
|
314
|
+
path.join(dir, INVENTORY_POSIX),
|
|
315
|
+
JSON.stringify({ targets: TARGETS, rows }, null, 2),
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function _row(over: Partial<InventoryRow> = {}): InventoryRow {
|
|
320
|
+
return {
|
|
321
|
+
id: 'placeholder.json',
|
|
322
|
+
axis: 'persistent_continuity_artefacts',
|
|
323
|
+
disposition: 'excluded',
|
|
324
|
+
locus: INVENTORY_POSIX,
|
|
325
|
+
reason: 'a reason long enough to clear the substance floor this gate enforces',
|
|
326
|
+
...over,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
function selfTest(): number {
|
|
332
|
+
const mk = (): string => fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), 'ccs-'));
|
|
333
|
+
const silent = (): void => {};
|
|
334
|
+
|
|
335
|
+
const cases: SelfTestCase[] = [
|
|
336
|
+
{
|
|
337
|
+
name: 'a persisted-state leaf the inventory does not dispose of → reject',
|
|
338
|
+
expect: 'reject',
|
|
339
|
+
run: () => {
|
|
340
|
+
const dir = mk();
|
|
341
|
+
_mkRepo(dir, []);
|
|
342
|
+
fs.writeFileSync(
|
|
343
|
+
path.join(dir, 'src', 'scripts', 'newthing.ts'),
|
|
344
|
+
`const P = '${_STATE_PREFIX}brand-new-leaf.json';\n`,
|
|
345
|
+
);
|
|
346
|
+
return run({ repoRoot: dir, baseRef: null, quiet: true, write: silent });
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
name: 'a row whose locus no longer resolves → reject (a claimed retirement the tree did not make)',
|
|
351
|
+
expect: 'reject',
|
|
352
|
+
run: () => {
|
|
353
|
+
const dir = mk();
|
|
354
|
+
_mkRepo(dir, [_row({ id: 'ghost', locus: 'src/scripts/deleted_thing.ts' })]);
|
|
355
|
+
return run({ repoRoot: dir, baseRef: null, quiet: true, write: silent });
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
name: 'a counted axis above the base-ref floor → reject',
|
|
360
|
+
expect: 'reject',
|
|
361
|
+
run: () => {
|
|
362
|
+
const dir = mk();
|
|
363
|
+
_mkRepo(dir, [
|
|
364
|
+
_row({ id: 'a', disposition: 'counted' }),
|
|
365
|
+
_row({ id: 'b', disposition: 'counted' }),
|
|
366
|
+
]);
|
|
367
|
+
const git = (args: string[]): void => {
|
|
368
|
+
spawnSync('git', args, { cwd: dir, encoding: 'utf-8' });
|
|
369
|
+
};
|
|
370
|
+
git(['init', '-q']);
|
|
371
|
+
git(['config', 'user.email', 'g@example.com']);
|
|
372
|
+
git(['config', 'user.name', 'g']);
|
|
373
|
+
// Base carries ONE counted row; HEAD carries two.
|
|
374
|
+
_mkRepo(dir, [_row({ id: 'a', disposition: 'counted' })]);
|
|
375
|
+
git(['add', '-A']);
|
|
376
|
+
git(['commit', '-qm', 'base']);
|
|
377
|
+
const base = spawnSync('git', ['rev-parse', 'HEAD'], {
|
|
378
|
+
cwd: dir,
|
|
379
|
+
encoding: 'utf-8',
|
|
380
|
+
}).stdout.trim();
|
|
381
|
+
_mkRepo(dir, [
|
|
382
|
+
_row({ id: 'a', disposition: 'counted' }),
|
|
383
|
+
_row({ id: 'b', disposition: 'counted' }),
|
|
384
|
+
]);
|
|
385
|
+
return run({ repoRoot: dir, baseRef: base, quiet: true, write: silent });
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: 'a row with no reason of substance → reject',
|
|
390
|
+
expect: 'reject',
|
|
391
|
+
run: () => {
|
|
392
|
+
const dir = mk();
|
|
393
|
+
_mkRepo(dir, [_row({ id: 'terse', reason: 'because' })]);
|
|
394
|
+
return run({ repoRoot: dir, baseRef: null, quiet: true, write: silent });
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
name: 'a leaf named only inside a *.test.ts fixture is not a surface → accept',
|
|
399
|
+
expect: 'accept',
|
|
400
|
+
run: () => {
|
|
401
|
+
const dir = mk();
|
|
402
|
+
// A real leaf as well, so the corpus is non-empty and the case
|
|
403
|
+
// isolates the ONE property under test: the `.test.ts` leaf is
|
|
404
|
+
// not flagged. Without it the gate would refuse an empty scan
|
|
405
|
+
// and the case would pass for the wrong reason.
|
|
406
|
+
_mkRepo(dir, [_row({ id: 'real-leaf.json' })]);
|
|
407
|
+
fs.writeFileSync(
|
|
408
|
+
path.join(dir, 'src', 'scripts', 'thing.ts'),
|
|
409
|
+
`const P = '${_STATE_PREFIX}real-leaf.json';\n`,
|
|
410
|
+
);
|
|
411
|
+
fs.writeFileSync(
|
|
412
|
+
path.join(dir, 'src', 'scripts', 'thing.test.ts'),
|
|
413
|
+
`const P = '${_STATE_PREFIX}fixture-only.json';\n`,
|
|
414
|
+
);
|
|
415
|
+
return run({ repoRoot: dir, baseRef: null, quiet: true, write: silent });
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
name: 'a disposed leaf plus a resolving locus, no growth → accept',
|
|
420
|
+
expect: 'accept',
|
|
421
|
+
run: () => {
|
|
422
|
+
const dir = mk();
|
|
423
|
+
_mkRepo(dir, [_row({ id: 'real-leaf.json' })]);
|
|
424
|
+
fs.writeFileSync(
|
|
425
|
+
path.join(dir, 'src', 'scripts', 'thing.ts'),
|
|
426
|
+
`const P = '${_STATE_PREFIX}real-leaf.json';\n`,
|
|
427
|
+
);
|
|
428
|
+
return run({ repoRoot: dir, baseRef: null, quiet: true, write: silent });
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
name: 'the shipped inventory describes the shipped tree → accept',
|
|
433
|
+
expect: 'accept',
|
|
434
|
+
run: () =>
|
|
435
|
+
run({
|
|
436
|
+
repoRoot: repoRootFrom(path.dirname(fileURLToPath(import.meta.url))),
|
|
437
|
+
baseRef: null,
|
|
438
|
+
quiet: true,
|
|
439
|
+
write: silent,
|
|
440
|
+
}),
|
|
441
|
+
},
|
|
442
|
+
];
|
|
443
|
+
|
|
444
|
+
return runSelfTest({ gate: GATE, cases, minCases: 6, minRejectCases: 4 });
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
448
|
+
if (argv.includes('--self-test')) return selfTest();
|
|
449
|
+
const baseIdx = argv.indexOf('--base');
|
|
450
|
+
const baseRef = baseIdx >= 0 ? (argv[baseIdx + 1] ?? null) : undefined;
|
|
451
|
+
return run({
|
|
452
|
+
json: argv.includes('--json'),
|
|
453
|
+
quiet: argv.includes('--quiet'),
|
|
454
|
+
...(baseRef !== undefined ? { baseRef } : {}),
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
|
|
459
|
+
function _isCliEntry(): boolean {
|
|
460
|
+
if (typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__) return false;
|
|
461
|
+
if (process.argv[1] === undefined) return false;
|
|
462
|
+
if (import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) return true;
|
|
463
|
+
try {
|
|
464
|
+
return (
|
|
465
|
+
fs.realpathSync(fileURLToPath(import.meta.url)) ===
|
|
466
|
+
fs.realpathSync(path.resolve(process.argv[1]))
|
|
467
|
+
);
|
|
468
|
+
} catch {
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (_isCliEntry()) {
|
|
474
|
+
process.exitCode = main();
|
|
475
|
+
}
|
|
@@ -47,13 +47,20 @@ const REPO_ROOT = path.resolve(fileURLToPath(import.meta.url), '..', '..', '..')
|
|
|
47
47
|
* cached or it is not), but it also forfeits patch fixes — so each one is
|
|
48
48
|
* named here rather than silently tolerated.
|
|
49
49
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
50
|
+
* `web-tree-sitter` returned to `dependencies` under ADR-259 (amended
|
|
51
|
+
* 2026-09-07), so this gate sees it again and it needs its exception back. The
|
|
52
|
+
* grammars it loads are vendored at `src/vendor/grammars/` rather than pulled
|
|
53
|
+
* from `tree-sitter-wasms`, which stays a devDependency and is therefore still
|
|
54
|
+
* outside this gate's scope.
|
|
55
55
|
*/
|
|
56
|
-
export const EXACT_PIN_EXCEPTIONS: Readonly<Record<string, string>> = {
|
|
56
|
+
export const EXACT_PIN_EXCEPTIONS: Readonly<Record<string, string>> = {
|
|
57
|
+
'web-tree-sitter':
|
|
58
|
+
'ABI-coupled to the vendored grammar set (grammar ABI 14, ' +
|
|
59
|
+
'`EXPECTED_GRAMMAR_ABI` in code_graph/types.ts). A patch bump can move the ABI, ' +
|
|
60
|
+
'which would make every grammar load throw rather than degrade — so the runtime ' +
|
|
61
|
+
'and its grammars move together, in one reviewed change, or not at all. ' +
|
|
62
|
+
'Refresh procedure: src/vendor/grammars/README.md.',
|
|
63
|
+
};
|
|
57
64
|
|
|
58
65
|
/**
|
|
59
66
|
* Floors that are non-settled ON PURPOSE, because every version below them is
|