@event4u/agent-config 4.9.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/.agent-src/commands/implement-ticket.md +5 -4
  2. package/.agent-src/contexts/execution/roadmap-process-loop.md +30 -4
  3. package/.agent-src/rules/language-and-tone.md +4 -10
  4. package/.agent-src/rules/linked-projects-onboarding-gate.md +82 -0
  5. package/.agent-src/rules/roadmap-progress-sync.md +39 -5
  6. package/.agent-src/scripts/update_roadmap_progress.py +63 -7
  7. package/.agent-src/skills/command-routing/SKILL.md +5 -4
  8. package/.agent-src/skills/roadmap-management/SKILL.md +121 -21
  9. package/.agent-src/skills/roadmap-writing/SKILL.md +63 -0
  10. package/.agent-src/templates/agent-settings.md +16 -0
  11. package/.agent-src/templates/roadmaps.md +22 -1
  12. package/.agent-src/templates/scripts/work_engine/_lib/agent_settings.py +20 -3
  13. package/.claude-plugin/marketplace.json +1 -1
  14. package/CHANGELOG.md +106 -0
  15. package/CONTRIBUTING.md +19 -0
  16. package/README.md +12 -1
  17. package/dist/cli/registry.js +0 -2
  18. package/dist/cli/registry.js.map +1 -1
  19. package/dist/discovery/deprecation-report.md +1 -1
  20. package/dist/discovery/discovery-manifest.json +36 -14
  21. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  22. package/dist/discovery/discovery-manifest.summary.md +3 -3
  23. package/dist/discovery/orphan-report.md +1 -1
  24. package/dist/discovery/packs.json +6 -5
  25. package/dist/discovery/trust-report.md +3 -3
  26. package/dist/discovery/workspaces.json +5 -4
  27. package/dist/mcp/registry-manifest.json +3 -3
  28. package/dist/router.json +1 -1671
  29. package/docs/architecture.md +1 -1
  30. package/docs/benchmark.md +20 -8
  31. package/docs/benchmarks.md +11 -0
  32. package/docs/catalog.md +3 -2
  33. package/docs/contracts/benchmark-corpus-spec.md +31 -3
  34. package/docs/contracts/command-surface-tiers.md +1 -1
  35. package/docs/contracts/hook-architecture-v1.md +33 -0
  36. package/docs/contracts/migrate-command.md +197 -0
  37. package/docs/contracts/settings-api.md +2 -1
  38. package/docs/contracts/value-dashboard-spec.md +374 -0
  39. package/docs/contracts/value-report-schema.md +150 -0
  40. package/docs/decisions/ADR-031-validation-severity-tiers-and-projection-roundtrip.md +97 -0
  41. package/docs/decisions/ADR-032-linked-projects-scope.md +118 -0
  42. package/docs/decisions/INDEX.md +2 -0
  43. package/docs/getting-started.md +1 -1
  44. package/docs/guidelines/agent-infra/installed-tools-manifest.md +6 -3
  45. package/docs/guidelines/agent-infra/language-and-tone-examples.md +35 -0
  46. package/docs/guides/cross-repo-linked-projects.md +86 -0
  47. package/docs/migration/v1-to-v2.md +40 -27
  48. package/docs/value.md +84 -0
  49. package/package.json +8 -8
  50. package/scripts/__pycache__/validate_frontmatter.cpython-312.pyc +0 -0
  51. package/scripts/_cli/cmd_migrate.py +264 -102
  52. package/scripts/_cli/cmd_settings_migrate.py +2 -1
  53. package/scripts/_dispatch.bash +147 -49
  54. package/scripts/_lib/__pycache__/__init__.cpython-312.pyc +0 -0
  55. package/scripts/_lib/__pycache__/agent_src.cpython-312.pyc +0 -0
  56. package/scripts/_lib/agent_settings.py +20 -3
  57. package/scripts/_lib/install_regenerator.py +129 -0
  58. package/scripts/_lib/linked_projects.py +238 -0
  59. package/scripts/_lib/value_ladder.py +599 -0
  60. package/scripts/_lib/value_report.py +441 -0
  61. package/scripts/bench_rtk_savings.py +320 -0
  62. package/scripts/check_no_local_settings_committed.py +51 -0
  63. package/scripts/compile_router.py +19 -5
  64. package/scripts/expected_perms.json +1 -1
  65. package/scripts/first_run_gate_hook.py +178 -0
  66. package/scripts/hook_manifest.yaml +16 -7
  67. package/scripts/hooks/dispatch_hook.py +27 -0
  68. package/scripts/hooks/dispatch_issues.py +136 -0
  69. package/scripts/hooks_doctor.py +40 -1
  70. package/scripts/install.py +25 -21
  71. package/scripts/lint_agents_layout.py +5 -4
  72. package/scripts/lint_bench_corpus.py +86 -4
  73. package/scripts/lint_global_paths.py +4 -3
  74. package/scripts/lint_marketplace_install_completeness.py +188 -0
  75. package/scripts/lint_value_dashboard.py +218 -0
  76. package/scripts/render_benchmark_md.py +6 -2
  77. package/scripts/render_value_md.py +355 -0
  78. package/scripts/repro/repro_marketplace_install_gap.sh +161 -0
  79. package/scripts/roadmap_progress_hook.py +23 -0
  80. package/scripts/router_telemetry.py +470 -0
  81. package/scripts/validate_frontmatter.py +23 -9
  82. package/scripts/_cli/cmd_migrate_to_global.py +0 -415
@@ -59,12 +59,13 @@ Three cases, in this order:
59
59
  and `.work-state.json` does not. Migrate before doing anything else:
60
60
 
61
61
  ```bash
62
- ./agent-config migrate-state
62
+ ./agent-config migrate
63
63
  ```
64
64
 
65
- Writes `.work-state.json` and renames the source to
66
- `.implement-ticket-state.json.bak`. Idempotent and safe to skip if
67
- already done. After this, treat the run as **Resume**.
65
+ Unified `migrate` writes `.work-state.json`, renames source to
66
+ `.implement-ticket-state.json.bak`, sweeps other legacy install
67
+ artefacts (see `docs/contracts/migrate-command.md`). Idempotent and
68
+ safe to skip if already done. After this, treat the run as **Resume**.
68
69
  - **Fresh run** — no state file at all. Write the resolved ticket to
69
70
  `ticket.json` (id, title, body, acceptance_criteria) and pass it via
70
71
  `--ticket-file ticket.json`. Honour `roles.active_role` from
@@ -231,10 +231,36 @@ execution either way.
231
231
  consultations count (if on), steps remaining, halts.
232
232
  - Final dashboard regen.
233
233
  - **If the entire roadmap reached `count_open == 0`** → run the full
234
- project quality pipeline. On greenarchival via the
235
- [`roadmap-management`](../../skills/roadmap-management/SKILL.md) skill
236
- (`git mv` to `agents/roadmaps/archive/`, regenerate dashboard). On
237
- red → stop, surface failures, do **not** archive.
234
+ project quality pipeline. On redstop, surface failures, do **not**
235
+ archive. On green → run **deferred-resolution gate** below before
236
+ archival.
237
+
238
+ ### 6a. Deferred-resolution gate — Iron Law 3
239
+
240
+ Before any `git mv` to `archive/`, count `[~]` items in closing
241
+ roadmap. If `count_deferred > 0`, archival **blocked** per
242
+ [`roadmap-progress-sync § Iron Law 3`](../../rules/roadmap-progress-sync.md).
243
+ Loop MUST:
244
+
245
+ 1. Enumerate every `[~]` step (phase + text + optional
246
+ `<!-- deferred: ... -->` annotation).
247
+ 2. Surface numbered-options block from
248
+ [`roadmap-management § 4b`](../../skills/roadmap-management/SKILL.md) —
249
+ five choices: follow-up (draft), follow-up (ready + blocked),
250
+ keep-in-archive (intentional drop), restore to `[ ]`, convert
251
+ to `[-]` cancelled.
252
+ 3. Wait for user. Autonomous mandate (`/work`,
253
+ `/roadmap:process-full`, "decide for me") does **not** lift this
254
+ gate — Iron Law 3 calls it "the canonical lost-information failure
255
+ mode this rule exists to prevent."
256
+ 4. On picks 1 / 2 → run "Spawn follow-up from deferred items"
257
+ procedure in [`roadmap-management`](../../skills/roadmap-management/SKILL.md).
258
+ On picks 3 / 4 / 5 → apply change, re-evaluate decision table,
259
+ archive when gate clears.
260
+
261
+ `count_deferred == 0` → archive proceeds via
262
+ [`roadmap-management`](../../skills/roadmap-management/SKILL.md) skill
263
+ (`git mv`, regen).
238
264
 
239
265
  ## Scope deltas — what each wrapper binds
240
266
 
@@ -49,26 +49,20 @@ Stays in source language: code blocks, command output, file contents, quoted too
49
49
  1. **Detect** — language of user's last chat message. Mixed → dominant; tie → German.
50
50
  2. **Scan** — every user-visible token per catalog above.
51
51
  3. **Rewrite** — wrong-language token → rewrite the whole reply.
52
- 4. **Confirm** — first sentence in target language; recommendation label matches; no English filler-phrase opener (`Let me`, `Now`, `Found`, `Confirmed`, `OK`, `Alright`, `Here's`, `So`) when target is German; no German opener (`Lass mich`, `Jetzt`, `Gefunden`, `Bestätigt`) when target is English.
52
+ 4. **Confirm** — first sentence in target language; recommendation label matches; no wrong-language filler-phrase opener. Blocklist: [`language-and-tone-examples § Pre-send gate`](../docs/guidelines/agent-infra/language-and-tone-examples.md#pre-send-gate--filler-phrase-blocklist).
53
53
 
54
54
  ## Spelled out
55
55
 
56
56
  - German → informal "Du" (never "Sie"); capitalized at sentence start, lowercase otherwise.
57
57
  - Code blocks / command output / file contents / quoted tool output stay native; only surrounding prose mirrors.
58
58
  - Numbered options — `.md` source English; rendered reply translated at runtime.
59
+ - Code comments in English. `.md` files in English (see below). Translate existing German `.md` files when touched.
59
60
 
60
61
  ## Slip handling
61
62
 
62
63
  Acknowledge **once** in the correct language ("Entschuldigung" / "Sorry"). Switch on the same reply. No re-explain in wrong language; no "from now on" promise.
63
64
 
64
- Examples + wrong-vs-correct: [`language-and-tone-examples`](../docs/guidelines/agent-infra/language-and-tone-examples.md).
65
-
66
- ## Other language rules
67
-
68
- - Code comments in English.
69
- - `.md` files in English (see below). Translate existing German `.md` files when touched.
70
- - Two spaces after `❌`, `✅`, `⚠️` in CLI; one space for other icons.
71
- - One blank line max; no double/triple blanks. File ends with exactly one newline.
65
+ Examples + CLI spacing rules + wrong-vs-correct: [`language-and-tone-examples`](../docs/guidelines/agent-infra/language-and-tone-examples.md).
72
66
 
73
67
  ## `.md` files — ALWAYS English
74
68
 
@@ -76,4 +70,4 @@ Every text inside `.md` under `.augment/`, `.agent-src/`, `.agent-src.uncondense
76
70
 
77
71
  **Labeled-anchor exception** — quoting German inside English prose is forbidden. Either translate, OR use a labeled `DE: … · EN: …` anchor block (only allowed location for German prose).
78
72
 
79
- **Detection heuristic** before saving: scan for umlauts (`ä`, `ö`, `ü`, `Ä`, `Ö`, `Ü`, `ß`) outside fenced code / paths / anchor blocks; German function words (`für`, `nicht`, `dass`, `wenn`, `sollte`, `werden`, `arbeite`, `selbstständig`, `jetzt`, `einfach`, `weiter`, `lösche`, `frag`, `schreib`, `mach`); non-English quoted phrases in body text. Hit → translate or move to `DE: … · EN: …` block.
73
+ Pre-save detection heuristic (umlauts / German function words / non-English quoted phrases): [`language-and-tone-examples § pre-save detection`](../docs/guidelines/agent-infra/language-and-tone-examples.md#md-files--pre-save-detection-heuristic).
@@ -0,0 +1,82 @@
1
+ ---
2
+ type: "auto"
3
+ tier: "2b"
4
+ alwaysApply: false
5
+ description: "IDE-attached sibling repo detected — prompt once to opt it into proactive cross-repo awareness, persist local-only, then surface cross-repo impact on relevant changes"
6
+ source: package
7
+ triggers:
8
+ - intent: "work across two projects"
9
+ - intent: "sibling repository"
10
+ - keyword: "linked project"
11
+ - keyword: "cross-repo"
12
+ - keyword: "sibling repo"
13
+ - path_prefix: ".idea/modules.xml"
14
+ - path_prefix: ".idea/vcs.xml"
15
+ validator_ignore:
16
+ - type: "substring"
17
+ pattern: "scripts/_lib/linked_projects.py"
18
+ reason: "Rule names the detector module as the runtime detection entrypoint."
19
+ workspaces:
20
+ - agent-config-maintainer
21
+ - engineering
22
+ packs:
23
+ - engineering-base
24
+ lifecycle: experimental
25
+ trust:
26
+ level: experimental
27
+ confidence: medium
28
+ human_review_required: false
29
+ install:
30
+ default: true
31
+ removable: true
32
+ ---
33
+
34
+ # Linked-Projects Onboarding Gate
35
+
36
+ **Iron Law.** IDE attached a sibling repo and it is not yet in `linked_projects` → prompt developer **once** to opt in, persist local-only, then proactively flag cross-repo impact — never bulk-include the sibling's files.
37
+
38
+ Closes the **proactivity gap**: agent can already read/write a sibling, but does not *consider* one unless told — and the developer who most needs cross-repo awareness won't think to mention the sibling. Detection reads the relationship already encoded by attaching the repo in the IDE (zero-knowledge). See the cross-repo guide (`docs/guides/cross-repo-linked-projects.md`) and ADR-032.
39
+
40
+ ## When this fires
41
+
42
+ First substantive turn (and on new IDE attachment), when a detected sibling is absent from `linked_projects` in `.agent-settings.local.yml` (in agents/settings/). Inert when no sibling attached or every detected sibling already decided (opted-in or declined).
43
+
44
+ ## Detection
45
+
46
+ Run the detector against project root:
47
+
48
+ ```bash
49
+ python3 -c "from scripts._lib.linked_projects import detect_linked_projects; \
50
+ import json; print(json.dumps(detect_linked_projects('.')))"
51
+ ```
52
+
53
+ Returns config-attached siblings only (PhpStorm `.idea/modules.xml` + `vcs.xml`, VS Code `*.code-workspace`) resolving outside the project, existing, git repos. A sibling above `linked_projects_max_files` (default 20000) carries `"large": true` — awareness only, never excluded.
54
+
55
+ ## Opt-in (one-time per sibling)
56
+
57
+ For each detected sibling **not** already in `linked_projects`, ask once (numbered options per `user-interaction`): include / decline / always / never-ask. Persist to `.agent-settings.local.yml` (in agents/settings/) (gitignored per-machine layer — never committed `.agent-settings.yml`):
58
+
59
+ ```yaml
60
+ linked_projects:
61
+ - path: /abs/path/to/sibling
62
+ include: true # false = declined; never re-prompt
63
+ ```
64
+
65
+ Declined sibling (`include: false`) never prompted again.
66
+
67
+ ## Behavioral directive (each `include: true` sibling)
68
+
69
+ - **proactively check cross-repo impact** when a change here may affect it — API-contract changes, shared-type / enum drift, renamed routes the sibling consumes — and **warn** before finishing;
70
+ - **do not bulk-include** the sibling's files (passive awareness, not implicit inclusion — large repos stay cheap);
71
+ - out-of-root edits are normal work, but the host agent's own out-of-root **permission gate still applies** (no silent cross-repo write).
72
+
73
+ ## Kill-switch
74
+
75
+ Experimental, removable rule. If opt-in consistently declined or siblings never cited, remove it — no telemetry, signal is local.
76
+
77
+ ## Follow-up (not yet shipped)
78
+
79
+ - Consumer-install detector reachability: detector lives in `scripts/_lib/`; exposing it as an `agent-config` CLI subcommand for consumers is a follow-up. Import-reachable in this repo / co-located maintainer setups today.
80
+ - Multi-agent verification: only Claude Code empirically validated (ADR-032). Cursor / Augment / Copilot unverified — manual fallback in the guide covers them until tested.
81
+
82
+ Trigger-set above activates this routing under the `balanced` and `full` profiles.
@@ -48,11 +48,42 @@ IS A RULE VIOLATION, NOT AN OVERSIGHT.
48
48
 
49
49
  `command:` triggers in this rule's frontmatter load it the moment any `/roadmap:process-*` command fires and keep it loaded for the whole run — independent of whether the agent is editing files under `agents/roadmaps/`. The loop carries its own deterministic flip-guard at [`roadmap-process-loop § 5b`](../contexts/execution/roadmap-process-loop.md#5b-flip-guard--deterministic) — defense-in-depth, not a substitute for the inline flip.
50
50
 
51
- **Step counts as done** when its code/doc change is written and saved AND the verification cited in the step has passed (fresh output in this reply or an earlier one).
51
+ **Step counts as done** when code/doc saved AND verification cited in step passed (fresh output, this reply or earlier).
52
52
 
53
- **In-progress marker.** When a step takes more than one reply, mark it `[~]` the moment work starts — the user sees one row move `[ ] → [~] → [x]` instead of silent rows. `[~]` stays open for `count_open` but advances the phase percentage.
53
+ **Glyph semantics** single source of truth, aligned with `scripts/update_roadmap_progress.py` and [`roadmap-management`](../skills/roadmap-management/SKILL.md):
54
54
 
55
- **Dashboard regen cadence opt-in batching.** The checkbox flip is non-batchable. The **subprocess regen** (`./agent-config roadmap:progress`) is batchable per `roadmap.dashboard_regen_cadence` in `.agent-settings.yml` (`per_step` default · `every_5_steps` · `phase_boundary`). Run end, phase boundary, and any file-shape touch (rename / phase add / archive — Iron Law 1) always force an immediate regen regardless of cadence.
55
+ | Glyph | Meaning | Counter |
56
+ |---|---|---|
57
+ | `[ ]` | open — planned, not done | `count_open` |
58
+ | `[x]` | done — landed + verified | `count_done` |
59
+ | `[~]` | deferred — planned, not happening **this** run; blocks archive (Iron Law 3) | `count_deferred` |
60
+ | `[-]` | cancelled — scope dropped | `count_cancelled` |
61
+
62
+ `[~]` is **not** "in-progress". Mid-reply work-in-flight has no checkbox change until step lands — normal `[ ] → [x]`.
63
+
64
+ **Dashboard regen cadence — opt-in batching.** Checkbox flip is non-batchable. **Subprocess regen** (`./agent-config roadmap:progress`) is batchable per `roadmap.dashboard_regen_cadence` (`per_step` default · `every_5_steps` · `phase_boundary`). Run end, phase boundary, any file-shape touch (rename / phase add / archive — Iron Law 1) always force immediate regen regardless of cadence.
65
+
66
+ ## Iron Law 3 — no silent archive with unresolved deferred items
67
+
68
+ ```
69
+ A ROADMAP WITH `[~]` DEFERRED ITEMS NEVER AUTO-ARCHIVES SILENTLY.
70
+ SURFACE EVERY DEFERRED STEP. ASK USER WHAT HAPPENS TO THE PLAN.
71
+ A SILENT ARCHIVE THAT BURIES PLANNED-FOR-LATER WORK
72
+ IS A RULE VIOLATION, NOT A CONVENIENCE.
73
+ ```
74
+
75
+ When closure check fires (`count_open == 0` and `count_deferred > 0`), agent MUST:
76
+
77
+ 1. Enumerate every `[~]` step (phase + step text + any inline `<!-- deferred: ... -->` annotation).
78
+ 2. Present numbered options (per [`user-interaction`](user-interaction.md)) — at minimum:
79
+ 1. **Follow-up roadmap (draft)** — spawn `agents/roadmaps/road-to-<slug>.md` with `status: draft`, `parent_roadmap: <this-slug>`, deferred steps lifted verbatim into phases. Draft hidden from dashboard until flipped to `ready`.
80
+ 2. **Follow-up roadmap (ready, blocked)** — spawn with `status: ready` (default), `parent_roadmap: <this-slug>`, plus body note `> Blocked until <condition>`. Dashboard surfaces it; execution waits.
81
+ 3. **Keep in this archive** — confirm deferred items stay searchable in archived file; no follow-up. Records explicit decision-to-drop in same reply.
82
+ 4. **Restore selected items to `[ ]`** — finish them in this roadmap before archive.
83
+ 5. **Convert selected items to `[-]` cancelled** — drop with rationale recorded inline.
84
+ 3. Only after user resolves deferrals does `git mv` to `archive/` run. Dashboard regen happens after resolution.
85
+
86
+ Migration mechanics (file naming, frontmatter, body shape, parent back-link) live in [`roadmap-management § Spawn follow-up from deferred items`](../skills/roadmap-management/SKILL.md). Rule owns obligation; skill owns procedure.
56
87
 
57
88
  ## Pre-send self-check — MANDATORY
58
89
 
@@ -66,9 +97,12 @@ Before sending any reply that landed roadmap work:
66
97
  - `phase_boundary` → only when this reply closes the phase or run.
67
98
  - Any file-shape touch (rename / phase add / archive) → yes, regardless of cadence.
68
99
  If yes and not run yet → run `./agent-config roadmap:progress`, then continue.
69
- 4. Did `count_open` reach 0? If yes → `git mv` to `archive/` and regen again — same reply.
100
+ 4. Did `count_open` reach 0?
101
+ - **No** → continue normally.
102
+ - **Yes + `count_deferred == 0`** → `git mv` to `archive/` and regen again — same reply.
103
+ - **Yes + `count_deferred > 0`** → STOP. Run Iron Law 3 deferred-resolution flow (surface items + numbered options + wait). Archive only after resolution.
70
104
 
71
- Any "no" at step 2 → reply is incomplete. Do not send. A skipped step 3 regen is fine when cadence permits — checkbox truth lives in the markdown file.
105
+ Any "no" at step 2 → reply is incomplete. Do not send. Skipped step 3 regen fine when cadence permits — checkbox truth lives in markdown file. Skipping deferred-resolution gate at step 4 is **never** acceptable; it is the canonical "lost-information" failure mode this rule exists to prevent.
72
106
 
73
107
  Long-form mechanics (failure-mode catalog, Copilot fallback, `[~]` vs `[ ]` semantics, hook + CI defence-in-depth) live in `guideline:agent-infra/roadmap-progress-mechanics`.
74
108
  Trigger-set above activates this routing under the `balanced` and `full` profiles.
@@ -16,6 +16,13 @@ Checkbox states:
16
16
  Percentage = done / (done + open). Deferred and cancelled do not count towards
17
17
  "open" (they are explicit decisions).
18
18
 
19
+ `[~]` deferred items carry plans the user intends to revisit later. They
20
+ block silent auto-archive per `roadmap-progress-sync` Iron Law 3: a
21
+ roadmap with `count_open == 0` and `count_deferred > 0` is reported
22
+ separately (`pending_iron_law_3`) and the user must resolve the
23
+ deferrals (spawn follow-up roadmap, restore, or convert to cancelled)
24
+ before the file moves to `archive/`.
25
+
19
26
  Roadmap visibility is binary:
20
27
 
21
28
  - No `status:` frontmatter (or `status: ready`) → executable, listed.
@@ -245,18 +252,38 @@ def collect(roadmap_root: Path) -> list[RoadmapStats]:
245
252
 
246
253
 
247
254
  def unarchived_complete(roadmaps: list[RoadmapStats]) -> list[RoadmapStats]:
248
- # A roadmap is complete when every active checkbox is done and at least
249
- # one active checkbox exists. The `roadmap-progress-sync` rule mandates
250
- # that such a roadmap be moved to `agents/roadmaps/archive/` in the
251
- # same response that closes its last open item; `collect()` already
252
- # excludes that directory, so anything left here is unarchived.
253
- return [r for r in roadmaps if r.total_active > 0 and r.open_ == 0]
255
+ # A roadmap is complete-and-clean when every active checkbox is done,
256
+ # at least one active checkbox exists, AND no `[~]` deferred items
257
+ # remain. The `roadmap-progress-sync` rule mandates that such a
258
+ # roadmap be moved to `agents/roadmaps/archive/` in the same response
259
+ # that closes its last open item; `collect()` already excludes that
260
+ # directory, so anything left here is unarchived.
261
+ #
262
+ # Deferred items are intentionally excluded — they block silent
263
+ # archive per Iron Law 3 (see `pending_iron_law_3` below).
264
+ return [
265
+ r for r in roadmaps
266
+ if r.total_active > 0 and r.open_ == 0 and r.deferred == 0
267
+ ]
268
+
269
+
270
+ def pending_iron_law_3(roadmaps: list[RoadmapStats]) -> list[RoadmapStats]:
271
+ # Roadmaps with no open work but unresolved `[~]` deferred items.
272
+ # Per `roadmap-progress-sync` Iron Law 3 the agent must NOT auto-
273
+ # archive these — surface the deferred items and ask the user
274
+ # (spawn follow-up, restore, or convert). The dashboard merely
275
+ # reports the state; the obligation lives in the rule.
276
+ return [
277
+ r for r in roadmaps
278
+ if r.total_active > 0 and r.open_ == 0 and r.deferred > 0
279
+ ]
254
280
 
255
281
 
256
282
  def render(roadmaps: list[RoadmapStats]) -> str:
257
283
  total_done = sum(r.done for r in roadmaps)
258
284
  total_active = sum(r.total_active for r in roadmaps)
259
285
  overall_pct = round(total_done * 100 / total_active) if total_active else 0
286
+ pending = pending_iron_law_3(roadmaps)
260
287
  lines: list[str] = []
261
288
  lines.append("# Roadmap Progress\n")
262
289
  header_meta = (
@@ -274,6 +301,21 @@ def render(roadmaps: list[RoadmapStats]) -> str:
274
301
  lines.append("## Overall\n")
275
302
  lines.append(f"**{total_done} / {total_active} steps done · {overall_pct}%**\n")
276
303
  lines.append("```text\n" + bar(overall_pct, 40) + f" {overall_pct}%\n```\n")
304
+ if pending:
305
+ lines.append("## ⚠️ Iron Law 3 — unresolved deferred items\n")
306
+ lines.append(
307
+ "These roadmaps have `count_open == 0` but carry `[~]` deferred "
308
+ "items. Per `roadmap-progress-sync` Iron Law 3 they do NOT "
309
+ "auto-archive — the user must resolve the deferrals first "
310
+ "(spawn follow-up, restore, or cancel). See "
311
+ "[`roadmap-management § 4b`](../packages/core/.agent-src.uncondensed/skills/roadmap-management/SKILL.md).\n"
312
+ )
313
+ lines.append("| Roadmap | Done | Deferred | Cancelled |")
314
+ lines.append("|---|---:|---:|---:|")
315
+ for r in pending:
316
+ lines.append(f"| [{r.rel}](roadmaps/{r.rel}) | {r.done} | "
317
+ f"{r.deferred} | {r.cancelled} |")
318
+ lines.append("")
277
319
  if not roadmaps:
278
320
  lines.append("_No open roadmaps._\n")
279
321
  return "\n".join(lines) + "\n"
@@ -326,6 +368,7 @@ def main() -> int:
326
368
  new_text = render(roadmaps)
327
369
  current = target.read_text(encoding="utf-8") if target.exists() else ""
328
370
  complete = unarchived_complete(roadmaps)
371
+ pending = pending_iron_law_3(roadmaps)
329
372
  if args.check:
330
373
  stale = current != new_text
331
374
  if stale:
@@ -340,7 +383,14 @@ def main() -> int:
340
383
  for r in complete:
341
384
  print(f" - {r.rel} ({r.done}/{r.total_active} done)",
342
385
  file=sys.stderr)
343
- if stale or complete:
386
+ if pending:
387
+ print("❌ Iron Law 3 — roadmaps with unresolved `[~]` deferred "
388
+ "items must NOT auto-archive. Resolve via `roadmap-management § 4b` "
389
+ "(spawn follow-up, restore, or cancel):", file=sys.stderr)
390
+ for r in pending:
391
+ print(f" - {r.rel} ({r.done}/{r.total_active} done · "
392
+ f"{r.deferred} deferred)", file=sys.stderr)
393
+ if stale or complete or pending:
344
394
  return 1
345
395
  print(f"✅ {target.relative_to(args.repo_root)} is up to date.")
346
396
  return 0
@@ -353,6 +403,12 @@ def main() -> int:
353
403
  "`agents/roadmaps/archive/`:", file=sys.stderr)
354
404
  for r in complete:
355
405
  print(f" - {r.rel}", file=sys.stderr)
406
+ if pending:
407
+ print("⚠️ Iron Law 3 — roadmaps with unresolved `[~]` deferred items. "
408
+ "Surface them and ask the user (`roadmap-management § 4b`) "
409
+ "before any archive:", file=sys.stderr)
410
+ for r in pending:
411
+ print(f" - {r.rel} ({r.deferred} deferred)", file=sys.stderr)
356
412
  return 0
357
413
 
358
414
 
@@ -80,10 +80,11 @@ output — surface it as-is. The two flows are mutually exclusive at the
80
80
  state-file level: one `.work-state.json` carries one envelope at a
81
81
  time, and the engine refuses to switch mid-flight.
82
82
 
83
- A sibling subcommand `./agent-config migrate-state` upgrades a legacy
84
- `.implement-ticket-state.json` file to the v1 `.work-state.json`
85
- schema. The wrapper invokes it automatically when the legacy file is
86
- detected; agents should not bypass the dispatcher.
83
+ Unified `./agent-config migrate` sweeps a legacy
84
+ `.implement-ticket-state.json` file into the v1 `.work-state.json`
85
+ schema as one cleanup step (see `docs/contracts/migrate-command.md`).
86
+ Wrapper invokes it automatically when legacy file is detected;
87
+ agents should not bypass the dispatcher.
87
88
 
88
89
  ## GitHub API: Replying to PR review comments
89
90
 
@@ -238,45 +238,145 @@ After the last step of a roadmap is done, check completion status:
238
238
  - `[-]` = cancelled (individual item dropped)
239
239
 
240
240
  3. **Decision rule — `count_open == 0` means the roadmap has no active
241
- work left. `[x]`, `[~]`, `[-]` are all finalized states; only `[ ]`
242
- blocks closure. "Fertig ist fertig" deferred and cancelled items
243
- don't hold a finished roadmap in the active set.**
244
-
245
- | count_x | count_open | count_deferred / cancelled | Action |
246
- |---|---|---|---|
247
- | ≥ 1 | 0 | 0 | **Auto-archive** (silent) — pure completion |
248
- | ≥ 1 | 0 | 1 | **Auto-archive** (silent) — done with intentional skips |
249
- | 0 | 0 | ≥ 1 | **Auto-skip** (silent) — no work happened, scope dropped |
250
- | ≥ 0 | ≥ 1 | ≥ 0 | **Ask the user** open work remains |
241
+ work left. `[x]`, `[-]` are final states. `[~]` deferred items
242
+ block silent closure they carry plans user has not consented to drop
243
+ (enforced by [`roadmap-progress-sync`](../../rules/roadmap-progress-sync.md)
244
+ Iron Law 3).**
245
+
246
+ | count_x | count_open | count_deferred | count_cancelled | Action |
247
+ |---|---|---|---|---|
248
+ | ≥ 1 | 0 | 0 | 0 | **Auto-archive** (silent) — pure completion |
249
+ | ≥ 1 | 0 | 0 | ≥ 1 | **Auto-archive** (silent) — done with explicit drops |
250
+ | ≥ 1 | 0 | ≥ 1 | ≥ 0 | **STOP Iron Law 3 flow.** Surface deferred items, present follow-up options, wait. Step 4b. |
251
+ | 0 | 0 | ≥ 1 | ≥ 0 | **STOP — Iron Law 3 flow.** Scope-drop or deferred-to-later? Same options as 4b. |
252
+ | 0 | 0 | 0 | ≥ 1 | **Auto-skip** (silent) — no work, all cancelled |
253
+ | ≥ 0 | ≥ 1 | ≥ 0 | ≥ 0 | **Ask the user** — open work remains (step 4a) |
251
254
 
252
255
  Show on auto-move:
253
256
 
254
257
  - Archive: `✅ Roadmap archived → agents/roadmaps/archive/{filename}`
255
258
  - Skip: `⏭️ Roadmap skipped → agents/roadmaps/skipped/{filename}`
256
259
 
257
- The deferred/cancelled items remain searchable inside the archived file
258
- (grep for `- [~]` / `- [-]` across `archive/`); a future revival opens a
259
- new roadmap that cites the archived one.
260
+ `[-]` cancelled items remain searchable in archived file — they were
261
+ explicit drops. `[~]` deferred items, by contrast, may not silently
262
+ follow file into archive: they represent work user planned and would
263
+ lose track of. Step 4b is the gate.
260
264
 
261
- 4. **If any items are `[ ]`:** → **Ask the user.** Show what's incomplete:
265
+ 4a. **Open items remain (`count_open 1`)** → **Ask the user.** Show what's incomplete:
262
266
 
263
267
  ```
264
268
  📋 Roadmap completion check:
265
269
 
266
270
  ✅ Completed: {count_x}
267
271
  ⬜ Open: {count_open} — {list of open items, 1 line each}
268
- ⏭️ Deferred: {count_skip} — {list of deferred items, 1 line each}
269
- ❌ Cancelled: {count_cancel} — {list of cancelled items, 1 line each}
272
+ ⏭️ Deferred: {count_deferred} — {list of deferred items, 1 line each}
273
+ ❌ Cancelled: {count_cancelled} — {list of cancelled items, 1 line each}
270
274
 
271
275
  > 1. Archive — mark open items as cancelled [-] and archive now
272
276
  > 2. Keep active — I want to finish the open items
273
- > 3. Mark open items as deferred [~] and archive
277
+ > 3. Mark open items as deferred [~] and archive (triggers Iron Law 3 flow)
274
278
  > 4. Skip — move to skipped/ (no meaningful work done, not pursuing)
275
279
  ```
276
280
 
277
- Option 4 is only appropriate when `count_x == 0` or the completed items were
278
- trivial (e.g. prerequisites only). If the user picks 4 despite meaningful work
279
- being done, confirm once — archive is usually the right choice.
281
+ Option 4 only appropriate when `count_x == 0` or completed items were
282
+ trivial (e.g. prerequisites). If user picks 4 despite meaningful work
283
+ done, confirm once — archive usually right. Picking option 3 does
284
+ NOT archive immediately — converts open → deferred, re-enters the
285
+ `count_deferred > 0` branch, which runs step 4b.
286
+
287
+ 4b. **Deferred items present (`count_deferred ≥ 1`, `count_open == 0`)** — Iron Law 3 flow.
288
+ Archive **blocked** until user resolves deferrals. Surface plan and ask:
289
+
290
+ ```
291
+ 📋 Roadmap closure check — deferred items must resolve before archive:
292
+
293
+ ✅ Completed: {count_x}
294
+ ⏭️ Deferred: {count_deferred}
295
+ {for each deferred item:}
296
+ - Phase {N}: {step text} {<!-- deferred: <annotation> --> if present}
297
+
298
+ These items carry plans you would lose to a silent archive.
299
+
300
+ > 1. Spawn follow-up roadmap as DRAFT
301
+ > → agents/roadmaps/road-to-{auto-slug}.md, status: draft,
302
+ > parent_roadmap: {this-slug}. Hidden from dashboard until you
303
+ > flip status to "ready".
304
+ > 2. Spawn follow-up roadmap as READY (with blocked-until note)
305
+ > → status: ready (default), parent_roadmap: {this-slug}, plus
306
+ > `> Blocked until <condition>` line in body. Visible in
307
+ > dashboard; execution waits on condition.
308
+ > 3. Keep deferred items in this archive — confirm "no follow-up"
309
+ > intentional drop. Items stay searchable in archive/.
310
+ > 4. Restore selected items to [ ] — finish them here before archive.
311
+ > 5. Convert selected items to [-] cancelled — drop with rationale.
312
+ ```
313
+
314
+ Picks 1 or 2 → see "Spawn follow-up from deferred items" below.
315
+ Picks 3, 4, or 5 → apply the change in this roadmap; re-evaluate
316
+ the decision table; archive when gate clears.
317
+
318
+ ### Spawn follow-up from deferred items (procedure)
319
+
320
+ When user picks option 1 or 2 in step 4b:
321
+
322
+ 1. **Derive slug.** Default `<parent-slug>-followup` (e.g. `road-to-x.md`
323
+ → `road-to-x-followup.md`). User-supplied slug in picker → use that.
324
+ Avoid collisions with `agents/roadmaps/` (active + `archive/` + `skipped/`).
325
+
326
+ 2. **Write new file** at `agents/roadmaps/<slug>.md`:
327
+
328
+ ```markdown
329
+ ---
330
+ complexity: lightweight # bump if parent was structural
331
+ status: draft # option 1; omit for option 2 (= ready)
332
+ parent_roadmap: <parent-slug> # back-link to source
333
+ ---
334
+
335
+ # Roadmap: Follow-up to <parent-title>
336
+
337
+ > <One sentence stating carried-over outcome.>
338
+
339
+ ## Context
340
+
341
+ This roadmap collects items deferred from
342
+ [`agents/roadmaps/archive/<parent-slug>.md`](archive/<parent-slug>.md).
343
+ See parent's archive entry for original rationale.
344
+
345
+ ## Prerequisites
346
+
347
+ - [ ] Read `AGENTS.md` and parent archive entry.
348
+ {parent prerequisites still relevant, copied verbatim}
349
+
350
+ <!-- Option 2 only — body note, NOT a frontmatter key: -->
351
+ > Blocked until <condition>. Execution starts when condition clears.
352
+
353
+ ## Phase 1: <name carried from parent>
354
+
355
+ - [ ] {deferred step text, copied verbatim with parent-phase pointer}
356
+ {repeat per deferred item, regrouped by parent phase}
357
+
358
+ ## Acceptance Criteria
359
+
360
+ - [ ] {restate or adjust per deferred scope}
361
+ - [ ] All quality gates pass — see `quality-tools`.
362
+ ```
363
+
364
+ 3. **In parent roadmap** (still in working tree), append a line at
365
+ bottom (above any final `---`):
366
+
367
+ ```
368
+ <!-- Deferred items migrated to agents/roadmaps/<followup-slug>.md on YYYY-MM-DD -->
369
+ ```
370
+
371
+ Do **not** delete `[~]` lines — keep visible in archived parent so
372
+ trail stays grep-able. Follow-up carries forward executable copy.
373
+
374
+ 4. **Regenerate dashboard.** Follow-up appears (draft hidden, ready
375
+ visible) and parent — once moved — drops off.
376
+
377
+ 5. **Archive parent** (`git mv` → `archive/`) and regen one more time
378
+ per [`roadmap-progress-sync`](../../rules/roadmap-progress-sync.md)
379
+ Iron Laws 1 + 3.
280
380
 
281
381
  5. **Move the file** with `git mv` so history is preserved:
282
382
 
@@ -351,7 +451,7 @@ The dashboard is a **read-only snapshot**. Do not edit it by hand — regenerate
351
451
  - Roadmap files go in `agents/roadmaps/` — don't create them in other directories.
352
452
  - Don't mark phases complete without running verification (tests, quality checks) — the verify-before-complete rule applies.
353
453
  - The model tends to skip phases it deems "simple" — every phase must be explicitly completed.
354
- - Auto-archive only when ALL checkboxes are `[x]`. Even one `[~]` or `[-]` requires user confirmation.
454
+ - Auto-archive is allowed when `count_open == 0` AND `count_deferred == 0`. `[-]` cancelled items archive silently (explicit drops). `[~]` deferred items **block** silent archive — they trigger the Iron Law 3 flow (see step 4b).
355
455
  - `archive/` and `skipped/` are distinct — `archive/` = work happened, `skipped/` = no meaningful work, not pursuing. Create either directory if it doesn't exist.
356
456
  - Use `git mv` (not `mv`) so history follows the file.
357
457
 
@@ -99,6 +99,69 @@ phase is done) and **rollback** (what to revert if the phase fails).
99
99
  A phase without exit criteria is open-ended; a phase without
100
100
  rollback assumes success.
101
101
 
102
+ ### 6. Step-marker semantics — pick `[~]` (defer) vs `[-]` (cancel) honestly
103
+
104
+ Difference carries load when authoring (and especially when rewriting
105
+ mid-flight):
106
+
107
+ | Glyph | Semantic | When to use |
108
+ |---|---|---|
109
+ | `[~]` | **deferred** — planned, will be done, just not in this roadmap | Scope-cut + clear intent to revisit. Triggers Iron Law 3 follow-up flow before archive — info preservation enforced. |
110
+ | `[-]` | **cancelled** — won't be done at all | Scope rejected, design changed, replaced by another roadmap. Decision final; no follow-up implied. |
111
+
112
+ Optional inline annotations on same line:
113
+
114
+ ```markdown
115
+ - [~] Migrate bulk-import job to chunked dispatch. <!-- deferred: ops capacity in Q3 -->
116
+ - [-] Wire SQS retry topic. <!-- cancelled: superseded by Lambda DLQ in road-to-event-bridge -->
117
+ ```
118
+
119
+ Annotation for next human reader (and for migration procedure when
120
+ [`roadmap-management`](../roadmap-management/SKILL.md) spawns a follow-up).
121
+ Bare `[~]` / `[-]` allowed; annotated preferred.
122
+
123
+ ### 7. Follow-up roadmaps spawn from deferred items — frontmatter shape
124
+
125
+ When parent roadmap closes with `[~]` items,
126
+ [`roadmap-management`](../roadmap-management/SKILL.md) skill spawns a
127
+ follow-up. Authors and reviewers must recognise the shape:
128
+
129
+ ```markdown
130
+ ---
131
+ complexity: lightweight
132
+ status: draft # optional — draft hides from dashboard
133
+ parent_roadmap: <parent-slug> # back-link to archived source
134
+ ---
135
+
136
+ # Roadmap: Follow-up to <parent-title>
137
+
138
+ > <One sentence: carried-over outcome.>
139
+
140
+ ## Context
141
+
142
+ This roadmap collects items deferred from
143
+ [`agents/roadmaps/archive/<parent-slug>.md`](../archive/<parent-slug>.md).
144
+ { … original phases preserved verbatim … }
145
+
146
+ <!-- For option 2 (ready + blocked), add as body note, NOT in frontmatter: -->
147
+ > Blocked until <condition>. Execution starts when condition clears.
148
+ ```
149
+
150
+ Two states author picks between (mirrors Iron Law 3 numbered-options
151
+ block in [`roadmap-progress-sync`](../../rules/roadmap-progress-sync.md)):
152
+
153
+ - **`status: draft`** → hidden from `agents/roadmaps-progress.md` until
154
+ flipped. Use for items user wants captured but not surfaced to
155
+ active backlog yet.
156
+ - **`status: ready` (default; omit key)** plus body `> Blocked until …`
157
+ note → visible in dashboard, execution gated by documented
158
+ condition. Blocking is body convention, not enforced by dashboard
159
+ generator — readers honor the note.
160
+
161
+ Follow-up is **not** authored from scratch — deferred steps copied
162
+ verbatim (with phase context). Preserves plan exactly as author
163
+ originally wrote it.
164
+
102
165
  ## Output format
103
166
 
104
167
  A single Markdown file at `agents/roadmaps/{name}.md`: