@event4u/agent-config 14.20.0 → 14.22.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 +441 -0
- package/CREDITS.md +37 -0
- package/README.md +1 -1
- package/agents/templates/.ai-council.yml.example +10 -0
- package/dist/agent-src/commands/analyze/inbox.md +381 -6
- package/dist/agent-src/commands/analyze/{reference-repo.md → repo.md} +251 -34
- package/dist/agent-src/commands/analyze/roadmap-repos.md +129 -0
- package/dist/agent-src/commands/analyze.md +12 -7
- package/dist/agent-src/commands/feature/plan.md +55 -16
- package/dist/agent-src/commands/feature/refactor.md +8 -3
- package/dist/agent-src/commands/humanize.md +48 -4
- 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/contract-decision-sheet.md +22 -1
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +4 -1
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +35 -2
- package/dist/agent-src/contexts/execution/terminal-states.md +4 -1
- package/dist/agent-src/rules/evaluator-independence.md +4 -4
- package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
- package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
- 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 +14 -23
- 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 +25 -0
- package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
- package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +27 -11
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +1 -1
- package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
- package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
- package/dist/agent-src/skills/technical-specification/SKILL.md +4 -0
- 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/agent-src/templates/features.md +5 -0
- package/dist/agent-src/templates/roadmaps.md +15 -0
- 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 +494 -0
- package/dist/cli-delegate/check_update_banner.js +923 -0
- package/dist/cli-delegate/{chunk-KPBWFOPZ.js → chunk-2FTMENJL.js} +194 -37
- package/dist/cli-delegate/{chunk-PI2GU3DP.js → chunk-2TAQ2K4V.js} +4 -1
- package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
- package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
- package/dist/cli-delegate/{chunk-C6IPZRDA.js → chunk-BIXEDQPZ.js} +7 -1
- package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
- package/dist/cli-delegate/{chunk-NUMQHLS5.js → chunk-J3RFNKG4.js} +21 -2
- package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
- package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
- package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
- package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-TGIPZBCW.js} +1 -1
- 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 +19 -5
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +27 -3
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_session_recycle.js +2 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- 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 +60 -29
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +7 -6
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +6 -5
- package/dist/hooks/dispatch.js +1522 -1008
- 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 +10 -1
- package/dist/install/partitionEligibility.js +231 -117
- package/dist/install/ruleLayerPartition.js +10 -7
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +39376 -38902
- package/dist/scripts/ai_council/content_ceiling.js +63 -0
- package/dist/scripts/ai_council/seat_policy.js +106 -0
- package/dist/scripts/ai_council/transport_resolver.js +21 -0
- package/dist/scripts/condense.js +32 -25
- package/docs/MIGRATION.md +49 -1
- package/docs/contracts/settings-classes.md +1 -1
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
- package/llms.txt +14 -14
- package/package.json +7 -4
- package/src/agent-src/scripts/_cli_entry.ts +51 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
- 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/continuity-surface.json +509 -0
- package/src/config/estate-count-budget.json +1 -1
- 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/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/release-gate-locality.yml +4 -3
- 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/_dispatch.bash +77 -27
- package/src/scripts/_lib/auto_dispatch.ts +2 -2
- package/src/scripts/_lib/blocked_by_marker.ts +189 -0
- package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
- package/src/scripts/_lib/continuity_surface.ts +296 -0
- package/src/scripts/_lib/corpus_manifest.ts +26 -9
- package/src/scripts/_lib/hook_settings.ts +1 -1
- package/src/scripts/_lib/host_capability.ts +38 -1
- package/src/scripts/_lib/injection_effect.ts +151 -0
- package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
- package/src/scripts/_lib/packed_binary_predicate.ts +225 -0
- package/src/scripts/_lib/pin_resolver.ts +13 -0
- package/src/scripts/_lib/regression_neighbourhood.ts +1 -1
- package/src/scripts/_lib/release_highlights.ts +19 -211
- package/src/scripts/_lib/release_material.ts +27 -77
- package/src/scripts/_lib/retired_status.ts +74 -0
- package/src/scripts/_lib/skill_catalogue.ts +156 -21
- package/src/scripts/_lib/structured_ask.ts +117 -0
- package/src/scripts/_lib/subagent_capsule.ts +117 -8
- package/src/scripts/_lib/tty_prompt.ts +76 -0
- package/src/scripts/ai_council/api_transport.ts +168 -0
- package/src/scripts/ai_council/clients.ts +58 -63
- package/src/scripts/ai_council/config.ts +13 -0
- package/src/scripts/ai_council/content_ceiling.ts +79 -0
- package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
- package/src/scripts/ai_council/jury_aggregate.ts +178 -0
- package/src/scripts/ai_council/prompts.ts +9 -0
- package/src/scripts/ai_council/seat_policy.ts +137 -0
- package/src/scripts/ai_council/status_surface.ts +79 -0
- package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
- package/src/scripts/ai_council/transport_resolver.ts +59 -1
- package/src/scripts/ai_tells_rules.ts +446 -5
- package/src/scripts/ask_block_census.ts +499 -0
- package/src/scripts/bench_humanizer_eval.ts +264 -40
- package/src/scripts/bench_prose_tells_fp.ts +198 -0
- package/src/scripts/capture_skill_catalogue.ts +24 -11
- package/src/scripts/check_branch_work_committed.ts +234 -0
- 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_pack_size.ts +263 -24
- package/src/scripts/check_release_highlights.ts +0 -157
- package/src/scripts/check_roadmap_trackable.ts +19 -16
- package/src/scripts/check_rule_layer_partition.ts +54 -34
- package/src/scripts/check_single_delivery.ts +97 -31
- package/src/scripts/check_update_banner.ts +13 -0
- package/src/scripts/code_graph/build.ts +138 -15
- package/src/scripts/code_graph/cli.ts +76 -8
- 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 +121 -11
- package/src/scripts/code_graph/resolution_tiers.ts +238 -0
- package/src/scripts/code_graph/sqlite_store.ts +247 -6
- package/src/scripts/code_graph/types.ts +66 -1
- package/src/scripts/code_graph/validate.ts +23 -0
- package/src/scripts/condense.ts +34 -22
- package/src/scripts/corpus_manifest.ts +5 -5
- package/src/scripts/council_cli.ts +29 -39
- package/src/scripts/count_intent_disagreement.ts +426 -0
- package/src/scripts/detect_ai_tells.ts +142 -20
- package/src/scripts/handoff_context_hook.ts +79 -0
- package/src/scripts/harvest_reference_tokens.ts +698 -0
- package/src/scripts/hook_effect_doctor.ts +82 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +94 -12
- package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
- package/src/scripts/hooks/code_graph_context_hook.ts +212 -0
- package/src/scripts/hooks/concern_registry.ts +6 -2
- package/src/scripts/hooks/delegation_nudge_hook.ts +45 -1
- package/src/scripts/hooks/host_semantics.ts +1 -1
- package/src/scripts/hooks/one_question_per_ask_hook.ts +191 -0
- package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
- package/src/scripts/hooks/rule_inject_hook.ts +1 -1
- package/src/scripts/hooks/skill_route_hook.ts +11 -4
- package/src/scripts/inbox_source_census.ts +322 -0
- package/src/scripts/install-hooks.sh +134 -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 +51 -1
- package/src/scripts/mcp_server/suggest_skill.ts +72 -0
- package/src/scripts/mcp_server/tools.ts +2 -56
- 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/migrate_command_suggestions.ts +1 -1
- package/src/scripts/probe_unblocked_ask.ts +135 -2
- package/src/scripts/rdp_candidates_delta.ts +276 -0
- package/src/scripts/rdp_quality_eval.ts +217 -19
- package/src/scripts/release.ts +26 -71
- package/src/scripts/release_drill.ts +6 -10
- package/src/scripts/release_env.ts +1 -0
- package/src/scripts/release_mix_taxonomy.json +3 -2
- package/src/scripts/release_publication.ts +34 -29
- 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/self_review_gate.ts +409 -15
- 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/vendor/grammars/README.md +61 -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
|
@@ -30,6 +30,18 @@ if [ -n "${AGENT_CONFIG_HOOKS_DIR+set}" ]; then
|
|
|
30
30
|
exit 2
|
|
31
31
|
fi
|
|
32
32
|
HOOKS_DIR="$AGENT_CONFIG_HOOKS_DIR"
|
|
33
|
+
elif CONFIGURED_HOOKS_PATH="$(git -C "$PROJECT_ROOT" config --get core.hooksPath 2>/dev/null)" \
|
|
34
|
+
&& [ -n "$CONFIGURED_HOOKS_PATH" ]; then
|
|
35
|
+
# core.hooksPath wins over .git/hooks WHEREVER IT IS SET — git stops looking
|
|
36
|
+
# in the common dir entirely once it is configured. Installing into
|
|
37
|
+
# .git/hooks while git reads somewhere else writes six hooks that never run,
|
|
38
|
+
# and the freshness gate would then compare the copy nothing executes.
|
|
39
|
+
# Resolved relative to PROJECT_ROOT because git interprets a relative
|
|
40
|
+
# core.hooksPath against the top level of the working tree, not against CWD.
|
|
41
|
+
case "$CONFIGURED_HOOKS_PATH" in
|
|
42
|
+
/*) HOOKS_DIR="$CONFIGURED_HOOKS_PATH" ;;
|
|
43
|
+
*) HOOKS_DIR="$PROJECT_ROOT/$CONFIGURED_HOOKS_PATH" ;;
|
|
44
|
+
esac
|
|
33
45
|
elif COMMON_GIT_DIR="$(git -C "$PROJECT_ROOT" rev-parse --git-common-dir 2>/dev/null)"; then
|
|
34
46
|
case "$COMMON_GIT_DIR" in /*) : ;; *) COMMON_GIT_DIR="$PROJECT_ROOT/$COMMON_GIT_DIR" ;; esac
|
|
35
47
|
HOOKS_DIR="$COMMON_GIT_DIR/hooks"
|
|
@@ -152,6 +164,58 @@ elif ! ./scripts-run src/scripts/check_branch_freshness --quiet; then
|
|
|
152
164
|
exit 1
|
|
153
165
|
fi
|
|
154
166
|
|
|
167
|
+
# BRANCH WORK COMMITTED — after base freshness, before everything else.
|
|
168
|
+
#
|
|
169
|
+
# Ordering is deliberate in BOTH directions. It runs AFTER base freshness
|
|
170
|
+
# because the remedy there (`task push-ready`) merges, and a merge changes the
|
|
171
|
+
# working tree this gate reads — checking first would answer against a tree the
|
|
172
|
+
# operator is about to replace. It runs BEFORE the remaining gates because they
|
|
173
|
+
# all answer against the working tree, and a tree carrying unfinished work
|
|
174
|
+
# makes their verdicts describe something that is not being pushed.
|
|
175
|
+
#
|
|
176
|
+
# Measured twice, and both placements were wrong for a different reason.
|
|
177
|
+
# Placing it FIRST reded tests/scripts/prepush_base_freshness.test.ts, which
|
|
178
|
+
# executes the whole hook and asserts the freshness block is reached. Placing
|
|
179
|
+
# it between the next block's comment header and that block's `echo` reded
|
|
180
|
+
# check_installed_hooks_fresh's advisory pin, whose extraction runs from that
|
|
181
|
+
# header to the following task-availability check and so swallowed this block's
|
|
182
|
+
# `exit 1`. A block goes BEFORE a neighbour's comment header, never between the
|
|
183
|
+
# header and its code.
|
|
184
|
+
#
|
|
185
|
+
# And this paragraph may NOT name that header literally: the pin locates it with
|
|
186
|
+
# a plain indexOf over the rendered hook, so a comment quoting the string
|
|
187
|
+
# becomes the first match and truncates the block to nothing. An anchor that is
|
|
188
|
+
# also content is not an anchor — spelled out here because the obvious edit to
|
|
189
|
+
# this comment is to put the string back in for clarity.
|
|
190
|
+
#
|
|
191
|
+
# The defect it exists for, measured 2026-09-07: a reconciled baseline was
|
|
192
|
+
# written into gate-violation-baselines.json AFTER `git add`, so
|
|
193
|
+
# `git commit --no-edit` captured the staged side, the edit stayed in the tree,
|
|
194
|
+
# and the push shipped a branch whose commits contradicted it. CI cannot see
|
|
195
|
+
# this class at all — it checks out the commit, not the tree the edit is in.
|
|
196
|
+
#
|
|
197
|
+
# It blocks only what it can attribute to THIS branch (staged paths, and dirty
|
|
198
|
+
# paths this branch's own commits touch). A gate script's report output and a
|
|
199
|
+
# parallel session's edit are named and waved through, which is what keeps this
|
|
200
|
+
# from becoming the blanket clean-tree rule that would train the operator to
|
|
201
|
+
# set the skip variable.
|
|
202
|
+
echo "🔍 Branch work committed — is any of this branch's own work still uncommitted?"
|
|
203
|
+
if [ "${AGENT_CONFIG_SKIP_PREPUSH_WORKTREE:-}" = "1" ]; then
|
|
204
|
+
echo "⏭️ skipped via AGENT_CONFIG_SKIP_PREPUSH_WORKTREE=1"
|
|
205
|
+
elif [ ! -x ./scripts-run ]; then
|
|
206
|
+
echo "⚠️ ./scripts-run not found — skipping the worktree check for this push."
|
|
207
|
+
elif ! ./scripts-run src/scripts/check_branch_work_committed --quiet; then
|
|
208
|
+
echo ""
|
|
209
|
+
echo " Push blocked — this branch's own work is not all committed, so the"
|
|
210
|
+
echo " pushed branch would carry commits its own tree contradicts."
|
|
211
|
+
echo ""
|
|
212
|
+
echo " Commit them, or 'git restore' whatever was not meant to land."
|
|
213
|
+
echo ""
|
|
214
|
+
echo " This hook refuses; it never commits for you. Bypass a genuine WIP"
|
|
215
|
+
echo " push with AGENT_CONFIG_SKIP_PREPUSH_WORKTREE=1."
|
|
216
|
+
exit 1
|
|
217
|
+
fi
|
|
218
|
+
|
|
155
219
|
# INSTALLED-HOOK FRESHNESS — ADVISORY, and deliberately AFTER base freshness.
|
|
156
220
|
#
|
|
157
221
|
# What it answers: are the hooks in .git/hooks still what THIS checkout's
|
|
@@ -522,3 +586,73 @@ EOF
|
|
|
522
586
|
append_auto_sync_block "post-merge" "1"
|
|
523
587
|
append_auto_sync_block "post-checkout" "3"
|
|
524
588
|
echo "✅ Auto-sync block appended to post-merge / post-checkout hooks." >&2
|
|
589
|
+
|
|
590
|
+
# Code-graph freshness, from git, with no daemon ----------------------------
|
|
591
|
+
#
|
|
592
|
+
# road-to-a-graph-that-is-shipped 1.3. A code graph goes stale the moment a
|
|
593
|
+
# commit lands, and the only thing that knew were the reader — after it had
|
|
594
|
+
# already answered from a stale index. These two hooks close that without a
|
|
595
|
+
# resident process: git tells us HEAD moved, and a detached child rebuilds.
|
|
596
|
+
#
|
|
597
|
+
# Four properties, each load-bearing:
|
|
598
|
+
#
|
|
599
|
+
# · BACKGROUND. The refresh is `&`-detached with stdout/stderr closed, so a
|
|
600
|
+
# commit never waits on a parse. Git's own runtime is unchanged.
|
|
601
|
+
# · SINGLE-FLIGHT. `mkdir` is atomic on every POSIX filesystem, so of two
|
|
602
|
+
# concurrent commits exactly one creates the lock and exactly one refresh
|
|
603
|
+
# runs. This is Risk 3 in the roadmap's register — "git hooks pile up
|
|
604
|
+
# rebuilds" — and a lock file written with `>` would not have closed it,
|
|
605
|
+
# because two processes can both truncate-and-write the same path.
|
|
606
|
+
# · BUDGETED. `--budget-seconds` is the engine's own wall-clock ceiling; a
|
|
607
|
+
# child that exceeds it is SIGKILLed and the OLD cache is left untouched
|
|
608
|
+
# (cli.ts's refresh contract). Kill register K7 — never block on a stale
|
|
609
|
+
# graph — holds by construction here, since nothing waits at all.
|
|
610
|
+
# · NEVER BUILDS ONE UNBIDDEN. The block is a no-op unless a cache already
|
|
611
|
+
# exists. Building a graph for someone who never asked for one is the pure
|
|
612
|
+
# cost Risk 1 names; refreshing one they built is the service.
|
|
613
|
+
#
|
|
614
|
+
# The lock is a directory under the gitignored runtime state, so a crashed
|
|
615
|
+
# child leaves a stale lock that the next refresh clears by age rather than
|
|
616
|
+
# blocking forever.
|
|
617
|
+
|
|
618
|
+
append_code_graph_refresh_block() {
|
|
619
|
+
local name="$1"
|
|
620
|
+
cat >> "$HOOKS_DIR/$name" << EOF
|
|
621
|
+
|
|
622
|
+
# --- code-graph background refresh ------------------------------------------
|
|
623
|
+
EOF
|
|
624
|
+
if [ "$name" = "post-checkout" ]; then
|
|
625
|
+
cat >> "$HOOKS_DIR/$name" << 'EOF'
|
|
626
|
+
# A file-checkout ($3 = 0) moves no commits, so there is nothing to be behind.
|
|
627
|
+
if [ "${3:-1}" = "0" ]; then
|
|
628
|
+
exit 0
|
|
629
|
+
fi
|
|
630
|
+
EOF
|
|
631
|
+
fi
|
|
632
|
+
cat >> "$HOOKS_DIR/$name" << 'EOF'
|
|
633
|
+
cg_cache="agents/runtime/state/code-graph-v1.json"
|
|
634
|
+
cg_lock="agents/runtime/state/code-graph-refresh.lock"
|
|
635
|
+
if [ -f "$cg_cache" ] && [ -x ./agent-config ]; then
|
|
636
|
+
# Clear a lock left by a killed child: older than the largest budget any
|
|
637
|
+
# caller passes, so it cannot race a refresh that is legitimately running.
|
|
638
|
+
if [ -d "$cg_lock" ]; then
|
|
639
|
+
if [ -z "$(find "$cg_lock" -maxdepth 0 -mmin -5 2>/dev/null)" ]; then
|
|
640
|
+
rmdir "$cg_lock" 2>/dev/null || true
|
|
641
|
+
fi
|
|
642
|
+
fi
|
|
643
|
+
# Atomic: exactly one of N concurrent hooks wins this, and the losers do
|
|
644
|
+
# nothing rather than queueing a second rebuild behind the first.
|
|
645
|
+
if mkdir "$cg_lock" 2>/dev/null; then
|
|
646
|
+
(
|
|
647
|
+
trap 'rmdir "$cg_lock" 2>/dev/null || true' EXIT
|
|
648
|
+
./agent-config code-graph refresh --budget-seconds 60 >/dev/null 2>&1 || true
|
|
649
|
+
) >/dev/null 2>&1 &
|
|
650
|
+
fi
|
|
651
|
+
fi
|
|
652
|
+
exit 0
|
|
653
|
+
EOF
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
append_code_graph_refresh_block "post-commit"
|
|
657
|
+
append_code_graph_refresh_block "post-checkout"
|
|
658
|
+
echo "✅ Code-graph refresh block appended to post-commit / post-checkout hooks." >&2
|
|
@@ -37,16 +37,16 @@
|
|
|
37
37
|
* three are red here, deliberately, until a disposition vocabulary exists to
|
|
38
38
|
* express them.
|
|
39
39
|
*
|
|
40
|
-
* A SECOND ENUMERATION
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
40
|
+
* A SECOND ENUMERATION used to run beside this one, over the live roadmaps that
|
|
41
|
+
* declared `status: carrier`, reddening any that no archived parent named. It
|
|
42
|
+
* existed only to police the three exemptions that status bought — from the
|
|
43
|
+
* active roadmap count, from `check_roadmap_trackable` and from the plan risk
|
|
44
|
+
* register — so that a file nobody carried anything to could not wear them for
|
|
45
|
+
* free. ADR-262 deleted the status and all three exemptions, leaving nothing to
|
|
46
|
+
* police: a file that would once have been an unjustified carrier is now
|
|
47
|
+
* ordinary estate, caught by those gates for present-tense reasons rather than
|
|
48
|
+
* for its ancestry. The reciprocal direction — every carry names a destination
|
|
49
|
+
* that exists and back-links — is this enumeration, and it is untouched.
|
|
50
50
|
*
|
|
51
51
|
* Exit codes: 0 = every carry resolves, 1 = at least one does not.
|
|
52
52
|
*/
|
|
@@ -102,12 +102,12 @@ const EXCLUDE_NAMES: ReadonlySet<string> = new Set([
|
|
|
102
102
|
* is an obligation that had a recorded home and lost it. Deleting a live
|
|
103
103
|
* receiver produces this class, not the other one.
|
|
104
104
|
*/
|
|
105
|
-
export type
|
|
105
|
+
export type DeferralProblemClass = 'unannotated' | 'broken-destination';
|
|
106
106
|
|
|
107
|
-
export interface
|
|
107
|
+
export interface DeferralProblem {
|
|
108
108
|
/** Repo-relative path of the archived roadmap whose carry is broken. */
|
|
109
109
|
source: string;
|
|
110
|
-
cls:
|
|
110
|
+
cls: DeferralProblemClass;
|
|
111
111
|
detail: string;
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -197,7 +197,6 @@ function _locate(root: string, slug: string): { file: string; dir: 'active' | 'l
|
|
|
197
197
|
const OPEN_STEP_RE = /^[ \t]*[-*][ \t]*\[[ ][ \t]*\][ \t]/;
|
|
198
198
|
/** A fenced block's delimiter, in either of the two fence styles in use here. */
|
|
199
199
|
const FENCE_RE = /^[ \t]*(?:```|~~~)/;
|
|
200
|
-
const CARRIER_STATUS_RE = /^status:[ \t]*carrier[ \t]*$/m;
|
|
201
200
|
|
|
202
201
|
/**
|
|
203
202
|
* True when an OPEN step appears OUTSIDE every fenced block.
|
|
@@ -222,11 +221,6 @@ export function hasOpenStep(text: string): boolean {
|
|
|
222
221
|
return false;
|
|
223
222
|
}
|
|
224
223
|
|
|
225
|
-
/** True when the frontmatter — not the body — declares `status: carrier`. */
|
|
226
|
-
export function declaresCarrier(text: string): boolean {
|
|
227
|
-
return CARRIER_STATUS_RE.test(frontmatter(text));
|
|
228
|
-
}
|
|
229
|
-
|
|
230
224
|
/**
|
|
231
225
|
* The standing carry policy for one archived roadmap. See the module header for
|
|
232
226
|
* why this is not `deferralProblems`.
|
|
@@ -235,23 +229,22 @@ export function carryProblems(
|
|
|
235
229
|
root: string,
|
|
236
230
|
sourceSlug: string,
|
|
237
231
|
text: string,
|
|
238
|
-
): Array<{ cls:
|
|
232
|
+
): Array<{ cls: DeferralProblemClass; detail: string }> {
|
|
239
233
|
return carryProblemsFor(root, sourceSlug, parseDeferredItems(text));
|
|
240
234
|
}
|
|
241
235
|
|
|
242
236
|
/**
|
|
243
237
|
* The same policy over an ALREADY-PARSED item list.
|
|
244
238
|
*
|
|
245
|
-
* Split out so the walk parses each candidate once and
|
|
246
|
-
*
|
|
247
|
-
* needs, rather than parsing the same file twice per run.
|
|
239
|
+
* Split out so the walk parses each candidate once and hands the item list to
|
|
240
|
+
* this check, rather than parsing the same file twice per run.
|
|
248
241
|
*/
|
|
249
242
|
export function carryProblemsFor(
|
|
250
243
|
root: string,
|
|
251
244
|
sourceSlug: string,
|
|
252
245
|
items: readonly DeferredItem[],
|
|
253
|
-
): Array<{ cls:
|
|
254
|
-
const problems: Array<{ cls:
|
|
246
|
+
): Array<{ cls: DeferralProblemClass; detail: string }> {
|
|
247
|
+
const problems: Array<{ cls: DeferralProblemClass; detail: string }> = [];
|
|
255
248
|
for (const item of items) {
|
|
256
249
|
const label = item.text === '' ? '(unnamed step)' : item.text;
|
|
257
250
|
if (item.kind === null || item.destination === null) {
|
|
@@ -289,20 +282,6 @@ export function carryProblemsFor(
|
|
|
289
282
|
continue;
|
|
290
283
|
}
|
|
291
284
|
const destText = fs.readFileSync(found.file, 'utf-8');
|
|
292
|
-
// A carrier exists to stay live. Archiving one is the terminal-archival
|
|
293
|
-
// transition that has no vocabulary yet, and it strands every item the
|
|
294
|
-
// carrier itself holds — none of which is an OPEN step, so the check
|
|
295
|
-
// below would not see it.
|
|
296
|
-
if (found.dir === 'archive' && declaresCarrier(destText)) {
|
|
297
|
-
problems.push({
|
|
298
|
-
cls: 'broken-destination',
|
|
299
|
-
detail:
|
|
300
|
-
`destination \`${item.destination}\` is a \`status: carrier\` roadmap that has been ` +
|
|
301
|
-
'archived — a carrier holds obligations whose triggers are unmet, so archiving it ' +
|
|
302
|
-
'strands them. Terminal archival of a live carrier is not expressible yet and fails closed.',
|
|
303
|
-
});
|
|
304
|
-
continue;
|
|
305
|
-
}
|
|
306
285
|
if (found.dir === 'archive' && hasOpenStep(destText)) {
|
|
307
286
|
problems.push({
|
|
308
287
|
cls: 'broken-destination',
|
|
@@ -322,67 +301,20 @@ export function carryProblemsFor(
|
|
|
322
301
|
return problems;
|
|
323
302
|
}
|
|
324
303
|
|
|
325
|
-
/** The live locations a carrier may occupy — both are live per `_locate`. */
|
|
326
|
-
const LIVE_SUBDIRS: readonly string[] = ['', 'later'];
|
|
327
|
-
|
|
328
304
|
/**
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
* The enumeration the walk-from-the-archive direction cannot produce. Sorted,
|
|
332
|
-
* repo-relative, and `later/` is included because `_locate` treats a parked
|
|
333
|
-
* receiver as live — a carrier parked there is equally excluded from the active
|
|
334
|
-
* count, so it needs the same justification.
|
|
335
|
-
*/
|
|
336
|
-
export function liveCarriers(root: string): string[] {
|
|
337
|
-
const out: string[] = [];
|
|
338
|
-
for (const sub of LIVE_SUBDIRS) {
|
|
339
|
-
const abs = path.join(root, 'agents', 'roadmaps', sub);
|
|
340
|
-
let entries: string[];
|
|
341
|
-
try {
|
|
342
|
-
entries = fs.readdirSync(abs);
|
|
343
|
-
} catch {
|
|
344
|
-
continue;
|
|
345
|
-
}
|
|
346
|
-
for (const name of entries.sort()) {
|
|
347
|
-
if (!name.endsWith('.md') || EXCLUDE_NAMES.has(name)) {
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
const full = path.join(abs, name);
|
|
351
|
-
let text: string;
|
|
352
|
-
try {
|
|
353
|
-
if (!fs.statSync(full).isFile()) {
|
|
354
|
-
continue;
|
|
355
|
-
}
|
|
356
|
-
text = fs.readFileSync(full, 'utf-8');
|
|
357
|
-
} catch {
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
|
-
if (declaresCarrier(text)) {
|
|
361
|
-
out.push(path.posix.join('agents/roadmaps', sub, name));
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
return out;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Validate every carry annotation in the dead directories, then every live
|
|
370
|
-
* carrier against the destinations those annotations actually name.
|
|
305
|
+
* Validate every carry annotation in the dead directories.
|
|
371
306
|
*
|
|
372
307
|
* @param walked the dead-roadmap list, when the caller already has it. The walk
|
|
373
308
|
* `statSync`s every file in the archive, so taking it as a parameter is what
|
|
374
309
|
* stops the run doing that twice — once for the ledger's plan and once here.
|
|
375
|
-
* @returns the problems found
|
|
376
|
-
* live carriers were checked.
|
|
310
|
+
* @returns the problems found and how many dead roadmaps were read.
|
|
377
311
|
*/
|
|
378
312
|
export function auditCarries(
|
|
379
313
|
root: string,
|
|
380
314
|
walked: readonly string[] = deadRoadmaps(root),
|
|
381
|
-
): { problems:
|
|
382
|
-
const problems:
|
|
315
|
+
): { problems: DeferralProblem[]; scanned: number } {
|
|
316
|
+
const problems: DeferralProblem[] = [];
|
|
383
317
|
let scanned = 0;
|
|
384
|
-
/** Every slug some dead roadmap named with `carried-to=`. */
|
|
385
|
-
const carriedTo = new Set<string>();
|
|
386
318
|
for (const rel of walked) {
|
|
387
319
|
let text: string;
|
|
388
320
|
try {
|
|
@@ -395,39 +327,15 @@ export function auditCarries(
|
|
|
395
327
|
continue;
|
|
396
328
|
}
|
|
397
329
|
const items = parseDeferredItems(text);
|
|
398
|
-
for (const item of items) {
|
|
399
|
-
if (item.kind === 'carried-to' && item.destination !== null) {
|
|
400
|
-
carriedTo.add(item.destination);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
330
|
const sourceSlug = path.basename(rel).replace(/\.md$/, '');
|
|
404
331
|
for (const found of carryProblemsFor(root, sourceSlug, items)) {
|
|
405
332
|
problems.push({ source: rel, ...found });
|
|
406
333
|
}
|
|
407
334
|
}
|
|
408
|
-
|
|
409
|
-
for (const rel of carriers) {
|
|
410
|
-
const slug = path.basename(rel).replace(/\.md$/, '');
|
|
411
|
-
if (carriedTo.has(slug)) {
|
|
412
|
-
continue;
|
|
413
|
-
}
|
|
414
|
-
problems.push({
|
|
415
|
-
source: rel,
|
|
416
|
-
cls: 'broken-destination',
|
|
417
|
-
detail:
|
|
418
|
-
'declares `status: carrier`, but no roadmap under `agents/roadmaps/archive/` or ' +
|
|
419
|
-
'`agents/roadmaps/skipped/` names it with a `<!-- deferred-resolution: ' +
|
|
420
|
-
`carried-to=${slug} -->\` annotation. That status is only legitimate for the ` +
|
|
421
|
-
'RECEIVER of a real carry: it buys exclusion from the active roadmap count, from ' +
|
|
422
|
-
'`check_roadmap_trackable` and from the plan risk register, so a file no archived ' +
|
|
423
|
-
'parent carried anything to would take all three exclusions for nothing. Either ' +
|
|
424
|
-
'record the carry in the parent that deferred the work, or drop `status: carrier`.',
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
return { problems, scanned, carriers };
|
|
335
|
+
return { problems, scanned };
|
|
428
336
|
}
|
|
429
337
|
|
|
430
|
-
const USAGE = `usage:
|
|
338
|
+
const USAGE = `usage: lint_deferral_integrity [--root <dir>] [--self-test] [--quiet]
|
|
431
339
|
|
|
432
340
|
--root <dir> audit another checkout (default: this repository)
|
|
433
341
|
--self-test run the built-in discrimination suite and exit
|
|
@@ -454,19 +362,15 @@ Body.
|
|
|
454
362
|
}
|
|
455
363
|
|
|
456
364
|
/**
|
|
457
|
-
* A body that DOCUMENTS roadmap syntax inside a fence
|
|
458
|
-
* used to match
|
|
365
|
+
* A body that DOCUMENTS roadmap syntax inside a fence rather than declaring it.
|
|
366
|
+
* `hasOpenStep` used to match the `- [ ]` line inside it and red an archived
|
|
367
|
+
* receiver that had completed its work.
|
|
459
368
|
*/
|
|
460
|
-
const FENCED_SYNTAX_DOC = 'Syntax this file documents rather than declares:\n\n```markdown\n---\nstatus:
|
|
369
|
+
const FENCED_SYNTAX_DOC = 'Syntax this file documents rather than declares:\n\n```markdown\n---\nstatus: draft\n---\n- [ ] **1.1 An example open step.**\n```\n';
|
|
461
370
|
|
|
462
371
|
/** A back-link that exists only in the body — a mention, never a declaration. */
|
|
463
372
|
const FENCED_BODY_BACKLINK = 'What a back-link looks like, quoted:\n\n```yaml\nparent_roadmap: road-to-parent\nrelates:\n - slug: road-to-parent\n relation: extends\n```\n';
|
|
464
373
|
|
|
465
|
-
/** A receiver that declares itself a carrier — used only where that is the point. */
|
|
466
|
-
function _carrierReceiver(backlink: string): string {
|
|
467
|
-
return _receiver(backlink).replace('---\n# Receiver', 'status: carrier\n---\n# Receiver');
|
|
468
|
-
}
|
|
469
|
-
|
|
470
374
|
/**
|
|
471
375
|
* Each case builds a throwaway tree, runs the real CLI against it with
|
|
472
376
|
* `--root`, and asserts the exit code. Shelling out rather than calling
|
|
@@ -488,7 +392,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
488
392
|
plant(dir);
|
|
489
393
|
return runGateCli(
|
|
490
394
|
root,
|
|
491
|
-
'src/scripts/
|
|
395
|
+
'src/scripts/lint_deferral_integrity.ts',
|
|
492
396
|
['--root', dir, '--quiet'],
|
|
493
397
|
root,
|
|
494
398
|
);
|
|
@@ -503,7 +407,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
503
407
|
{
|
|
504
408
|
name: 'a live receiver with a back-link is accepted',
|
|
505
409
|
expect: 'accept',
|
|
506
|
-
run: build('
|
|
410
|
+
run: build('deferral-ok-', (dir) => {
|
|
507
411
|
parent(dir);
|
|
508
412
|
_writeFixture(
|
|
509
413
|
dir,
|
|
@@ -515,7 +419,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
515
419
|
{
|
|
516
420
|
name: 'a receiver parked under later/ is equally live and is accepted',
|
|
517
421
|
expect: 'accept',
|
|
518
|
-
run: build('
|
|
422
|
+
run: build('deferral-later-', (dir) => {
|
|
519
423
|
parent(dir);
|
|
520
424
|
_writeFixture(
|
|
521
425
|
dir,
|
|
@@ -527,7 +431,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
527
431
|
{
|
|
528
432
|
name: 'a receiver that completed its work and archived is accepted — fulfilment, not loss',
|
|
529
433
|
expect: 'accept',
|
|
530
|
-
run: build('
|
|
434
|
+
run: build('deferral-done-', (dir) => {
|
|
531
435
|
parent(dir);
|
|
532
436
|
_writeFixture(
|
|
533
437
|
dir,
|
|
@@ -539,7 +443,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
539
443
|
{
|
|
540
444
|
name: 'a back-link supplied as a relates: row instead of parent_roadmap is accepted',
|
|
541
445
|
expect: 'accept',
|
|
542
|
-
run: build('
|
|
446
|
+
run: build('deferral-relates-', (dir) => {
|
|
543
447
|
parent(dir);
|
|
544
448
|
_writeFixture(
|
|
545
449
|
dir,
|
|
@@ -548,22 +452,10 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
548
452
|
);
|
|
549
453
|
}),
|
|
550
454
|
},
|
|
551
|
-
{
|
|
552
|
-
name: 'a live carrier an archived parent actually names is accepted',
|
|
553
|
-
expect: 'accept',
|
|
554
|
-
run: build('carrier-named-', (dir) => {
|
|
555
|
-
parent(dir);
|
|
556
|
-
_writeFixture(
|
|
557
|
-
dir,
|
|
558
|
-
'agents/roadmaps/road-to-receiver.md',
|
|
559
|
-
_carrierReceiver('parent_roadmap: road-to-parent'),
|
|
560
|
-
);
|
|
561
|
-
}),
|
|
562
|
-
},
|
|
563
455
|
{
|
|
564
456
|
name: 'an archived receiver that only documents roadmap syntax in a fence is accepted',
|
|
565
457
|
expect: 'accept',
|
|
566
|
-
run: build('
|
|
458
|
+
run: build('deferral-fenced-', (dir) => {
|
|
567
459
|
parent(dir);
|
|
568
460
|
_writeFixture(
|
|
569
461
|
dir,
|
|
@@ -572,44 +464,10 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
572
464
|
);
|
|
573
465
|
}),
|
|
574
466
|
},
|
|
575
|
-
{
|
|
576
|
-
name: 'a live carrier no archived parent names is rejected — the status is not free',
|
|
577
|
-
expect: 'reject',
|
|
578
|
-
run: build('carrier-orphan-', (dir) => {
|
|
579
|
-
parent(dir);
|
|
580
|
-
_writeFixture(
|
|
581
|
-
dir,
|
|
582
|
-
'agents/roadmaps/road-to-receiver.md',
|
|
583
|
-
_receiver('parent_roadmap: road-to-parent'),
|
|
584
|
-
);
|
|
585
|
-
_writeFixture(
|
|
586
|
-
dir,
|
|
587
|
-
'agents/roadmaps/road-to-self-declared.md',
|
|
588
|
-
_carrierReceiver('owner: maintainer'),
|
|
589
|
-
);
|
|
590
|
-
}),
|
|
591
|
-
},
|
|
592
|
-
{
|
|
593
|
-
name: 'an orphan carrier parked under later/ is rejected too',
|
|
594
|
-
expect: 'reject',
|
|
595
|
-
run: build('carrier-orphan-later-', (dir) => {
|
|
596
|
-
parent(dir);
|
|
597
|
-
_writeFixture(
|
|
598
|
-
dir,
|
|
599
|
-
'agents/roadmaps/road-to-receiver.md',
|
|
600
|
-
_receiver('parent_roadmap: road-to-parent'),
|
|
601
|
-
);
|
|
602
|
-
_writeFixture(
|
|
603
|
-
dir,
|
|
604
|
-
'agents/roadmaps/later/road-to-self-declared.md',
|
|
605
|
-
_carrierReceiver('owner: maintainer'),
|
|
606
|
-
);
|
|
607
|
-
}),
|
|
608
|
-
},
|
|
609
467
|
{
|
|
610
468
|
name: 'an archived receiver with an asterisk-bulleted OPEN step is rejected',
|
|
611
469
|
expect: 'reject',
|
|
612
|
-
run: build('
|
|
470
|
+
run: build('deferral-asterisk-', (dir) => {
|
|
613
471
|
parent(dir);
|
|
614
472
|
_writeFixture(
|
|
615
473
|
dir,
|
|
@@ -621,7 +479,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
621
479
|
{
|
|
622
480
|
name: 'a back-link that only appears in the body, not the frontmatter, is rejected',
|
|
623
481
|
expect: 'reject',
|
|
624
|
-
run: build('
|
|
482
|
+
run: build('deferral-bodylink-', (dir) => {
|
|
625
483
|
parent(dir);
|
|
626
484
|
_writeFixture(
|
|
627
485
|
dir,
|
|
@@ -630,27 +488,15 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
630
488
|
);
|
|
631
489
|
}),
|
|
632
490
|
},
|
|
633
|
-
{
|
|
634
|
-
name: 'a receiver that is itself a carrier and has been archived is rejected',
|
|
635
|
-
expect: 'reject',
|
|
636
|
-
run: build('carrier-archived-carrier-', (dir) => {
|
|
637
|
-
parent(dir);
|
|
638
|
-
_writeFixture(
|
|
639
|
-
dir,
|
|
640
|
-
'agents/roadmaps/archive/road-to-receiver.md',
|
|
641
|
-
_carrierReceiver('parent_roadmap: road-to-parent'),
|
|
642
|
-
);
|
|
643
|
-
}),
|
|
644
|
-
},
|
|
645
491
|
{
|
|
646
492
|
name: 'the receiver deleted entirely is rejected',
|
|
647
493
|
expect: 'reject',
|
|
648
|
-
run: build('
|
|
494
|
+
run: build('deferral-deleted-', parent),
|
|
649
495
|
},
|
|
650
496
|
{
|
|
651
497
|
name: 'a receiver archived while still carrying OPEN steps is rejected — stranded, not completed',
|
|
652
498
|
expect: 'reject',
|
|
653
|
-
run: build('
|
|
499
|
+
run: build('deferral-stranded-', (dir) => {
|
|
654
500
|
parent(dir);
|
|
655
501
|
_writeFixture(
|
|
656
502
|
dir,
|
|
@@ -662,7 +508,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
662
508
|
{
|
|
663
509
|
name: 'a receiver under skipped/ is rejected — skipping is not fulfilling',
|
|
664
510
|
expect: 'reject',
|
|
665
|
-
run: build('
|
|
511
|
+
run: build('deferral-skipped-', (dir) => {
|
|
666
512
|
parent(dir);
|
|
667
513
|
_writeFixture(
|
|
668
514
|
dir,
|
|
@@ -674,7 +520,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
674
520
|
{
|
|
675
521
|
name: 'the back-link removed is rejected — the link must verify from both ends',
|
|
676
522
|
expect: 'reject',
|
|
677
|
-
run: build('
|
|
523
|
+
run: build('deferral-nolink-', (dir) => {
|
|
678
524
|
parent(dir);
|
|
679
525
|
_writeFixture(
|
|
680
526
|
dir,
|
|
@@ -686,7 +532,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
686
532
|
{
|
|
687
533
|
name: 'a back-link naming a different parent is rejected',
|
|
688
534
|
expect: 'reject',
|
|
689
|
-
run: build('
|
|
535
|
+
run: build('deferral-reparent-', (dir) => {
|
|
690
536
|
parent(dir);
|
|
691
537
|
_writeFixture(
|
|
692
538
|
dir,
|
|
@@ -711,34 +557,36 @@ export function main(argv: string[]): number {
|
|
|
711
557
|
} else if (arg === '--root') {
|
|
712
558
|
const next = argv[i + 1];
|
|
713
559
|
if (next === undefined) {
|
|
714
|
-
process.stderr.write('
|
|
560
|
+
process.stderr.write('lint_deferral_integrity: --root needs a directory\n');
|
|
715
561
|
return 2;
|
|
716
562
|
}
|
|
717
563
|
root = path.resolve(next);
|
|
718
564
|
i += 1;
|
|
719
565
|
} else if (arg === '--self-test') {
|
|
720
566
|
return runSelfTest({
|
|
721
|
-
gate: '
|
|
567
|
+
gate: 'lint_deferral_integrity',
|
|
722
568
|
cases: selfTestCases(_DEFAULT_ROOT),
|
|
723
|
-
|
|
724
|
-
|
|
569
|
+
// 16/10 before ADR-262 deleted `status: carrier`, which took one
|
|
570
|
+
// accept case (a named live carrier) and three reject cases (two
|
|
571
|
+
// orphan carriers, one archived carrier destination) with it.
|
|
572
|
+
minCases: 12,
|
|
573
|
+
minRejectCases: 7,
|
|
725
574
|
});
|
|
726
575
|
} else {
|
|
727
|
-
process.stderr.write(`
|
|
576
|
+
process.stderr.write(`lint_deferral_integrity: unknown argument ${JSON.stringify(arg)}\n`);
|
|
728
577
|
process.stderr.write(USAGE);
|
|
729
578
|
return 2;
|
|
730
579
|
}
|
|
731
580
|
}
|
|
732
581
|
|
|
733
|
-
const ledger = new GateLedger('
|
|
582
|
+
const ledger = new GateLedger('lint_deferral_integrity');
|
|
734
583
|
const walked = deadRoadmaps(root);
|
|
735
|
-
// Walked ONCE and handed to the audit
|
|
736
|
-
//
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
ledger.plan([...walked, ...carriers]);
|
|
584
|
+
// Walked ONCE and handed to the audit, so the run does not readdir and
|
|
585
|
+
// `statSync` every archived file twice for one reading.
|
|
586
|
+
const { problems, scanned } = auditCarries(root, walked);
|
|
587
|
+
ledger.plan([...walked]);
|
|
740
588
|
const failed = new Set(problems.map((p) => p.source));
|
|
741
|
-
for (const rel of
|
|
589
|
+
for (const rel of walked) {
|
|
742
590
|
if (failed.has(rel)) {
|
|
743
591
|
ledger.fail(rel, problems.find((p) => p.source === rel)?.detail ?? 'broken carry');
|
|
744
592
|
} else {
|
|
@@ -747,7 +595,7 @@ export function main(argv: string[]): number {
|
|
|
747
595
|
}
|
|
748
596
|
try {
|
|
749
597
|
reportScanned({
|
|
750
|
-
gate: '
|
|
598
|
+
gate: 'lint_deferral_integrity',
|
|
751
599
|
scanned,
|
|
752
600
|
units: 'archived roadmap(s)',
|
|
753
601
|
roots: DEAD_DIRS.map((d) => `agents/roadmaps/${d}`),
|
|
@@ -779,16 +627,14 @@ export function main(argv: string[]): number {
|
|
|
779
627
|
process.stderr.write(
|
|
780
628
|
'\n Restore the receiver, restore its `parent_roadmap:` back-link, or move the\n' +
|
|
781
629
|
' carried items back to open in the archived parent. A rename or a re-parent is\n' +
|
|
782
|
-
' not expressible yet and fails closed on purpose.\n'
|
|
783
|
-
' A file listed for declaring `status: carrier` needs neither: record the carry\n' +
|
|
784
|
-
' in the archived parent, or drop the status and let the file be ordinary estate.\n',
|
|
630
|
+
' not expressible yet and fails closed on purpose.\n',
|
|
785
631
|
);
|
|
786
632
|
ledger.report();
|
|
787
633
|
return 1;
|
|
788
634
|
}
|
|
789
635
|
|
|
790
636
|
const verdict = checkRatchet({
|
|
791
|
-
gate: '
|
|
637
|
+
gate: 'lint_deferral_integrity',
|
|
792
638
|
actual: unannotated.length,
|
|
793
639
|
repoRoot: _DEFAULT_ROOT,
|
|
794
640
|
});
|
|
@@ -807,8 +653,8 @@ export function main(argv: string[]): number {
|
|
|
807
653
|
if (!quiet) {
|
|
808
654
|
process.stdout.write(`✅ ${verdict.message}\n`);
|
|
809
655
|
process.stdout.write(
|
|
810
|
-
`✅
|
|
811
|
-
|
|
656
|
+
`✅ lint_deferral_integrity: ${String(scanned)} dead roadmap(s) scanned, ` +
|
|
657
|
+
'every annotated carry resolves.\n',
|
|
812
658
|
);
|
|
813
659
|
}
|
|
814
660
|
ledger.report();
|