@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.
Files changed (235) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +164 -187
  5. package/CREDITS.md +37 -0
  6. package/README.md +1 -1
  7. package/dist/agent-src/commands/analyze/inbox.md +380 -5
  8. package/dist/agent-src/commands/analyze/repo.md +4 -2
  9. package/dist/agent-src/commands/pr/merge.md +47 -38
  10. package/dist/agent-src/commands/roadmap/next.md +8 -0
  11. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +69 -0
  12. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -5
  13. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +12 -0
  14. package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
  15. package/dist/agent-src/rules/notes-first-reasoning.md +36 -25
  16. package/dist/agent-src/rules/token-efficiency.md +26 -3
  17. package/dist/agent-src/scripts/_cli_entry.ts +51 -0
  18. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +120 -27
  19. package/dist/agent-src/scripts/roadmap_gates.ts +4 -20
  20. package/dist/agent-src/scripts/roadmap_set_step.ts +5 -2
  21. package/dist/agent-src/scripts/stubs_due.ts +4 -18
  22. package/dist/agent-src/scripts/update_roadmap_progress.ts +7 -24
  23. package/dist/agent-src/skills/code-intelligence/SKILL.md +82 -1
  24. package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +9 -0
  25. package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
  26. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +52 -2
  27. package/dist/agent-src/templates/agent-settings.md +0 -1
  28. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  29. package/dist/cli/registry.js +1 -1
  30. package/dist/cli-delegate/_cli_entry.js +8 -0
  31. package/dist/cli-delegate/_lib/pin_resolver.js +170 -0
  32. package/dist/cli-delegate/archival_sweep.js +8 -0
  33. package/dist/cli-delegate/archive_completed_roadmaps.js +542 -0
  34. package/dist/cli-delegate/check_update_banner.js +923 -0
  35. package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-2KKXJW25.js} +23 -9
  36. package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
  37. package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
  38. package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
  39. package/dist/cli-delegate/{chunk-4FS4WRXP.js → chunk-IAI3XL43.js} +15 -3
  40. package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
  41. package/dist/cli-delegate/{chunk-643Z7FIC.js → chunk-KCNTUSPI.js} +7 -1
  42. package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
  43. package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
  44. package/dist/cli-delegate/{chunk-YS6XTLEK.js → chunk-TYSY33MD.js} +56 -6
  45. package/dist/cli-delegate/chunk-ULET4BYS.js +328 -0
  46. package/dist/cli-delegate/chunk-YDQSHTD7.js +187 -0
  47. package/dist/cli-delegate/chunk-YWVXLR44.js +48 -0
  48. package/dist/cli-delegate/chunk-ZZQBUHFK.js +273 -0
  49. package/dist/cli-delegate/cmd_conformance.js +2 -2
  50. package/dist/cli-delegate/cmd_export.js +2 -2
  51. package/dist/cli-delegate/cmd_handoff.js +45 -13
  52. package/dist/cli-delegate/cmd_prune.js +2 -2
  53. package/dist/cli-delegate/cmd_session_recycle.js +4 -14
  54. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  55. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  56. package/dist/cli-delegate/cmd_sync.js +2 -2
  57. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  58. package/dist/cli-delegate/cmd_validate.js +2 -2
  59. package/dist/cli-delegate/dashboard_mode.js +16 -0
  60. package/dist/cli-delegate/gate_budget.js +24 -0
  61. package/dist/cli-delegate/gate_execute.js +25 -0
  62. package/dist/cli-delegate/guarded_baseline.js +18 -0
  63. package/dist/cli-delegate/resume_probe.js +22 -0
  64. package/dist/cli-delegate/roadmap_gates.js +638 -0
  65. package/dist/cli-delegate/roadmap_set_step.js +173 -0
  66. package/dist/cli-delegate/stub_queue.js +47 -0
  67. package/dist/cli-delegate/stubs_due.js +38 -0
  68. package/dist/cli-delegate/ticket_bundles.js +59 -0
  69. package/dist/cli-delegate/update_roadmap_progress.js +60 -0
  70. package/dist/discovery/deprecation-report.md +1 -1
  71. package/dist/discovery/discovery-manifest.json +13 -13
  72. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  73. package/dist/discovery/discovery-manifest.summary.md +1 -1
  74. package/dist/discovery/dormancy-report.md +1 -1
  75. package/dist/discovery/orphan-report.md +1 -1
  76. package/dist/discovery/packs.json +2 -2
  77. package/dist/discovery/trust-report.md +1 -1
  78. package/dist/discovery/workspaces.json +2 -2
  79. package/dist/hooks/dispatch.js +1813 -946
  80. package/dist/install/claudeLayerCarriage.js +237 -0
  81. package/dist/install/claudePathsPlan.js +79 -0
  82. package/dist/install/hostLayerFingerprint.js +1 -1
  83. package/dist/install/install.mjs +21 -3
  84. package/dist/install/partitionEligibility.js +231 -117
  85. package/dist/install/ruleLayerPartition.js +10 -7
  86. package/dist/mcp/registry-manifest.json +2 -2
  87. package/dist/mcp/server.json +2 -2
  88. package/dist/mcp/server.mjs +10870 -7641
  89. package/dist/scripts/_lib/lean_projection_mode.js +108 -0
  90. package/dist/scripts/_lib/router_match.js +243 -0
  91. package/dist/scripts/_lib/rule_injection.js +216 -0
  92. package/dist/scripts/condense.js +52 -45
  93. package/dist/scripts/project_thin_rules.js +139 -19
  94. package/dist/server/schemas/settings.js +16 -3
  95. package/docs/MIGRATION.md +105 -1
  96. package/docs/contracts/settings-classes.md +40 -8
  97. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
  98. package/docs/guidelines/agent-infra/notes-horizon-mechanics.md +90 -0
  99. package/llms.txt +1 -1
  100. package/package.json +7 -4
  101. package/src/agent-src/scripts/_cli_entry.ts +51 -0
  102. package/src/agent-src/scripts/archive_completed_roadmaps.ts +120 -27
  103. package/src/agent-src/scripts/roadmap_gates.ts +4 -20
  104. package/src/agent-src/scripts/roadmap_set_step.ts +5 -2
  105. package/src/agent-src/scripts/stubs_due.ts +4 -18
  106. package/src/agent-src/scripts/update_roadmap_progress.ts +7 -24
  107. package/src/config/agent-settings.template.yml +47 -1
  108. package/src/config/continuity-surface.json +516 -0
  109. package/src/config/estate-count-budget.json +1 -1
  110. package/src/config/evaluator-budgets.json +4 -4
  111. package/src/config/gate-coverage.yml +29 -8
  112. package/src/config/gate-violation-baselines.json +8 -8
  113. package/src/config/gitignore-block.txt +19 -0
  114. package/src/config/hook-token-budget.json +7 -6
  115. package/src/config/host-injection-effect.json +73 -0
  116. package/src/config/packed-binary-manifest.json +33 -0
  117. package/src/config/preamble-payload-budget.json +112 -105
  118. package/src/config/publish-surface.json +4 -2
  119. package/src/config/routing-coverage-seed.json +9 -2
  120. package/src/install/claudeLayerCarriage.ts +273 -0
  121. package/src/install/claudePathsPlan.ts +76 -0
  122. package/src/install/hostLayerFingerprint.ts +1 -1
  123. package/src/install/partitionEligibility.ts +261 -127
  124. package/src/install/ruleLayerPartition.ts +19 -23
  125. package/src/scripts/_cli/cmd_session_recycle.ts +2 -25
  126. package/src/scripts/_cli/handoff_sessions.ts +14 -44
  127. package/src/scripts/_dispatch.bash +77 -27
  128. package/src/scripts/_lib/auto_dispatch.ts +2 -2
  129. package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
  130. package/src/scripts/_lib/continuity_slot.ts +295 -0
  131. package/src/scripts/_lib/continuity_surface.ts +296 -0
  132. package/src/scripts/_lib/continuity_writer.ts +253 -0
  133. package/src/scripts/_lib/corpus_manifest.ts +26 -9
  134. package/src/scripts/_lib/hook_settings.ts +86 -1
  135. package/src/scripts/_lib/injection_effect.ts +151 -0
  136. package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
  137. package/src/scripts/_lib/lean_projection_mode.ts +127 -0
  138. package/src/scripts/_lib/packed_binary_predicate.ts +262 -0
  139. package/src/scripts/_lib/pin_resolver.ts +13 -0
  140. package/src/scripts/_lib/recycle_envelope_paths.ts +109 -21
  141. package/src/scripts/_lib/regression_neighbourhood.ts +159 -22
  142. package/src/scripts/_lib/retired_status.ts +74 -0
  143. package/src/scripts/_lib/session_eol.ts +44 -0
  144. package/src/scripts/_lib/session_index_trust.ts +346 -0
  145. package/src/scripts/_lib/skill_catalogue.ts +156 -21
  146. package/src/scripts/_lib/standing_bound_ratchet.ts +154 -0
  147. package/src/scripts/_lib/subagent_capsule.ts +117 -8
  148. package/src/scripts/_lib/vendored_grammar_upstream.ts +280 -0
  149. package/src/scripts/ai_council/api_transport.ts +5 -0
  150. package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
  151. package/src/scripts/ai_council/prompts.ts +9 -0
  152. package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
  153. package/src/scripts/audit_mcp_tools.ts +69 -25
  154. package/src/scripts/build_mcp_catalog.ts +24 -1
  155. package/src/scripts/capture_skill_catalogue.ts +24 -11
  156. package/src/scripts/check_candidate_lines.ts +499 -0
  157. package/src/scripts/check_continuity_surface.ts +475 -0
  158. package/src/scripts/check_dependency_floors.ts +13 -6
  159. package/src/scripts/check_estate_count.ts +40 -79
  160. package/src/scripts/check_host_tree_parity.ts +291 -0
  161. package/src/scripts/check_pack_size.ts +262 -23
  162. package/src/scripts/check_preamble_payload_budget.ts +136 -7
  163. package/src/scripts/check_roadmap_trackable.ts +19 -16
  164. package/src/scripts/check_rule_layer_partition.ts +54 -34
  165. package/src/scripts/check_rule_projection_integrity.ts +102 -2
  166. package/src/scripts/check_single_delivery.ts +97 -31
  167. package/src/scripts/check_update_banner.ts +13 -0
  168. package/src/scripts/ci_settle.ts +205 -5
  169. package/src/scripts/code_graph/build.ts +217 -15
  170. package/src/scripts/code_graph/cli.ts +334 -15
  171. package/src/scripts/code_graph/detect.ts +38 -0
  172. package/src/scripts/code_graph/extract.ts +11 -0
  173. package/src/scripts/code_graph/grammar_probe.ts +43 -0
  174. package/src/scripts/code_graph/loader.ts +88 -14
  175. package/src/scripts/code_graph/query.ts +144 -11
  176. package/src/scripts/code_graph/resolution_tiers.ts +238 -0
  177. package/src/scripts/code_graph/sqlite_store.ts +262 -6
  178. package/src/scripts/code_graph/types.ts +117 -3
  179. package/src/scripts/code_graph/validate.ts +26 -1
  180. package/src/scripts/code_graph/verbs.ts +590 -0
  181. package/src/scripts/condense.ts +54 -42
  182. package/src/scripts/corpus_manifest.ts +5 -5
  183. package/src/scripts/council_cli.ts +11 -12
  184. package/src/scripts/count_intent_disagreement.ts +426 -0
  185. package/src/scripts/handoff_context_hook.ts +118 -1
  186. package/src/scripts/hook_effect_doctor.ts +82 -1
  187. package/src/scripts/hook_manifest.json +1 -1
  188. package/src/scripts/hook_manifest.yaml +87 -12
  189. package/src/scripts/hooks/block_speaking_inbox_dir.ts +182 -16
  190. package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
  191. package/src/scripts/hooks/code_graph_context_hook.ts +201 -0
  192. package/src/scripts/hooks/concern_registry.ts +4 -2
  193. package/src/scripts/hooks/host_semantics.ts +1 -1
  194. package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
  195. package/src/scripts/hooks/push_settle_hook.ts +3 -2
  196. package/src/scripts/hooks/rule_inject_hook.ts +108 -21
  197. package/src/scripts/hooks/session_eol_hook.ts +120 -2
  198. package/src/scripts/hooks/skill_route_hook.ts +11 -4
  199. package/src/scripts/hot_context_hook.ts +62 -7
  200. package/src/scripts/inbox_source_census.ts +322 -0
  201. package/src/scripts/install-hooks.sh +82 -0
  202. package/src/scripts/{lint_carrier_integrity.ts → lint_deferral_integrity.ts} +60 -214
  203. package/src/scripts/lint_plan_risk_register.ts +13 -17
  204. package/src/scripts/lint_roadmap_blockers.ts +164 -1
  205. package/src/scripts/mcp_server/consumer_tool_catalog.json +152 -2
  206. package/src/scripts/mcp_server/graph_tools.ts +294 -0
  207. package/src/scripts/mcp_server/path_util.ts +31 -0
  208. package/src/scripts/mcp_server/suggest_skill.ts +72 -0
  209. package/src/scripts/mcp_server/tool_catalog_source.ts +7 -1
  210. package/src/scripts/mcp_server/tools.ts +5 -69
  211. package/src/scripts/media/lib/adapter-contract.md +7 -2
  212. package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
  213. package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
  214. package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
  215. package/src/scripts/model_rule_injection.ts +61 -4
  216. package/src/scripts/project_thin_rules.ts +149 -16
  217. package/src/scripts/rdp_candidates_delta.ts +276 -0
  218. package/src/scripts/rdp_quality_eval.ts +217 -19
  219. package/src/scripts/release_mix_taxonomy.json +3 -2
  220. package/src/scripts/report_host_injection_effect.ts +189 -0
  221. package/src/scripts/report_layer_overlap.ts +41 -0
  222. package/src/scripts/report_skill_menu_census.ts +341 -0
  223. package/src/scripts/report_standing_payload_by_host.ts +650 -0
  224. package/src/scripts/schemas/agent-settings.schema.json +7 -1
  225. package/src/scripts/session_memory_index.ts +81 -8
  226. package/src/scripts/skill_tools/score_skill_relevance.ts +70 -6
  227. package/src/scripts/skill_tools/suggest_skill_for_task.ts +37 -3
  228. package/src/server/schemas/settings.ts +23 -4
  229. package/src/vendor/grammars/README.md +86 -0
  230. package/src/vendor/grammars/tree-sitter-javascript.wasm +0 -0
  231. package/src/vendor/grammars/tree-sitter-php.wasm +0 -0
  232. package/src/vendor/grammars/tree-sitter-typescript.wasm +0 -0
  233. package/src/scripts/_lib/carrier_status.ts +0 -118
  234. package/src/scripts/_lib/estate_carrier_cases.ts +0 -72
  235. package/src/scripts/hooks/code_graph_nudge_hook.ts +0 -191
@@ -155,3 +155,72 @@ could have shared. `probe_turnaround` reports the rate afterwards and refuses to
155
155
  gate on a store CI does not have. This paragraph is the whole mechanism, and the
156
156
  roadmap that added it pre-committed to recording a null if the rate does not
157
157
  move rather than repeating the reminder more loudly.
158
+
159
+ ## One command per Bash call — the detail
160
+
161
+ Migrated from `token-efficiency` § One command per Bash call on 2026-09-08,
162
+ because that rule's body is re-written on every subagent spawn while this file
163
+ is loaded on demand. The Iron Law stays in the rule; everything below is
164
+ lookup material.
165
+
166
+ ### Why chaining costs more, not less
167
+
168
+ The host splits a compound command on `&&`, `||`, `;`, `|`, `|&`, `&` and
169
+ newlines and requires **each segment to match the allowlist independently**.
170
+ A single unmatched segment sends the whole call down the permission path even
171
+ when every other segment was already allowed. So chaining does not save a
172
+ round-trip — it converts N cheap authorizations into one expensive one.
173
+
174
+ ### The measurement
175
+
176
+ Over 40,268 real Bash calls from one maintainer's transcripts:
177
+
178
+ - **97.7 %** carry a shell metacharacter. The package's own category-A
179
+ classifier (`permission_gate` + `category_a.ts`) is therefore reachable on
180
+ **2.3 %** of real calls; the host allowlist decides the rest.
181
+ - **17.9 %** have a head token matching no allowlist pattern. Distribution:
182
+ a leading `VAR=…` assignment 5,215×, `./scripts-run` 520×, `python3` 518×,
183
+ shell loop keywords 460×, `sleep` 155×, `mkdir` 71×, `cp` 49×.
184
+ - Of those 5,215 assignments, **5,037 are genuinely standalone** (`F=/path; …`)
185
+ and only 178 are env prefixes (`VAR=v cmd`). The distinction was checked
186
+ rather than assumed, because a review had claimed the whole class was a
187
+ measurement artifact. It is not: that shape cannot be written as an
188
+ allowlist pattern at all, so it shrinks only by not being written.
189
+
190
+ ### The substitutions
191
+
192
+ | Instead of | Write |
193
+ |---|---|
194
+ | `D=/repo; cd $D && git status` | `git -C /repo status` |
195
+ | `V=$(git rev-parse HEAD); echo $V` | two calls, the second using the printed value |
196
+ | `mkdir -p x && cp a x/` | two calls in the same block |
197
+ | `cd sub && <cmd>` | the directory flag the tool already has (`-C`, `--cwd`, `--prefix`) |
198
+
199
+ A loop that genuinely cannot be expressed without the shell stays a loop —
200
+ prefer a script file over an inline `for` when it recurs.
201
+
202
+ ### What the carrier does and does not do
203
+
204
+ `chain-nudge` (`src/scripts/hooks/chain_nudge_hook.ts`) is a `pre_tool_use`
205
+ concern bound on the three hook-capable platforms. It reads
206
+ `tool_input.command`, strips quoted spans and heredoc bodies so an operator
207
+ inside a string literal cannot be misread, and injects one advisory line the
208
+ first time a session chains work. It is ON by default with
209
+ `hooks.chain_nudge.enabled: false` as the opt-out — inverting
210
+ `code-graph-nudge`, which gates on a project capability that may be absent,
211
+ because the fact this one carries is a property of the host's permission
212
+ matcher and holds in every consumer.
213
+
214
+ **It never blocks, and it fires once.** So it changes what the agent knows,
215
+ not what the agent may do: compliance stays model-carried, and the concern's
216
+ tests establish detector behavior, not adherence. On a host with no
217
+ `pre_tool_use` slot nothing fires at all and the rule is model-carried end to
218
+ end — `agent-config hooks:status` reports which slots are bound where you are.
219
+
220
+ ### The sentence this replaced
221
+
222
+ Until 2026-09-08 the batching paragraph in the rule ended "NOT write shorter
223
+ commands: the long commands are already the batching." That was written about
224
+ token cost, where it holds, and it is wrong about the permission layer below —
225
+ a batch is N tool calls in ONE block, never N shell commands in ONE call. The
226
+ old wording collapsed the two and so encouraged the most expensive shape.
@@ -232,11 +232,11 @@ NEVER A SILENTLY DEGRADED ANSWER. NO LLM CLASSIFIER FALLBACK (CUT C3).
232
232
  **Why rg-first** (council 2026-07-28, 2 rounds): the pre-registered benchmark
233
233
  behind the `code-graph-retrieval-null` claim (`docs/CLAIMS.md`) measured
234
234
  native-graph recall 0.365 vs grep 0.797 on graph-shaped questions — an
235
- indexing gap that keeps `code_graph.enabled` false BY DEFAULT — not
236
- permanently: the 2026-08-15 withdrawal retracted that, and the figures predate
237
- the 2026-08-22 extractor repair. The accelerant clause above is that bound's
238
- escape hatch; `classifyLookup` reads the flag, so turning it on is the whole
239
- change.
235
+ indexing gap that kept `code_graph.enabled` false BY DEFAULT until that flag
236
+ was retired (2026-09-07) — not permanently: the 2026-08-15 withdrawal retracted
237
+ that, and the figures predate the 2026-08-22 extractor repair. The accelerant
238
+ clause above is that bound's escape hatch, reached only when a caller passes a
239
+ usable graph, which no production caller yet does.
240
240
 
241
241
  **Escalation, not degradation:** a primitive that returns nothing (index miss,
242
242
  pattern too ambiguous, report script absent) — **or an unusable result**
@@ -193,6 +193,18 @@ decision. Engage per [`rdp-gate`](rdp-gate.md).
193
193
  The suite states three retry numbers; they are **stages of one escalation
194
194
  ladder on the same failing target**, not competing budgets:
195
195
 
196
+ 0. **Attempt 1 fails → read the candidate list, if one exists, BEFORE retry 1.**
197
+ Where the run recorded a candidate set for this work
198
+ ([`notes-first-reasoning`](../../rules/notes-first-reasoning.md)
199
+ § Reopening), the first failure reads it first: a failure is evidence about
200
+ the CHOSEN FORM, and retrying the same approach spends the budget without
201
+ ever reaching the second-best candidate — which is how the mechanism that
202
+ discovers a form was wrong never routes back to the form. Where no candidate
203
+ list exists the ladder is unchanged and stage 1 runs immediately.
204
+ **This adds a branch; it does not move the budget.** Reading the list is not
205
+ an attempt and is not counted, and the reopen is capped at one per candidate:
206
+ the second contradiction on the same candidate continues down this ladder
207
+ rather than reopening again.
196
208
  1. **Attempt 1 fails → up to 2 retries of the same approach**
197
209
  ([`think-before-action`](../../rules/think-before-action.md) "MAX 2
198
210
  RETRIES PER APPROACH") — the soft, agent-side stage: rethink between
@@ -25,11 +25,10 @@ obligation_frequency: "per-turn"
25
25
 
26
26
  Some repos commit a pre-built code-intelligence index — a `graph.json`-shaped
27
27
  artifact or a SCIP index (`index.scip`, `*.scip`). One that exists is already
28
- built and structured, so it is the cheap first question for "who calls X",
29
- "where is Y used", "what does this import". This suite is an **orchestrator
30
- first, owner where it wins** (ADR-124): query a consumer-shipped index when it
31
- is present and fresh; where none is shipped, the native engine (default-off,
32
- benchmark-gated) covers the gap.
28
+ built, so it is the cheap first question for "who calls X", "where is Y used",
29
+ "what does this import". This suite is an **orchestrator first, owner where it
30
+ wins** (ADR-124): query a shipped index when present and fresh; otherwise the
31
+ native engine covers the gap (shipped since ADR-259 — no manual install).
33
32
 
34
33
  ## The rule
35
34
 
@@ -41,6 +40,17 @@ NO FRESH INDEX SHIPPED → THE NATIVE ENGINE MAY BUILD ONE (ADR-124); STILL
41
40
  GREP-FALLBACK FOR WHAT THE GRAPH DOES NOT ANSWER, AND SAY WHICH SOURCE ANSWERED.
42
41
  ```
43
42
 
43
+ ## Staleness is part of the answer
44
+
45
+ ```
46
+ AN ANSWER FROM AN INDEX N COMMITS BEHIND IS WORTH LESS THAN THE SAME ANSWER
47
+ FROM A FRESH ONE. NEVER REPORT A GRAPH ANSWER WITHOUT KNOWING WHICH IT WAS.
48
+ ```
49
+
50
+ `behind:N` → refresh first, or grep and say so. Silence is not freshness.
51
+ Delivery per host: [`code-intelligence`](../skills/code-intelligence/SKILL.md)
52
+ § Staleness delivery.
53
+
44
54
  ## When it fires
45
55
 
46
56
  A codebase-structure question (call graph, references, usages, import graph)
@@ -58,9 +68,8 @@ AND the repo contains a detectable index:
58
68
  3. **Fall back to `grep`/read** only for what the index does not cover, and say
59
69
  so ("the index has no entry for X, so I grepped").
60
70
 
61
- Query-first is an **ordering** heuristic, never a claim the index answers better:
62
- measured 2026-08-28, zero of four graph-shaped classes beat `grep`, so no class
63
- is graph-first. Routing changes; permission does not. Figures:
71
+ Query-first is an **ordering** heuristic, never a claim the index answers better
72
+ — no measured class beat `grep`. Figures:
64
73
  [`code-intelligence`](../skills/code-intelligence/SKILL.md) § Measured.
65
74
 
66
75
  ## When NOT to fire
@@ -71,11 +80,7 @@ is graph-first. Routing changes; permission does not. Figures:
71
80
  ## See also
72
81
 
73
82
  - [`code-intelligence`](../skills/code-intelligence/SKILL.md) — the executable
74
- routing skill (`agent-config code-graph detect|query|affected|path`), the
75
- measured per-class figures, and the hook-vs-instruction-file surface question
76
- (`agent-config hooks:status`, never a guess from the host name).
77
- - [`discovery_graph`](../scripts/discovery_graph.ts) — this suite's OWN artefact
78
- relation-graph (`affected`/`explain`); the external code-graph is the
79
- *source-code* analogue this rule defers to for code questions.
83
+ routing skill, the measured figures, staleness delivery per host, and the
84
+ pointer to this suite's OWN artifact relation-graph (`discovery_graph`).
80
85
  - [`think-before-action`](think-before-action.md) — analyze with the best
81
86
  available tool before grepping blind.
@@ -24,11 +24,8 @@ obligation_frequency: "per-turn"
24
24
  # Notes-First Reasoning
25
25
 
26
26
  Part of the Reasoning Discipline Protocol. Engage per
27
- [`rdp-gate`](../contexts/execution/rdp-gate.md) (settings + task-signal + host
28
- self-assessment) — skip on trivial tasks; apply lightly on a strong-reasoning
29
- host. The notes file is grounded in the documented cross-run lessons memory
30
- (consolidated via [`memory-consolidation`](../skills/memory-consolidation/SKILL.md));
31
- the in-task sections below are a local derivation for within-task scope.
27
+ [`rdp-gate`](../contexts/execution/rdp-gate.md) — skip on trivial tasks, apply
28
+ lightly on a strong-reasoning host.
32
29
 
33
30
  ## The Iron Law
34
31
 
@@ -38,36 +35,50 @@ NOTES FILE — NEVER ECHOED INTO THE RESPONSE.
38
35
  THE RESPONSE CARRIES CONCLUSIONS + EVIDENCE ONLY.
39
36
  ```
40
37
 
41
- Reasoning dumped into the user-facing answer is both noise and a
42
- `reasoning_extraction` refusal risk (see `rdp-gate`). Keep it in the notes file.
43
- This is not "show your work in the reply" — it is the opposite.
38
+ This is not "show your work in the reply" — it is the opposite. Why, and what
39
+ grounds the notes file:
40
+ [`notes-horizon-mechanics`](../docs/guidelines/agent-infra/notes-horizon-mechanics.md).
44
41
 
45
42
  ## Notes file structure (the file, not the response)
46
43
 
47
- Use the sections that apply; the structure carries the enumeration, so there is
48
- no "write N hypotheses" instruction — record what the work actually surfaced.
44
+ Use the sections that apply — record what the work actually surfaced.
49
45
 
50
46
  - `## In-Task Hypothesis Log` — competing explanations under consideration.
51
- - `## Killed beliefs` — each discarded hypothesis + the evidence that killed it.
52
- - `## Predictions` — prediction · confidence · result · lesson (the calibration
53
- loop: hypothesis → prediction → reality → calibration).
54
- - `## Decisions` — decision · alternatives · reason · revisit-if. Tactical,
55
- in-task decisions stay here; **escalate to
56
- [`decision-record`](../skills/decision-record/SKILL.md)/ADR** when the decision
57
- is cross-task or architectural (litmus: would a dev on a different component
58
- next month need this context?).
47
+ - `## Killed beliefs` — each discarded hypothesis + the `killed-if` that killed
48
+ it.
49
+ - `## Predictions` — **chosen form** · prediction · confidence · result · lesson.
50
+ - `## Decisions` — decision · alternatives · reason · revisit-if ·
51
+ **next-commitment**. Tactical decisions stay here; **escalate to
52
+ [`decision-record`](../skills/decision-record/SKILL.md)/ADR** when cross-task
53
+ or architectural.
59
54
  - `## Uncertainty` — per-dimension score (e.g. architecture/implementation/
60
55
  requirements: high/medium/low); feeds the adaptive-effort decision.
61
56
 
57
+ ### The horizon — where `next-commitment` ends
58
+
59
+ ```
60
+ A CHOICE AUTHORISES WORK UP TO THE NEXT EVIDENCE-PRODUCING BOUNDARY.
61
+ NAME THE BOUNDARY FROM THE LIST. NEVER "THE WORK THE EVIDENCE SUPPORTS" —
62
+ THAT IS A JUDGEMENT, AND A JUDGEMENT IS NOT A HORIZON.
63
+ ```
64
+
65
+ ### Reopening — once, on contradiction, never on a schedule
66
+
67
+ ```
68
+ AN OBSERVATION THAT CONTRADICTS THE PREDICTION REOPENS THE CHOICE BEFORE THE
69
+ NEXT STEP — FROM THE CANDIDATE LIST THAT ALREADY EXISTS, NEVER A FRESH
70
+ ENUMERATION. ONE REOPEN PER CANDIDATE. THE SECOND CONTRADICTION HANDS OVER TO
71
+ THE RETRY-BUDGET LADDER RATHER THAN REOPENING AGAIN.
72
+ ```
73
+
62
74
  ## What stays out of notes
63
75
 
64
- User-attribute facts, transient TODOs, and durable cross-run lessons — those go
65
- to the memory system (`memory-consolidation`), not the in-task notes.
76
+ User-attribute facts, transient TODOs, and durable cross-run lessons go to the
77
+ memory system (`memory-consolidation`), not the in-task notes.
66
78
 
67
79
  ## See also
68
80
 
69
- - [`rdp-gate`](../contexts/execution/rdp-gate.md) — the table-free engagement gate.
70
- - [`memory-consolidation`](../skills/memory-consolidation/SKILL.md) — promotes
71
- durable lessons across runs.
72
- - [`verify-before-complete`](verify-before-complete.md) — the evidence the
73
- response carries comes from real tool results.
81
+ [`notes-horizon-mechanics`](../docs/guidelines/agent-infra/notes-horizon-mechanics.md)
82
+ — every mechanic this rule points at. Also
83
+ [`rdp-gate`](../contexts/execution/rdp-gate.md) and
84
+ [`verify-before-complete`](verify-before-complete.md).
@@ -37,9 +37,32 @@ A CALL THAT DOES NOT READ THE PREVIOUS RESULT IS NOT A SECOND TURN.
37
37
 
38
38
  The ceiling above forbids repetition; this forbids splitting work that had no
39
39
  reason to be split — measured mean batch size **1.01**, i.e. fully serial. The
40
- discriminator is the dependency, never the count. NOT "write shorter commands":
41
- the long commands are already the batching. `instruction-only` — nothing can
42
- observe a call that was not batched. Evidence + the absent-cause finding:
40
+ discriminator is the dependency, never the count. `instruction-only` — nothing
41
+ can observe a call that was not batched. Evidence + the absent-cause finding:
42
+ [`token-efficiency-mechanics`](../contexts/communication/rules-auto/token-efficiency-mechanics.md).
43
+
44
+ ## One command per Bash call
45
+
46
+ ```
47
+ ONE COMMAND PER BASH CALL. NEVER CHAIN WORK STEPS WITH `;`, `&&` OR `||`,
48
+ AND NEVER CARRY STATE FORWARD IN A LEADING `VAR=…` ASSIGNMENT.
49
+ N COMMANDS GO IN N CALLS IN ONE BLOCK — THAT IS WHAT A BATCH IS.
50
+ A CHAINED CALL IS AUTHORIZED ONLY AS STRONGLY AS ITS WEAKEST SEGMENT.
51
+ ```
52
+
53
+ A permission rule, not a token one — which is why it is not folded into the
54
+ section above. The host splits a compound command on the shell operators and
55
+ requires **each segment to match the allowlist independently**, so one
56
+ unmatched segment sends the whole call down the permission path even when
57
+ every other segment was already allowed. Chaining converts N cheap
58
+ authorizations into one expensive one. `D=/repo; cd $D && git status` is
59
+ `git -C /repo status`.
60
+
61
+ **Not forbidden:** a pipe of ordinary filters, a redirect, a heredoc — one
62
+ command with a filter or with input, not two work steps. Carried at tool-call
63
+ time by the `chain-nudge` `pre_tool_use` concern, which is advisory and never
64
+ blocks, so compliance stays model-carried. Measured numbers, the substitution
65
+ table and the carrier's limits:
43
66
  [`token-efficiency-mechanics`](../contexts/communication/rules-auto/token-efficiency-mechanics.md).
44
67
 
45
68
  ## Enumerated file sets are ONE operation, not N repetitions
@@ -0,0 +1,51 @@
1
+ /**
2
+ * "Was this module invoked as the command?" — one definition for the scripts
3
+ * under `agents/scripts` that both run directly and are imported by siblings.
4
+ *
5
+ * Five of them carried a byte-identical copy of this logic, and the copies were
6
+ * about to gain a sixth clause each. The interesting part is why the obvious
7
+ * one-liner is not enough:
8
+ *
9
+ * - `--splitting` in `build:agent-src-delegate` can move a module body into a
10
+ * shared chunk, where `import.meta.url` names the CHUNK. The URL comparison
11
+ * then never matches and the command exits 0 having done nothing — measured
12
+ * on `update_roadmap_progress` before this guard existed. Inside the bundle
13
+ * the invoked file name is the reliable signal, so the delegate branch runs
14
+ * first and a miss falls through rather than returning false.
15
+ * - A symlinked invocation (`.augment/scripts` → `dist/agent-src/scripts`, or
16
+ * macOS `/var` → `/private/var`) makes the raw URLs differ: `import.meta.url`
17
+ * is the resolved real path while `argv[1]` keeps the symlink. Hence the
18
+ * realpath comparison last.
19
+ *
20
+ * `moduleUrl` is the caller's own `import.meta.url` — it cannot be read from
21
+ * here, which is why it is a parameter rather than a lookup.
22
+ */
23
+ import * as fs from 'node:fs';
24
+ import * as path from 'node:path';
25
+ import { fileURLToPath, pathToFileURL } from 'node:url';
26
+
27
+ /** Defined only by the esbuild delegate builds; `undefined` under tsx and node. */
28
+ declare const __AGENT_CONFIG_CLI_DELEGATE__: boolean | undefined;
29
+
30
+ export function isCliEntry(moduleUrl: string, name: string): boolean {
31
+ const argv1 = process.argv[1];
32
+ if (argv1 === undefined) {
33
+ return false;
34
+ }
35
+ if (
36
+ typeof __AGENT_CONFIG_CLI_DELEGATE__ !== 'undefined' &&
37
+ __AGENT_CONFIG_CLI_DELEGATE__ &&
38
+ path.basename(argv1, '.js') === name
39
+ ) {
40
+ return true;
41
+ }
42
+ const argvPath = path.resolve(argv1);
43
+ if (moduleUrl === pathToFileURL(argvPath).href) {
44
+ return true;
45
+ }
46
+ try {
47
+ return fs.realpathSync(fileURLToPath(moduleUrl)) === fs.realpathSync(argvPath);
48
+ } catch {
49
+ return false;
50
+ }
51
+ }
@@ -56,11 +56,17 @@ import { spawnSync } from 'node:child_process';
56
56
  import * as fs from 'node:fs';
57
57
  import * as path from 'node:path';
58
58
  import process from 'node:process';
59
- import { fileURLToPath, pathToFileURL } from 'node:url';
59
+ import { fileURLToPath } from 'node:url';
60
60
 
61
- import { collect } from './update_roadmap_progress.js';
61
+ import {
62
+ blocker_is_resolved,
63
+ collect,
64
+ parse_blockers,
65
+ } from './update_roadmap_progress.js';
62
66
  import { guardedBaselineProblems, parseGuardedBaselines } from './guarded_baseline.js';
63
67
 
68
+ import { isCliEntry } from './_cli_entry.js';
69
+
64
70
  const _HERE = fileURLToPath(import.meta.url);
65
71
 
66
72
  interface CompletedProcess {
@@ -263,6 +269,80 @@ function _git_mv(root: string, src_rel: string, dst_rel: string, dry_run: boolea
263
269
  }
264
270
  }
265
271
 
272
+ // ---------------------------------------------------------------------
273
+ // The index/working-tree blocker divergence check (1b) + staging (1a)
274
+ // ---------------------------------------------------------------------
275
+ //
276
+ // The open-blocker refusal below reads the file with `fs.readFileSync`, i.e.
277
+ // the WORKING TREE. `git mv` then moves the INDEX ENTRY, which still carries
278
+ // whatever was staged. When the two disagree the commit ships the index
279
+ // content — content this gate never read.
280
+ //
281
+ // Measured, once, at full cost (2026-09-08): an edit removing a roadmap's
282
+ // blocker was made and never staged; `git mv` moved the stale index entry; the
283
+ // archived file shipped a blocker declared `Status: open` that had already been
284
+ // relocated to an active roadmap. The same blocker was open in two files at
285
+ // once, one of them archived, and it was caught by a human reading
286
+ // `git status` rather than by any gate.
287
+ //
288
+ // The check is scoped to blocker id → status and deliberately NOT to whole-file
289
+ // equality. An unstaged typo fix in a roadmap title has nothing to do with the
290
+ // defect, and refusing archival for it trains `git add -A`, which destroys the
291
+ // staging discipline this check depends on. AI council 2026-09-08, 2/2 present
292
+ // (anthropic/claude-sonnet-4-5 + openai/gpt-4o), on that scoping specifically:
293
+ // broad scoping is "user-hostile, trains developers to `git add -A`
294
+ // reflexively, defeating staging discipline".
295
+
296
+ /** Blocker id → status, as the archive-transition check compares them. */
297
+ function _blockerStatuses(text: string): Map<string, string> {
298
+ const out = new Map<string, string>();
299
+ for (const b of parse_blockers(text)) {
300
+ out.set(b.id, blocker_is_resolved(b) ? 'resolved' : 'open');
301
+ }
302
+ return out;
303
+ }
304
+
305
+ /**
306
+ * `git show :<rel>` — the STAGED content of a path, or null when the path has
307
+ * no index entry.
308
+ *
309
+ * Null is the untracked / pre-first-commit case, which `_git_mv` already
310
+ * handles by falling back to a plain rename. There is no index entry to
311
+ * disagree with, so the divergence check has nothing to assert and says so by
312
+ * returning null rather than by guessing at an empty string — an empty staged
313
+ * file and an absent one differ, and only the second is legitimate here.
314
+ */
315
+ function _indexContent(root: string, rel: string): string | null {
316
+ const cp = _run(['git', 'show', `:${rel}`], root);
317
+ return cp.returncode === 0 ? cp.stdout : null;
318
+ }
319
+
320
+ /**
321
+ * Human-readable disagreements between the staged and working-tree blocker
322
+ * maps of one path. Empty array = the two agree, or there is no index entry.
323
+ */
324
+ function _blockerDivergence(root: string, rel: string, workTreeText: string): string[] {
325
+ const staged = _indexContent(root, rel);
326
+ if (staged === null) {
327
+ return [];
328
+ }
329
+ const wt = _blockerStatuses(workTreeText);
330
+ const idx = _blockerStatuses(staged);
331
+ const ids = [...new Set([...wt.keys(), ...idx.keys()])].sort();
332
+ const out: string[] = [];
333
+ for (const id of ids) {
334
+ const a = idx.get(id);
335
+ const b = wt.get(id);
336
+ if (a === b) {
337
+ continue;
338
+ }
339
+ out.push(
340
+ `blocker '${id}': staged=${a ?? 'absent'} vs working tree=${b ?? 'absent'}`,
341
+ );
342
+ }
343
+ return out;
344
+ }
345
+
266
346
  /**
267
347
  * Repo-relative paths of files containing `needle`, by walking the filesystem.
268
348
  *
@@ -545,9 +625,8 @@ function archive_completed(
545
625
  // and invisible in a sweep that only reports what it moved; a MALFORMED
546
626
  // one on `[x]` would otherwise archive with an annotation claiming the
547
627
  // opposite. Both are named here instead.
548
- const guarded = parseGuardedBaselines(
549
- fs.readFileSync(path.join(roadmap_root, stats.rel), 'utf-8'),
550
- );
628
+ const roadmapText = fs.readFileSync(path.join(roadmap_root, stats.rel), 'utf-8');
629
+ const guarded = parseGuardedBaselines(roadmapText);
551
630
  if (guarded.length > 0) {
552
631
  process.stderr.write(
553
632
  ` ⚠️ ${stats.rel}: ${guarded.length} guarded-baseline step(s) — ` +
@@ -601,12 +680,37 @@ function archive_completed(
601
680
  if (changed_only && !(touched as Set<string>).has(old_rel)) {
602
681
  continue; // complete, but not this branch's work
603
682
  }
683
+ // The gate above validated the WORKING TREE; `git mv` ships the INDEX.
684
+ // When they disagree about a blocker, the validated content is not the
685
+ // content that gets committed — see § the index/working-tree blocker
686
+ // divergence check above for the measured failure.
687
+ const divergence = _blockerDivergence(root, old_rel, roadmapText);
688
+ if (divergence.length > 0) {
689
+ process.stderr.write(
690
+ ` ⚠️ ${stats.rel}: staged and working-tree blockers disagree ` +
691
+ `(${divergence.length}) — not archived. \`git mv\` would commit ` +
692
+ 'the staged content, which this sweep never validated. Stage or ' +
693
+ 'discard the change, then re-run.\n',
694
+ );
695
+ for (const d of divergence) {
696
+ process.stderr.write(` · ${d}\n`);
697
+ }
698
+ continue;
699
+ }
604
700
  const new_rel = `agents/roadmaps/archive/${stats.rel}`;
605
701
  const mv = _git_mv(root, old_rel, new_rel, dry_run);
606
702
  if (!mv.ok) {
607
703
  process.stderr.write(` ⚠️ could not archive ${old_rel} (mv failed)\n`);
608
704
  continue;
609
705
  }
706
+ // `git mv` moved the index ENTRY, not the working-tree content. Staging
707
+ // the destination makes what gets committed identical to what the checks
708
+ // above read — the divergence check refuses a disagreement that exists
709
+ // BEFORE the move, and this closes one created by an edit between the
710
+ // move and the commit.
711
+ if (!dry_run && mv.tracked) {
712
+ _run(['git', 'add', '--', new_rel], root);
713
+ }
610
714
  const refs = _inbound_ref_rewrite(root, old_rel, new_rel, dry_run, mv.tracked);
611
715
  // Stage rewritten refs only in a tracked repo; an untracked consumer's
612
716
  // files are not in the index, so the filesystem rewrite is the whole job.
@@ -638,6 +742,16 @@ function _regen_dashboard(root: string, dry_run: boolean): void {
638
742
  if (dry_run) {
639
743
  return;
640
744
  }
745
+ // Inside `dist/cli-delegate/` the twin sits next to us as a precompiled
746
+ // bundle, and plain `node` runs it — the tsx walk below would otherwise
747
+ // reach `npx tsx`, which resolves against the CONSUMER's npm config and
748
+ // hard-fails on a mismatched `engines`/`devEngines` pin (the very class
749
+ // ADR-204 removed from the dispatcher and left standing here).
750
+ const bundledTwin = path.join(path.dirname(_HERE), 'update_roadmap_progress.js');
751
+ if (_isFile(bundledTwin)) {
752
+ spawnSync(process.execPath, [bundledTwin], { cwd: root, encoding: 'utf-8' });
753
+ return;
754
+ }
641
755
  const script = path.join(path.dirname(_HERE), 'update_roadmap_progress.ts');
642
756
  // Python: `_run([sys.executable, str(script)], root)`; the twin spawns the
643
757
  // dashboard generator twin through the same `tsx` runtime, cwd=root.
@@ -800,28 +914,7 @@ function main(argv?: readonly string[]): number {
800
914
  return 0;
801
915
  }
802
916
 
803
- function _isCliEntry(): boolean {
804
- if (process.argv[1] === undefined) {
805
- return false;
806
- }
807
- const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
808
- if (import.meta.url === argvUrl) {
809
- return true;
810
- }
811
- // A symlinked invocation (e.g. via `.augment/scripts` → `dist/agent-src/scripts`,
812
- // or macOS /var → /private/var temp dirs) makes the raw URLs differ:
813
- // import.meta.url is the resolved real path while argv[1] keeps the symlink
814
- // path. Compare realpaths so the entry guard still fires (without this the
815
- // archival sweep silently no-ops when run through the symlink).
816
- try {
817
- const here = fs.realpathSync(fileURLToPath(import.meta.url));
818
- const argv = fs.realpathSync(path.resolve(process.argv[1]));
819
- return here === argv;
820
- } catch {
821
- return false;
822
- }
823
- }
824
- if (_isCliEntry() || process.argv[1] === _HERE) {
917
+ if (isCliEntry(import.meta.url, 'archive_completed_roadmaps')) {
825
918
  try {
826
919
  process.exitCode = main();
827
920
  } catch (exc) {
@@ -65,7 +65,7 @@
65
65
  */
66
66
 
67
67
  import * as path from 'node:path';
68
- import { fileURLToPath, pathToFileURL } from 'node:url';
68
+ import { fileURLToPath } from 'node:url';
69
69
  import * as fs from 'node:fs';
70
70
  import { spawnSync } from 'node:child_process';
71
71
 
@@ -80,6 +80,8 @@ import { execute } from './gate_execute.js';
80
80
  import { listPending } from '../templates/scripts/work_engine/hooks/builtin/staged_confirmation_store.js';
81
81
  import type { StagedAction } from '../templates/scripts/work_engine/hooks/builtin/staged_confirmation.js';
82
82
 
83
+ import { isCliEntry } from './_cli_entry.js';
84
+
83
85
  const _HERE = fileURLToPath(import.meta.url);
84
86
 
85
87
  /** Hard wrap for the rendered body — keeps output readable in a narrow pane. */
@@ -928,26 +930,8 @@ function main(argv?: readonly string[]): number {
928
930
  return 0;
929
931
  }
930
932
 
931
- function _isCliEntry(): boolean {
932
- if (process.argv[1] === undefined) {
933
- return false;
934
- }
935
- const argvPath = path.resolve(process.argv[1]);
936
- if (import.meta.url === pathToFileURL(argvPath).href) {
937
- return true;
938
- }
939
- // A symlinked invocation (`.augment/scripts` → `dist/agent-src/scripts`, or
940
- // macOS /var → /private/var) makes the raw URLs differ: import.meta.url is
941
- // the resolved real path while argv[1] keeps the symlink. Compare realpaths
942
- // so the entry guard still fires through the projection.
943
- try {
944
- return fs.realpathSync(_HERE) === fs.realpathSync(argvPath);
945
- } catch {
946
- return false;
947
- }
948
- }
949
933
 
950
- if (_isCliEntry()) {
934
+ if (isCliEntry(import.meta.url, 'roadmap_gates')) {
951
935
  process.exitCode = main();
952
936
  }
953
937
 
@@ -30,7 +30,9 @@
30
30
  import * as fs from 'node:fs';
31
31
  import * as path from 'node:path';
32
32
  import process from 'node:process';
33
- import { fileURLToPath, pathToFileURL } from 'node:url';
33
+ import { fileURLToPath } from 'node:url';
34
+
35
+ import { isCliEntry } from './_cli_entry.js';
34
36
 
35
37
  const _HERE = fileURLToPath(import.meta.url);
36
38
 
@@ -246,8 +248,9 @@ export function main(argv: string[] = process.argv.slice(2)): number {
246
248
  }
247
249
  }
248
250
 
251
+
249
252
  /* c8 ignore start */
250
- if (process.argv[1] !== undefined && pathToFileURL(path.resolve(process.argv[1])).href === pathToFileURL(_HERE).href) {
253
+ if (isCliEntry(import.meta.url, 'roadmap_set_step')) {
251
254
  process.exit(main());
252
255
  }
253
256
  /* c8 ignore stop */
@@ -42,7 +42,9 @@
42
42
  */
43
43
  import * as fs from 'node:fs';
44
44
  import * as path from 'node:path';
45
- import { fileURLToPath, pathToFileURL } from 'node:url';
45
+ import { fileURLToPath } from 'node:url';
46
+
47
+ import { isCliEntry } from './_cli_entry.js';
46
48
 
47
49
  const STUB_DIR = path.join('agents', 'roadmaps', 'stubs');
48
50
 
@@ -459,24 +461,8 @@ export function main(argv: readonly string[] = process.argv.slice(2)): number {
459
461
  return 0;
460
462
  }
461
463
 
462
- function _isCliEntry(): boolean {
463
- if (process.argv[1] === undefined) {
464
- return false;
465
- }
466
- const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
467
- if (import.meta.url === argvUrl) {
468
- return true;
469
- }
470
- try {
471
- const here = fs.realpathSync(fileURLToPath(import.meta.url));
472
- const argv = fs.realpathSync(path.resolve(process.argv[1]));
473
- return here === argv;
474
- } catch {
475
- return false;
476
- }
477
- }
478
464
 
479
- if (_isCliEntry() || process.argv[1] === _HERE) {
465
+ if (isCliEntry(import.meta.url, 'stubs_due')) {
480
466
  process.exitCode = main();
481
467
  }
482
468