@event4u/agent-config 1.18.0 → 1.19.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 (126) hide show
  1. package/.agent-src/commands/council/default.md +74 -76
  2. package/.agent-src/commands/feature/roadmap.md +22 -0
  3. package/.agent-src/commands/roadmap/create.md +38 -6
  4. package/.agent-src/commands/roadmap/execute.md +36 -9
  5. package/.agent-src/rules/agent-authority.md +1 -0
  6. package/.agent-src/rules/agent-docs.md +1 -0
  7. package/.agent-src/rules/analysis-skill-routing.md +1 -0
  8. package/.agent-src/rules/architecture.md +1 -0
  9. package/.agent-src/rules/artifact-drafting-protocol.md +1 -0
  10. package/.agent-src/rules/artifact-engagement-recording.md +1 -0
  11. package/.agent-src/rules/ask-when-uncertain.md +1 -0
  12. package/.agent-src/rules/augment-portability.md +1 -0
  13. package/.agent-src/rules/augment-source-of-truth.md +1 -0
  14. package/.agent-src/rules/autonomous-execution.md +1 -0
  15. package/.agent-src/rules/capture-learnings.md +1 -0
  16. package/.agent-src/rules/chat-history-cadence.md +34 -0
  17. package/.agent-src/rules/chat-history-ownership.md +1 -0
  18. package/.agent-src/rules/chat-history-visibility.md +1 -0
  19. package/.agent-src/rules/cli-output-handling.md +2 -2
  20. package/.agent-src/rules/command-suggestion-policy.md +1 -0
  21. package/.agent-src/rules/commit-conventions.md +1 -0
  22. package/.agent-src/rules/commit-policy.md +1 -0
  23. package/.agent-src/rules/context-hygiene.md +22 -0
  24. package/.agent-src/rules/direct-answers.md +1 -0
  25. package/.agent-src/rules/docker-commands.md +1 -0
  26. package/.agent-src/rules/docs-sync.md +1 -0
  27. package/.agent-src/rules/downstream-changes.md +1 -0
  28. package/.agent-src/rules/e2e-testing.md +1 -0
  29. package/.agent-src/rules/guidelines.md +1 -0
  30. package/.agent-src/rules/improve-before-implement.md +1 -0
  31. package/.agent-src/rules/language-and-tone.md +1 -0
  32. package/.agent-src/rules/laravel-translations.md +1 -0
  33. package/.agent-src/rules/markdown-safe-codeblocks.md +1 -0
  34. package/.agent-src/rules/minimal-safe-diff.md +1 -0
  35. package/.agent-src/rules/missing-tool-handling.md +1 -0
  36. package/.agent-src/rules/model-recommendation.md +1 -0
  37. package/.agent-src/rules/no-cheap-questions.md +1 -0
  38. package/.agent-src/rules/no-roadmap-references.md +1 -0
  39. package/.agent-src/rules/non-destructive-by-default.md +1 -0
  40. package/.agent-src/rules/onboarding-gate.md +26 -0
  41. package/.agent-src/rules/package-ci-checks.md +1 -0
  42. package/.agent-src/rules/php-coding.md +1 -0
  43. package/.agent-src/rules/preservation-guard.md +1 -0
  44. package/.agent-src/rules/review-routing-awareness.md +1 -0
  45. package/.agent-src/rules/reviewer-awareness.md +1 -0
  46. package/.agent-src/rules/roadmap-progress-sync.md +22 -0
  47. package/.agent-src/rules/role-mode-adherence.md +2 -2
  48. package/.agent-src/rules/rule-type-governance.md +1 -0
  49. package/.agent-src/rules/runtime-safety.md +1 -0
  50. package/.agent-src/rules/scope-control.md +1 -0
  51. package/.agent-src/rules/security-sensitive-stop.md +1 -0
  52. package/.agent-src/rules/size-enforcement.md +1 -0
  53. package/.agent-src/rules/skill-improvement-trigger.md +1 -0
  54. package/.agent-src/rules/skill-quality.md +1 -0
  55. package/.agent-src/rules/slash-command-routing-policy.md +39 -0
  56. package/.agent-src/rules/think-before-action.md +1 -0
  57. package/.agent-src/rules/token-efficiency.md +1 -0
  58. package/.agent-src/rules/tool-safety.md +1 -0
  59. package/.agent-src/rules/ui-audit-gate.md +1 -0
  60. package/.agent-src/rules/upstream-proposal.md +1 -0
  61. package/.agent-src/rules/user-interaction.md +1 -0
  62. package/.agent-src/rules/verify-before-complete.md +1 -0
  63. package/.agent-src/skills/roadmap-management/SKILL.md +29 -4
  64. package/.agent-src/skills/verify-completion-evidence/SKILL.md +8 -1
  65. package/.agent-src/templates/agent-settings.md +16 -0
  66. package/.agent-src/templates/roadmaps.md +8 -3
  67. package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +9 -0
  68. package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +4 -0
  69. package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +4 -0
  70. package/.agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.py +163 -0
  71. package/.agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.py +111 -0
  72. package/.agent-src/templates/scripts/work_engine/hooks/settings.py +36 -0
  73. package/.agent-src/templates/scripts/work_engine/scoring/decision_trace.py +141 -0
  74. package/.agent-src/templates/scripts/work_engine/scoring/memory_visibility.py +125 -0
  75. package/.claude-plugin/marketplace.json +1 -1
  76. package/CHANGELOG.md +62 -0
  77. package/README.md +19 -19
  78. package/config/agent-settings.template.yml +23 -0
  79. package/docs/catalog.md +5 -2
  80. package/docs/contracts/adr-settings-sync-engine.md +127 -0
  81. package/docs/contracts/decision-trace-v1.md +146 -0
  82. package/docs/contracts/file-ownership-matrix.json +7 -0
  83. package/docs/contracts/hook-architecture-v1.md +213 -0
  84. package/docs/contracts/memory-visibility-v1.md +138 -0
  85. package/docs/contracts/one-off-script-lifecycle.md +109 -0
  86. package/docs/contracts/rule-interactions.yml +22 -0
  87. package/docs/customization.md +1 -0
  88. package/docs/development.md +4 -1
  89. package/docs/guidelines/agent-infra/layered-settings.md +32 -13
  90. package/package.json +1 -1
  91. package/scripts/agent-config +44 -0
  92. package/scripts/ai_council/bundler.py +3 -3
  93. package/scripts/ai_council/clients.py +24 -8
  94. package/scripts/ai_council/one_off_archive/2026-05/README.md +22 -0
  95. package/scripts/ai_council/one_off_archive/2026-05/_one_off_roundtrip.py +13 -8
  96. package/scripts/ai_council/one_off_archive/2026-05/_one_off_tier_retrofit.py +180 -0
  97. package/scripts/ai_council/session.py +92 -0
  98. package/scripts/capture_showcase_session.py +361 -0
  99. package/scripts/chat_history.py +11 -1
  100. package/scripts/check_always_budget.py +7 -2
  101. package/scripts/context_hygiene_hook.py +14 -6
  102. package/scripts/council_cli.py +357 -0
  103. package/scripts/hook_manifest.yaml +184 -0
  104. package/scripts/hooks/__init__.py +1 -0
  105. package/scripts/hooks/augment-dispatcher.sh +72 -0
  106. package/scripts/hooks/cline-dispatcher.sh +86 -0
  107. package/scripts/hooks/cursor-dispatcher.sh +76 -0
  108. package/scripts/hooks/dispatch_hook.py +348 -0
  109. package/scripts/hooks/envelope.py +98 -0
  110. package/scripts/hooks/gemini-dispatcher.sh +117 -0
  111. package/scripts/hooks/state_io.py +122 -0
  112. package/scripts/hooks/windsurf-dispatcher.sh +123 -0
  113. package/scripts/hooks_status.py +146 -0
  114. package/scripts/install.py +725 -87
  115. package/scripts/install.sh +1 -1
  116. package/scripts/lint_hook_manifest.py +216 -0
  117. package/scripts/lint_one_off_age.py +184 -0
  118. package/scripts/lint_rule_tiers.py +78 -0
  119. package/scripts/lint_showcase_sessions.py +148 -0
  120. package/scripts/minimal_safe_diff_hook.py +245 -0
  121. package/scripts/onboarding_gate_hook.py +13 -8
  122. package/scripts/readme_linter.py +12 -3
  123. package/scripts/roadmap_progress_hook.py +5 -0
  124. package/scripts/sync_agent_settings.py +32 -129
  125. package/scripts/sync_yaml_rt.py +734 -0
  126. package/scripts/verify_before_complete_hook.py +216 -0
package/README.md CHANGED
@@ -14,13 +14,15 @@ Give your AI agents an audit-disciplined orchestration contract — testing, Git
14
14
 
15
15
  ## Start here
16
16
 
17
- New to agent-config? 60 seconds, three links:
17
+ Three ways in, depending on what you're doing today:
18
18
 
19
- 1. **[Install](#quickstart)** `composer require` or `npm install`, then run the installer.
20
- 2. **[First command](#2-minute-demo-implement-ticket)** — `/implement-ticket` or `/work` walkthrough.
21
- 3. **[Where the rules live](#documentation)** `.augment/`, `.claude/`, `.cursor/`, and friends.
19
+ | Path | Audience | What it does |
20
+ |---|---|---|
21
+ | **[`/onboard`](.agent-src/commands/onboard.md)** | New user, fresh install | Captures name, IDE, rtk, and cost profile; sets `onboarding.onboarded=true` |
22
+ | **[`task ci`](docs/development.md#ci--verification)** | Contributor working **on** this package | Runs the full sync + lint + test pipeline; must be green before push |
23
+ | **[`task generate-tools`](docs/development.md#tool-generation)** | Multi-agent user / consumer project | Rebuilds `.claude/`, `.cursor/`, `.clinerules/`, `.windsurfrules` from the source |
22
24
 
23
- ---
25
+ If none of those apply yet — start with the [Quickstart](#quickstart) and pick a path once it's installed.
24
26
 
25
27
  ## Quickstart
26
28
 
@@ -63,6 +65,14 @@ project-locally for all supported AI tools. Task is required for
63
65
  *contributors* who want to rebuild the compressed content locally — see
64
66
  [CONTRIBUTING.md](CONTRIBUTING.md).
65
67
 
68
+ **Verify hook coverage** after installing — every supported platform
69
+ (Augment, Claude, Cursor, Cline, Windsurf, Gemini CLI, Copilot fallback)
70
+ is wired through one universal dispatcher per
71
+ [`hook-architecture-v1`](docs/contracts/hook-architecture-v1.md). Run
72
+ `./agent-config hooks:status` for the matrix (`--strict` for CI,
73
+ `--format json` for tooling). The installer also dry-fires the
74
+ dispatcher per bridge as a post-install smoke test (skip: `--no-smoke`).
75
+
66
76
  ### For individual use (optional)
67
77
 
68
78
  Install directly in your agent for global, cross-project use:
@@ -479,20 +489,10 @@ task lint-skills # Lint skills, rules, commands
479
489
 
480
490
  ## Requirements
481
491
 
482
- **To install the package into a consumer project:**
483
-
484
- - **Bash** — primary installer is `scripts/install`, orchestrating
485
- `scripts/install.sh` (payload sync) and `scripts/install.py` (bridges).
486
- Available on macOS, Linux, and WSL.
487
- - **Python 3.10+** — required for the bridge stage only. Pre-installed
488
- on macOS 12.3+ and all major Linux distros. If missing, the
489
- orchestrator skips bridges and completes the payload sync.
490
- - **Composer or npm** — to pull the package itself.
491
-
492
- **Platform support:** macOS 12.3+, Linux (modern distros), and Windows
493
- (WSL2) are fully supported. Git Bash works but symlinks require
494
- Developer Mode; native PowerShell / cmd is not supported. Contributors
495
- rebuilding `.augment/` locally also need [Task](https://taskfile.dev/).
492
+ - **Bash** — `scripts/install` orchestrates payload sync (`install.sh`) and bridges (`install.py`).
493
+ - **Python 3.10+** — bridge stage only; missing → orchestrator skips bridges.
494
+ - **Composer or npm** — to pull the package.
495
+ - **Platform:** macOS 12.3+, Linux, WSL2. Git Bash needs Developer Mode for symlinks; native PowerShell / cmd unsupported. Contributors rebuilding `.augment/` also need [Task](https://taskfile.dev/).
496
496
 
497
497
  ## License
498
498
 
@@ -121,6 +121,24 @@ pipelines:
121
121
  # want a silent agent; `custom` profile ignores this default entirely.
122
122
  skill_improvement: true
123
123
 
124
+ # --- Roadmap execution ---
125
+ #
126
+ # Controls when /roadmap execute runs the project's quality pipeline
127
+ # (the `task ci` / `make test` / `npm run check` style command).
128
+ # Steps are ALWAYS marked `[x]` and the dashboard is ALWAYS regenerated
129
+ # in the same response — that cadence is governed by `roadmap-progress-sync`
130
+ # and is non-negotiable. This setting only governs *quality tool runs*.
131
+ roadmap:
132
+ # When to run quality tools during /roadmap execute.
133
+ # end_of_roadmap = once, before archiving the completed roadmap (default — fastest, fewest tokens)
134
+ # per_phase = once after every completed phase
135
+ # per_step = after every completed step (legacy verbose; highest token cost)
136
+ # Trade-off: end_of_roadmap saves tokens and time but lets errors compound
137
+ # across phases. Pick per_phase if the work is risky or unfamiliar.
138
+ # Iron Law `verify-before-complete` still applies — fresh quality output
139
+ # is mandatory before any "roadmap complete" claim regardless of cadence.
140
+ quality_cadence: end_of_roadmap
141
+
124
142
  # --- Subagent orchestration ---
125
143
  #
126
144
  # Controls model selection and parallelism for subagent-based workflows
@@ -195,6 +213,11 @@ ai_council:
195
213
  max_calls: 10
196
214
  max_total_usd: 0.50
197
215
 
216
+ # Retention for `agents/council-sessions/` audit folders. Sessions
217
+ # older than this are pruned automatically on the next `save()`.
218
+ # `0` disables pruning (keep forever — disk grows unbounded).
219
+ session_retention_days: 14
220
+
198
221
  # --- Onboarding ---
199
222
  #
200
223
  # Tracks whether the initial setup flow (/onboard) has been completed
package/docs/catalog.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # agent-config — Public Catalog
2
2
 
3
- Consumer-facing catalog of all **327 public artefacts** shipped by
3
+ Consumer-facing catalog of all **330 public artefacts** shipped by
4
4
  this package. Internal package-maintenance rules and deprecation shims
5
5
  are excluded.
6
6
 
@@ -301,14 +301,16 @@ are excluded.
301
301
  | command | [`upstream-contribute`](../.agent-src/commands/upstream-contribute.md) | | Contribute a learning, skill, rule, or fix from a consumer project back to the shared agent-config package |
302
302
  | command | [`work`](../.agent-src/commands/work.md) | | Drive a free-form prompt end-to-end through refine → score → plan → implement → test → verify → report — Option-A loop over the `work_engine` Python engine, confidence-band gated, no auto-git. |
303
303
 
304
- ## Guidelines (48)
304
+ ## Guidelines (51)
305
305
 
306
306
  | kind | name | category | description |
307
307
  |---|---|---|---|
308
308
  | guideline | [`agent-interaction-and-decision-quality`](../docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md) | agent-infra | |
309
+ | guideline | [`ask-when-uncertain-demos`](../docs/guidelines/agent-infra/ask-when-uncertain-demos.md) | agent-infra | |
309
310
  | guideline | [`asking-and-brevity-examples`](../docs/guidelines/agent-infra/asking-and-brevity-examples.md) | agent-infra | |
310
311
  | guideline | [`break-glass-usage`](../docs/guidelines/agent-infra/break-glass-usage.md) | agent-infra | |
311
312
  | guideline | [`developer-judgment`](../docs/guidelines/agent-infra/developer-judgment.md) | agent-infra | |
313
+ | guideline | [`direct-answers-demos`](../docs/guidelines/agent-infra/direct-answers-demos.md) | agent-infra | |
312
314
  | guideline | [`engineering-memory-data-format`](../docs/guidelines/agent-infra/engineering-memory-data-format.md) | agent-infra | |
313
315
  | guideline | [`language-and-tone-examples`](../docs/guidelines/agent-infra/language-and-tone-examples.md) | agent-infra | |
314
316
  | guideline | [`layered-settings`](../docs/guidelines/agent-infra/layered-settings.md) | agent-infra | |
@@ -322,6 +324,7 @@ are excluded.
322
324
  | guideline | [`self-improvement-pipeline`](../docs/guidelines/agent-infra/self-improvement-pipeline.md) | agent-infra | |
323
325
  | guideline | [`size-and-scope`](../docs/guidelines/agent-infra/size-and-scope.md) | agent-infra | |
324
326
  | guideline | [`tool-integration`](../docs/guidelines/agent-infra/tool-integration.md) | agent-infra | |
327
+ | guideline | [`verify-before-complete-demos`](../docs/guidelines/agent-infra/verify-before-complete-demos.md) | agent-infra | |
325
328
  | guideline | [`readme-size-and-splitting`](../docs/guidelines/docs/readme-size-and-splitting.md) | docs | |
326
329
  | guideline | [`playwright`](../docs/guidelines/e2e/playwright.md) | e2e | |
327
330
  | guideline | [`api-design`](../docs/guidelines/php/api-design.md) | php | |
@@ -0,0 +1,127 @@
1
+ ---
2
+ stability: beta
3
+ ---
4
+
5
+ # ADR — Settings sync engine: stdlib-only round-trip
6
+
7
+ > **Status:** Decided · 2026-05-04
8
+ > **Context:** Additive Settings Sync roadmap — review round 2
9
+ > (Self-Review + AI Council, Anthropic + OpenAI) flagged the
10
+ > 735-line `scripts/sync_yaml_rt.py` as potential NIH burden vs.
11
+ > adopting `ruamel.yaml` as a third-party dependency.
12
+ > **Builds on:** [`docs/guidelines/agent-infra/layered-settings.md`](../guidelines/agent-infra/layered-settings.md)
13
+ > § Sync rules — defines the additive-merge-with-user-line-preservation
14
+ > contract this engine implements.
15
+
16
+ ## Decision
17
+
18
+ `.agent-settings.yml` synchronization uses a **custom, stdlib-only
19
+ round-trip parser + emitter** in [`scripts/sync_yaml_rt.py`](../../scripts/sync_yaml_rt.py),
20
+ not `ruamel.yaml` or any other third-party YAML library.
21
+
22
+ The engine implements a narrow YAML subset (block-mappings, scalars,
23
+ flow-list values, comments, CRLF/LF) that covers the full surface of
24
+ `.agent-settings.yml` plus its template (`config/agent-settings.template.yml`).
25
+ Out-of-subset YAML — anchors, aliases, multi-document streams, complex
26
+ keys, nested flow mappings — is **not supported** and raises `ValueError`.
27
+
28
+ The merge layer (additive walk, max-index insertion, scalar→section
29
+ guard, healer for legacy `_user._user.foo` corruption, EOL
30
+ normalization) sits on top of the parser and is custom regardless of
31
+ the parser choice.
32
+
33
+ ## Why this was a real question
34
+
35
+ Three options were on the table:
36
+
37
+ 1. **`ruamel.yaml` for parse + emit, custom merge on top.** Rejected.
38
+ 2. **`PyYAML` for parse, custom emitter for round-trip.** Rejected
39
+ earlier in the roadmap — PyYAML's parser drops comments and
40
+ formatting before the merger ever sees them.
41
+ 3. **Custom stdlib-only parser + emitter + merger.** Chosen.
42
+
43
+ ### Why ruamel.yaml does not match the contract
44
+
45
+ The driving requirement from `layered-settings.md` is **verbatim
46
+ user-line preservation** — every byte of every line in the user's
47
+ file is preserved unless that line carries a key the merger is
48
+ explicitly editing. Tests pin this contract by asserting byte-identity
49
+ across two consecutive sync runs (`test_user_block_round_trip_is_idempotent`,
50
+ `test_three_level_idempotent`).
51
+
52
+ `ruamel.yaml` is a round-trip-aware library, not a verbatim one — it
53
+ re-parses into an in-memory model and **re-emits** through its own
54
+ emitter. This re-emit normalizes:
55
+
56
+ | Behavior | ruamel.yaml | Custom engine |
57
+ |---|---|---|
58
+ | User-line bytes (whitespace, quoting, blanks) | re-emitted, may shift | preserved 1:1 |
59
+ | Mixed CRLF/LF in user file | normalized to one EOL (typically platform default) | detected + normalized to the user's predominant EOL |
60
+ | `personal: null` blocking template-section injection | requires custom merge logic regardless | scalar guard in `_merge_into` |
61
+ | Legacy `_user._user.foo.bar` healer (one-off migration) | requires custom logic regardless | `heal_user_block` |
62
+ | Synthetic header rendering for newly-inserted template keys | re-emits the entire file | only renders the new subtree |
63
+ | Unknown user blocks at top level | preserved as data, but indent / quoting may shift on emit | preserved verbatim |
64
+ | 3rd-party dependency in distribution package | +1 (`ruamel.yaml` + transitive `ruamel.yaml.clib`) | 0 |
65
+
66
+ The rows where the libraries diverge are exactly the rows the test
67
+ suite asserts on.
68
+
69
+ ### Cost analysis
70
+
71
+ | Axis | Custom engine | ruamel.yaml |
72
+ |---|---|---|
73
+ | Lines of code | 735 (engine) + 335 (merge/heal) = 1070 | ~400 (parser+emitter saved) + 335 (merge/heal stays) + adapter glue = ~735 |
74
+ | Net code saved | — | ~335 lines |
75
+ | 3rd-party deps | 0 | +2 (`ruamel.yaml`, `ruamel.yaml.clib`) |
76
+ | Runtime YAML surface | narrow (documented subset) | full YAML 1.2 |
77
+ | Verbatim guarantee | yes | no |
78
+ | Performance | irrelevant — cold-path, runs on profile change | irrelevant |
79
+
80
+ The 335-line saving is real but offset by a stronger contract (verbatim)
81
+ and a 0-dep posture. The package is a distribution-layer library
82
+ (`composer.json` `type: library`, `package.json` thin manifest); it
83
+ already restricts itself to stdlib for portability across consumer
84
+ projects, several of which lock Python deps tightly.
85
+
86
+ ## Consequences
87
+
88
+ - **Maintenance:** the engine must keep covering the YAML subset its
89
+ template + user files exercise. Any new template feature (e.g. a
90
+ block-style nested list) is a parser change, not a config change.
91
+ - **Error surface:** YAML outside the subset (anchors, complex keys)
92
+ surfaces as a friendly `ValueError` from `_rt.sync()`, caught by
93
+ `sync_agent_settings.main` and turned into exit code 2 with a
94
+ user-readable message. Documented in
95
+ `tests/test_sync_agent_settings.py::test_malformed_user_yaml_exits_2_with_message`.
96
+ - **Test debt:** `tests/test_sync_round_trip.py` (34 tests) and
97
+ `tests/test_sync_agent_settings.py` (15 tests) are the contract.
98
+ Any parser change must keep those green and is the entry point
99
+ for new fixtures under `tests/fixtures/sync_yaml_rt/`.
100
+
101
+ ## Revisit triggers
102
+
103
+ This decision is revisited (new ADR with successor link) when **any**
104
+ of the following holds:
105
+
106
+ 1. `.agent-settings.yml` schema gains a YAML feature outside the
107
+ supported subset (anchors, multi-doc, complex keys, nested flow
108
+ mappings) — the cost of extending the parser exceeds the cost of
109
+ adopting ruamel.
110
+ 2. The verbatim-preservation contract is relaxed (e.g. consumers
111
+ accept that sync can re-format) — the driver for the custom engine
112
+ is gone.
113
+ 3. The 0-dep posture for Python tooling is dropped at the package level
114
+ — the marginal cost of one more dep collapses.
115
+ 4. A maintenance bug surfaces in the engine that would have been
116
+ prevented by ruamel's mature spec coverage.
117
+
118
+ ## See also
119
+
120
+ - [`docs/guidelines/agent-infra/layered-settings.md`](../guidelines/agent-infra/layered-settings.md)
121
+ § Sync rules — the contract this engine implements.
122
+ - [`scripts/sync_yaml_rt.py`](../../scripts/sync_yaml_rt.py) module
123
+ docstring — the supported YAML subset, listed exhaustively.
124
+ - `tests/test_sync_round_trip.py` — verbatim, scalar-guard, healer,
125
+ CRLF, and synthetic-header pinning.
126
+ - `tests/test_sync_agent_settings.py` — CLI integration, profile
127
+ override, malformed-input exit code.
@@ -0,0 +1,146 @@
1
+ ---
2
+ stability: beta
3
+ ---
4
+
5
+ # Decision-trace v1
6
+
7
+ **Purpose.** Pin the JSON shape that `/implement-ticket` and `/work`
8
+ runs emit when the user opts into trace surfacing. The trace is the
9
+ audit substrate for the rule-interaction matrix
10
+ ([`rule-interactions.md`](rule-interactions.md)) and feeds the
11
+ showcase-session capture pipeline
12
+ ([`outcome-baseline.md`](../../agents/contexts/outcome-baseline.md)).
13
+
14
+ **Scope.** Defines the JSON envelope written next to a `WorkState`
15
+ file when `decision_engine.surface_traces: true`. Does **not**
16
+ specify how individual rules detect their own activation — that is
17
+ the rule's own responsibility — only the shape of the report.
18
+
19
+ Last refreshed: 2026-05-04.
20
+
21
+ ## Opt-in
22
+
23
+ Off by default. Toggled in `.agent-settings.yml`:
24
+
25
+ ```yaml
26
+ decision_engine:
27
+ surface_traces: true
28
+ ```
29
+
30
+ The `/work` and `/implement-ticket` engines check this flag at phase
31
+ boundaries and emit one trace file per phase when set.
32
+
33
+ ## File location
34
+
35
+ ```
36
+ agents/state/work/<work-id>/decision-trace-<phase>.json
37
+ ```
38
+
39
+ `work-id` matches the `WorkState` directory; `phase` is one of
40
+ `refine`, `memory`, `analyze`, `plan`, `implement`, `test`, `verify`,
41
+ `report`. Files are gitignored.
42
+
43
+ ## Envelope shape
44
+
45
+ ```json
46
+ {
47
+ "schema_version": 1,
48
+ "work_id": "ABCD-1234-2026-05-04T12-34-56Z",
49
+ "phase": "implement",
50
+ "started_at": "2026-05-04T12:35:01Z",
51
+ "ended_at": "2026-05-04T12:38:42Z",
52
+ "confidence_band": "high",
53
+ "risk_class": "low",
54
+ "rules": [
55
+ {
56
+ "rule_id": "verify-before-complete",
57
+ "applied": true,
58
+ "skipped": false,
59
+ "conflicted_with": [],
60
+ "evidence_refs": ["agents/state/work/.../verify.log"]
61
+ }
62
+ ],
63
+ "memory": {
64
+ "asks": 3,
65
+ "hits": 2,
66
+ "ids": ["mem_42", "mem_57"]
67
+ },
68
+ "verify": {
69
+ "claims": 1,
70
+ "first_try_passes": 1
71
+ }
72
+ }
73
+ ```
74
+
75
+ Concerns and engines MUST treat unknown top-level keys as forward-
76
+ compat extensions and MUST NOT raise on them.
77
+
78
+ ## Field semantics
79
+
80
+ | Field | Meaning |
81
+ |---|---|
82
+ | `schema_version` | Always `1` for this contract. Major bump on breaking changes. |
83
+ | `work_id` | Stable id of the WorkState directory. Allows cross-trace correlation across phases. |
84
+ | `phase` | Engine phase that produced the trace. One of the eight phases above. |
85
+ | `confidence_band` | One of `low` / `medium` / `high`. Heuristic defined below — derived from memory hits + ambiguity flags + verify evidence count. |
86
+ | `risk_class` | One of `low` / `medium` / `high`. Per [`rule-interactions.md`](rule-interactions.md) — drives reviewer routing. |
87
+ | `rules[].rule_id` | Stable rule id, matches the filename under `.agent-src.uncompressed/rules/` minus `.md`. |
88
+ | `rules[].applied` | True if the rule's Iron Law fired and changed engine behaviour this phase. |
89
+ | `rules[].skipped` | True if the rule was checked but produced no effect (no trigger match). |
90
+ | `rules[].conflicted_with` | List of rule_ids that fired against this one. Reduction handled per `rule-interactions.md`. |
91
+ | `rules[].evidence_refs` | Optional list of paths under `agents/state/` or `tests/` that back the `applied` claim. |
92
+ | `memory.asks` | Count of `memory_retrieve` calls during the phase. |
93
+ | `memory.hits` | Count of calls that returned ≥ 1 result. |
94
+ | `memory.ids` | Stable memory entry ids returned. Bounded to ≤ 32 ids per phase; remainder dropped silently. |
95
+ | `verify.claims` | Count of "done"-class claims the engine attempted this phase. |
96
+ | `verify.first_try_passes` | Count of those claims that passed the verify gate without a re-prompt. |
97
+
98
+ ## Confidence-band heuristic
99
+
100
+ ```
101
+ high: memory.hits ≥ 2 AND verify.first_try_passes == verify.claims AND no ambiguity flag
102
+ medium: memory.hits ≥ 1 OR verify.first_try_passes ≥ 1
103
+ low: otherwise
104
+ ```
105
+
106
+ Edge case: `verify.claims == 0` is not "high" by default — it folds
107
+ into "medium" if at least one memory hit landed, "low" otherwise.
108
+
109
+ ## Risk-class heuristic
110
+
111
+ Mirrors [`file-ownership-matrix.json`](file-ownership-matrix.json):
112
+ the trace inherits the **maximum** risk class across all files the
113
+ phase touched. If no files were touched (pure planning phase), risk
114
+ is `low`.
115
+
116
+ ## Privacy floor
117
+
118
+ - `memory.ids` carries opaque ids only — no entry bodies, no secrets.
119
+ - `evidence_refs` carries paths only — no file contents.
120
+ - `rules[].rule_id` is stable id, not free-form text.
121
+
122
+ The visibility line surfaced to the user (Phase 4) consumes this file
123
+ under the same floor.
124
+
125
+ ## Stability
126
+
127
+ Beta. Breaking changes between v1 and v2 are allowed in a minor
128
+ release if the change appears in `CHANGELOG.md` under a `### Breaking`
129
+ heading. Engines MUST gate on `schema_version` and refuse unknown
130
+ majors.
131
+
132
+ ## Cross-references
133
+
134
+ - Personas (Architect, Risk-Officer) live in the package's persona
135
+ library under [`.agent-src.uncompressed/personas/`](../../.agent-src.uncompressed/personas/).
136
+ This contract does not duplicate them — when a future trace consumer
137
+ attributes a decision to one of those personas, the persona file is
138
+ the source of truth, not this envelope.
139
+ - Rule-interaction matrix:
140
+ [`rule-interactions.md`](rule-interactions.md) (machine-readable
141
+ source: [`rule-interactions.yml`](rule-interactions.yml)).
142
+ - Confidence-band heuristic is implemented in
143
+ `work_engine/scoring/decision_trace.py` and exercised by
144
+ `tests/work_engine/scoring/test_decision_trace_scoring.py`.
145
+ - Outcome metrics consume `verify.first_try_passes`:
146
+ [`outcome-baseline.md`](../../agents/contexts/outcome-baseline.md).
@@ -6249,6 +6249,13 @@
6249
6249
  "via": "body_link",
6250
6250
  "depth": 1
6251
6251
  },
6252
+ {
6253
+ "source": ".agent-src.uncompressed/skills/roadmap-management/SKILL.md",
6254
+ "target": ".agent-src.uncompressed/commands/roadmap/create.md",
6255
+ "type": "READ_ONLY",
6256
+ "via": "body_link",
6257
+ "depth": 1
6258
+ },
6252
6259
  {
6253
6260
  "source": ".agent-src.uncompressed/skills/roadmap-management/SKILL.md",
6254
6261
  "target": ".agent-src.uncompressed/rules/roadmap-progress-sync.md",
@@ -0,0 +1,213 @@
1
+ ---
2
+ stability: beta
3
+ ---
4
+
5
+ # Hook architecture v1
6
+
7
+ **Purpose.** Pin the contract that the universal hook dispatcher
8
+ implements, so concern scripts and per-platform trampolines can be
9
+ written, tested, and refactored against a stable surface.
10
+
11
+ **Scope.** Defines the dispatcher's stdin/stdout shape, exit-code
12
+ semantics, the `hook_manifest.yaml` schema, the concurrency contract
13
+ for `agents/state/` writes, and the Copilot fallback pattern. Does
14
+ **not** specify per-platform install paths — those live in
15
+ [`chat-history-platform-hooks.md`](../../agents/contexts/chat-history-platform-hooks.md).
16
+
17
+ Last refreshed: 2026-05-04.
18
+
19
+ ## Vocabulary
20
+
21
+ | Term | Meaning |
22
+ |---|---|
23
+ | **Platform** | Host agent surface — one of `augment`, `claude`, `cursor`, `cline`, `windsurf`, `gemini`, `copilot`. The `claude` value covers both Claude Code (CLI) and Claude.ai Web; the canonical platform identifier is `claude` (matches `chat_history.PLATFORM_EVENT_MAP`). |
24
+ | **Concern** | A single agent-config behaviour wired to one or more lifecycle events — e.g. `chat-history`, `roadmap-progress`, `verify-before-complete`. Lives as a Python script under `scripts/hooks/concerns/<name>.py`. |
25
+ | **Event** | The agent-config-internal event vocabulary the dispatcher exposes — `session_start`, `session_end`, `user_prompt_submit`, `pre_tool_use`, `post_tool_use`, `stop`, `pre_compact`, `agent_error`. Per-platform native names map to these. `agent_error` is synthetic — fired by the agent (or wrapper) when the host crashes outside a concern, so chat-history can checkpoint partial sessions on abnormal exit. (Added in Round 2 — 2026-05-04.) |
26
+ | **Trampoline** | A 5–10 line per-platform shell script that reads the platform's native payload, calls the dispatcher with `--platform <name>`, and forwards the platform's exit-code semantics. |
27
+ | **Dispatcher** | `scripts/hooks/dispatch_hook.py` — single Python entrypoint that reads the manifest, resolves which concerns fire on `(platform, event)`, runs each one with the contract envelope below, and reduces their exit codes. |
28
+
29
+ ## Dispatcher invocation
30
+
31
+ ```
32
+ python3 scripts/hooks/dispatch_hook.py \
33
+ --platform <name> \
34
+ --event <agent-config-event> \
35
+ [--native-event <platform-event>] \
36
+ < platform-payload.json
37
+ ```
38
+
39
+ `--native-event` is informational; the dispatcher does not branch on
40
+ it. The trampoline is responsible for translating the platform's
41
+ native event name to the agent-config vocabulary before invocation.
42
+
43
+ ## Stdin contract — concern envelope
44
+
45
+ The dispatcher writes a single JSON object to each concern's stdin:
46
+
47
+ ```json
48
+ {
49
+ "schema_version": 1,
50
+ "platform": "augment",
51
+ "event": "stop",
52
+ "native_event": "Stop",
53
+ "session_id": "…",
54
+ "workspace_root": "/abs/path",
55
+ "payload": { /* opaque, platform-native */ },
56
+ "settings": { /* materialized .agent-settings.yml subset */ }
57
+ }
58
+ ```
59
+
60
+ Concerns MUST treat unknown top-level keys as forward-compat extensions
61
+ and MUST NOT raise on them. `payload` is passed through verbatim from
62
+ the platform — concerns extract what they need via their own helpers
63
+ (see `scripts/chat_history.py` `_extract_*` for the pattern).
64
+
65
+ ## Stdout contract — concern reply
66
+
67
+ A concern MAY write a single JSON object to stdout. The dispatcher
68
+ reads it; non-JSON or empty stdout is treated as no-op (decision
69
+ inferred from exit code only).
70
+
71
+ ```json
72
+ {
73
+ "decision": "allow" | "block" | "warn",
74
+ "reason": "human-readable, ≤ 200 chars",
75
+ "additional_context": "optional — surfaces back to the model on platforms that support it",
76
+ "state_writes": ["agents/state/chat-history.json", "…"]
77
+ }
78
+ ```
79
+
80
+ `state_writes` is advisory; concerns still write the files themselves
81
+ under the concurrency rules below.
82
+
83
+ ## Exit-code semantics
84
+
85
+ | Code | Meaning | Dispatcher action |
86
+ |---|---|---|
87
+ | `0` | allow | no-op; pass through |
88
+ | `1` | block | dispatcher exits 1, surfaces `reason` to platform's deny channel |
89
+ | `2` | warn | dispatcher exits 0, logs `reason` to stderr, sets `additionalContext` if platform supports it |
90
+ | `≥ 3` | error | dispatcher logs full traceback, exits 0 (fail-open) unless `concerns.<name>.fail_closed: true` in settings |
91
+
92
+ ## Reduction across multiple concerns
93
+
94
+ When a `(platform, event)` tuple maps to ≥ 2 concerns, the dispatcher
95
+ runs them **sequentially** in manifest order and reduces:
96
+
97
+ - Any `block` → final decision is `block` (most-restrictive merge).
98
+ - Else any `warn` → final decision is `warn`.
99
+ - Else `allow`.
100
+
101
+ `additional_context` strings are concatenated with `\n\n` separators,
102
+ in manifest order. Concerns are never run in parallel — concurrency
103
+ guarantees rely on serial state writes.
104
+
105
+ ## Feedback channel — `agents/state/.dispatcher/<session_id>/`
106
+
107
+ Exit-code reduction collapses the severity ladder to a single
108
+ platform-native code, which can hide a `warn` behind a `block` or
109
+ mask non-actioned reasons entirely. To preserve per-concern detail
110
+ without re-routing control flow, the dispatcher writes a feedback
111
+ directory per invocation:
112
+
113
+ ```
114
+ agents/state/.dispatcher/<session_id>/
115
+ <concern>.json — one file per concern that ran
116
+ summary.json — rollup written after the last concern
117
+ ```
118
+
119
+ Each `<concern>.json` carries:
120
+
121
+ ```json
122
+ {
123
+ "concern": "chat-history",
124
+ "exit_code": 0,
125
+ "raw_exit_code": 0,
126
+ "severity": "allow",
127
+ "decision": "allow",
128
+ "reason": "appended turn 12",
129
+ "duration_ms": 47,
130
+ "started_at": "2026-05-04T12:34:56Z",
131
+ "completed_at": "2026-05-04T12:34:56Z",
132
+ "fail_closed": false
133
+ }
134
+ ```
135
+
136
+ `summary.json` carries the platform / event tuple, the reduced
137
+ `final_exit_code` + `final_severity`, and a trimmed list of all
138
+ concern entries. `session_id` falls back to
139
+ `dispatch-<unix_ts>-<pid>` when the envelope omits one. Path
140
+ traversal in `session_id` is collapsed (`/`, `\`, `..` → `_`).
141
+
142
+ Feedback writes are non-fatal — IO errors log to stderr but never
143
+ change the dispatcher's exit code. The directory is gitignored and
144
+ consumed by `task hooks-status` (Phase 7.11). Added in Round 2
145
+ (2026-05-04) per Q1 of `tmp/council_round2/q1_feedback_channel.md`.
146
+
147
+ ## Manifest schema — `scripts/hook_manifest.yaml`
148
+
149
+ ```yaml
150
+ schema_version: 1
151
+ concerns:
152
+ chat-history:
153
+ script: scripts/hooks/concerns/chat_history.py
154
+ fail_closed: false
155
+ roadmap-progress:
156
+ script: scripts/hooks/concerns/roadmap_progress.py
157
+ fail_closed: false
158
+
159
+ platforms:
160
+ augment:
161
+ session_start: [chat-history]
162
+ stop: [chat-history, roadmap-progress]
163
+ post_tool_use: [chat-history]
164
+ claude:
165
+ session_start: [chat-history]
166
+ user_prompt_submit: [chat-history]
167
+ stop: [chat-history, roadmap-progress]
168
+ copilot:
169
+ # No dispatcher — see "Copilot fallback" below.
170
+ ```
171
+
172
+ Validated by `scripts/lint_hook_manifest.py` (Phase 7.10): every
173
+ concern script must exist on disk, every platform key must be a known
174
+ platform, every event key must be in the agent-config event vocabulary.
175
+
176
+ ## Concurrency — atomic state writes
177
+
178
+ Concerns that write under `agents/state/` MUST use the pattern:
179
+
180
+ 1. Acquire `fcntl.flock(LOCK_EX)` on `agents/state/.dispatcher.lock`.
181
+ 2. Write to a sibling `<dest>.tmp.<pid>` file in the same directory.
182
+ 3. `os.replace(tmp, dest)` — POSIX-atomic on the same filesystem.
183
+ 4. Release the lock.
184
+
185
+ The single `.dispatcher.lock` is intentional: serialising state
186
+ writes across concerns is cheaper than per-file locks, and concerns
187
+ already run sequentially within one dispatcher invocation. The lock
188
+ file is gitignored.
189
+
190
+ Phase 7.4 ships a regression test that spawns two concurrent
191
+ dispatcher invocations against the same event and asserts no torn
192
+ writes (file ends with valid JSON, last-writer-wins).
193
+
194
+ ## Copilot fallback pattern
195
+
196
+ Copilot has no hook surface. Concerns whose source rule cites
197
+ `agents/state/<concern>.json` MUST gain a "Copilot fallback" section
198
+ that:
199
+
200
+ - Names the state file the concern would have written.
201
+ - Names a manual command or task that reproduces the side effect
202
+ (e.g. `task chat-history:append`).
203
+ - Includes no Iron-Law-changing prose.
204
+
205
+ The dispatcher silently no-ops when called with `--platform copilot`;
206
+ the fallback is consumed by reading the rule, not by hook invocation.
207
+
208
+ ## Stability
209
+
210
+ Beta. Breaking changes between v1 and v2 are allowed in a minor
211
+ release if the change appears in `CHANGELOG.md` under a `### Breaking`
212
+ heading. Concerns MUST gate on `schema_version` and refuse unknown
213
+ majors.