@event4u/agent-config 1.17.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 (158) 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 +28 -0
  24. package/.agent-src/rules/direct-answers.md +18 -26
  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 +15 -21
  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 +33 -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 +49 -0
  47. package/.agent-src/rules/role-mode-adherence.md +2 -2
  48. package/.agent-src/rules/rule-type-governance.md +29 -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 +12 -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 +97 -0
  77. package/README.md +20 -20
  78. package/config/agent-settings.template.yml +23 -0
  79. package/docs/architecture.md +1 -1
  80. package/docs/catalog.md +5 -2
  81. package/docs/contracts/adr-settings-sync-engine.md +127 -0
  82. package/docs/contracts/decision-trace-v1.md +146 -0
  83. package/docs/contracts/file-ownership-matrix.json +7 -0
  84. package/docs/contracts/hook-architecture-v1.md +213 -0
  85. package/docs/contracts/load-context-budget-model.md +80 -0
  86. package/docs/contracts/load-context-schema.md +20 -0
  87. package/docs/contracts/memory-visibility-v1.md +138 -0
  88. package/docs/contracts/one-off-script-lifecycle.md +109 -0
  89. package/docs/contracts/roadmap-complexity-standard.md +137 -0
  90. package/docs/contracts/rule-interactions.yml +22 -0
  91. package/docs/customization.md +1 -0
  92. package/docs/development.md +4 -1
  93. package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +134 -0
  94. package/docs/guidelines/agent-infra/direct-answers-demos.md +145 -0
  95. package/docs/guidelines/agent-infra/layered-settings.md +32 -13
  96. package/docs/guidelines/agent-infra/verify-before-complete-demos.md +128 -0
  97. package/package.json +1 -1
  98. package/scripts/agent-config +64 -0
  99. package/scripts/ai_council/bundler.py +3 -3
  100. package/scripts/ai_council/clients.py +24 -8
  101. package/scripts/ai_council/one_off_archive/2026-05/README.md +67 -0
  102. package/scripts/ai_council/one_off_archive/2026-05/_one_off_budget_v2_audit.py +206 -0
  103. package/scripts/ai_council/{_one_off_roundtrip.py → one_off_archive/2026-05/_one_off_roundtrip.py} +13 -8
  104. package/scripts/ai_council/one_off_archive/2026-05/_one_off_tier_retrofit.py +180 -0
  105. package/scripts/ai_council/session.py +92 -0
  106. package/scripts/build_rule_trigger_matrix.py +360 -0
  107. package/scripts/capture_showcase_session.py +361 -0
  108. package/scripts/chat_history.py +11 -1
  109. package/scripts/check_always_budget.py +46 -2
  110. package/scripts/check_one_off_location.py +81 -0
  111. package/scripts/check_references.py +6 -0
  112. package/scripts/compress.py +5 -2
  113. package/scripts/context_hygiene_hook.py +181 -0
  114. package/scripts/council_cli.py +357 -0
  115. package/scripts/hook_manifest.yaml +184 -0
  116. package/scripts/hooks/__init__.py +1 -0
  117. package/scripts/hooks/augment-context-hygiene.sh +55 -0
  118. package/scripts/hooks/augment-dispatcher.sh +72 -0
  119. package/scripts/hooks/augment-onboarding-gate.sh +55 -0
  120. package/scripts/hooks/cline-dispatcher.sh +86 -0
  121. package/scripts/hooks/cursor-dispatcher.sh +76 -0
  122. package/scripts/hooks/dispatch_hook.py +348 -0
  123. package/scripts/hooks/envelope.py +98 -0
  124. package/scripts/hooks/gemini-dispatcher.sh +117 -0
  125. package/scripts/hooks/state_io.py +122 -0
  126. package/scripts/hooks/windsurf-dispatcher.sh +123 -0
  127. package/scripts/hooks_status.py +146 -0
  128. package/scripts/install.py +728 -51
  129. package/scripts/install.sh +1 -1
  130. package/scripts/lint_examples.py +98 -0
  131. package/scripts/lint_hook_manifest.py +216 -0
  132. package/scripts/lint_one_off_age.py +184 -0
  133. package/scripts/lint_roadmap_complexity.py +127 -0
  134. package/scripts/lint_rule_tiers.py +78 -0
  135. package/scripts/lint_showcase_sessions.py +148 -0
  136. package/scripts/minimal_safe_diff_hook.py +245 -0
  137. package/scripts/onboarding_gate_hook.py +142 -0
  138. package/scripts/readme_linter.py +12 -3
  139. package/scripts/roadmap_progress_hook.py +5 -0
  140. package/scripts/schemas/rule.schema.json +5 -0
  141. package/scripts/sync_agent_settings.py +32 -129
  142. package/scripts/sync_yaml_rt.py +734 -0
  143. package/scripts/verify_before_complete_hook.py +216 -0
  144. /package/scripts/ai_council/{_one_off_2a4_acceptance.py → one_off_archive/2026-05/_one_off_2a4_acceptance.py} +0 -0
  145. /package/scripts/ai_council/{_one_off_context_layer_v1_estimate.py → one_off_archive/2026-05/_one_off_context_layer_v1_estimate.py} +0 -0
  146. /package/scripts/ai_council/{_one_off_context_layer_v1_review.py → one_off_archive/2026-05/_one_off_context_layer_v1_review.py} +0 -0
  147. /package/scripts/ai_council/{_one_off_followups_review.py → one_off_archive/2026-05/_one_off_followups_review.py} +0 -0
  148. /package/scripts/ai_council/{_one_off_nondestructive_inline_audit.py → one_off_archive/2026-05/_one_off_nondestructive_inline_audit.py} +0 -0
  149. /package/scripts/{_one_off_phase4_dispatch_latency.py → ai_council/one_off_archive/2026-05/_one_off_phase4_dispatch_latency.py} +0 -0
  150. /package/scripts/{_one_off_phase6_trigger_jaccard.py → ai_council/one_off_archive/2026-05/_one_off_phase6_trigger_jaccard.py} +0 -0
  151. /package/scripts/ai_council/{_one_off_phase_2a_budget_rebalance.py → one_off_archive/2026-05/_one_off_phase_2a_budget_rebalance.py} +0 -0
  152. /package/scripts/ai_council/{_one_off_phase_2a_post_revert.py → one_off_archive/2026-05/_one_off_phase_2a_post_revert.py} +0 -0
  153. /package/scripts/ai_council/{_one_off_rebalancing_audit.py → one_off_archive/2026-05/_one_off_rebalancing_audit.py} +0 -0
  154. /package/scripts/ai_council/{_one_off_rule_hardening_v1.py → one_off_archive/2026-05/_one_off_rule_hardening_v1.py} +0 -0
  155. /package/scripts/ai_council/{_one_off_structural_open_questions.py → one_off_archive/2026-05/_one_off_structural_open_questions.py} +0 -0
  156. /package/scripts/ai_council/{_one_off_structural_optimization.py → one_off_archive/2026-05/_one_off_structural_optimization.py} +0 -0
  157. /package/scripts/ai_council/{_one_off_structural_v3_gaps.py → one_off_archive/2026-05/_one_off_structural_v3_gaps.py} +0 -0
  158. /package/scripts/ai_council/{_one_off_structural_v3_review.py → one_off_archive/2026-05/_one_off_structural_v3_review.py} +0 -0
@@ -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.
@@ -65,6 +65,86 @@ extraction pattern (Q1) and the **one-rule + three-contexts** consolidation
65
65
  (Q2); model (b) literal is the accounting that makes both patterns
66
66
  honest about their cost.
67
67
 
68
+ ## Load order (Q1) — file order in frontmatter
69
+
70
+ Locked by Phase 1.2 of `road-to-context-layer-maturity` (council
71
+ session `2026-05-03T17-56-21Z`, v2 lock).
72
+
73
+ When a rule declares multiple `load_context:` and / or
74
+ `load_context_eager:` entries, the agent processes them in the order
75
+ they appear in the YAML list, top to bottom. `load_context_eager:`
76
+ entries are concatenated into the active context in declaration
77
+ order; `load_context:` entries are available for lazy retrieval in
78
+ the same order, surfaced first-listed-first when the rule body cites
79
+ them in prose.
80
+
81
+ **Rejected alternatives:**
82
+
83
+ - *Citation order in prose* — non-machine-readable; would force every
84
+ rule to embed a sort hint and the linter to parse rule body text.
85
+ - *Priority field per entry* — adds frontmatter surface area without
86
+ observable benefit. The current rule with the most contexts
87
+ (`autonomous-execution`, 3 contexts) reads each in declaration
88
+ order without ambiguity.
89
+
90
+ This contract treats list order as the canonical signal. Authors
91
+ must order their `load_context:` entries from "load this first" to
92
+ "load this last" so prose citations and frontmatter agree.
93
+
94
+ ## Per-rule context-count cap (Q2) — ≤ 3 contexts per rule
95
+
96
+ Locked by Phase 1.3 of `road-to-context-layer-maturity` (council
97
+ session `2026-05-03T17-56-21Z`, v2 lock). Enforced by
98
+ `scripts/check_always_budget.py` as `MAX_CONTEXTS_PER_RULE = 3`.
99
+
100
+ A rule's combined count of `load_context:` + `load_context_eager:`
101
+ top-level entries must not exceed **3**. The cap is on *declared*
102
+ entries (depth 1 from the rule), not transitive closure — depth-2
103
+ context citations are governed by the depth-2 nesting cap below.
104
+
105
+ **Rationale:**
106
+
107
+ - Empirical max in the current rule set is 3
108
+ (`autonomous-execution`); the cap locks the ceiling without forcing
109
+ any rewrite.
110
+ - A 4th declared context is the structural signal that the rule
111
+ should split (one rule, one obligation surface), not load more.
112
+ - Cross-platform mechanical: a count check is O(N), no semantic
113
+ analysis, identical observable on Augment and Claude Code.
114
+
115
+ The cap applies to **all rule types** (`always` and `auto`) — Q2 is
116
+ a structural constraint on rule shape, not a budget concern.
117
+
118
+ ## Cross-rule sharing (Q3) — Model (b) literal locked
119
+
120
+ Locked by Phase 1.4 of `road-to-context-layer-maturity` (decision
121
+ 3a). The accounting model in § The locked model above stands without
122
+ shared-context discount.
123
+
124
+ **Decision:** when context X is loaded by N rules, each rule pays
125
+ the full `RawSize(X)` under Model (b). No `chars(X) / N` discount.
126
+
127
+ **Rationale (3a over 3b):**
128
+
129
+ - The linter is correct as-is — `RECOVERY_BAND_ENABLED` plus the
130
+ per-rule allowlist is already a working ratchet to drive total
131
+ utilization below 100 %.
132
+ - 3b would require a linter rewrite plus a new failure-mode test
133
+ family (cross-rule attribution, divisor-stability invariants).
134
+ Phase 1.4a's 4-hour / 200-LOC feasibility cap was structured to
135
+ reject 3b on cost; with no current shared-context patterns
136
+ exceeding 1 loader per context (verified Phase 1.1 inventory), 3b
137
+ has no measurable upside.
138
+ - Phase 4c (shared-context discount) becomes a no-op under 3a.
139
+ Phase 4 leverage shifts to 4a (demote), 4b (merge), and 4d
140
+ (hard-compress) — see `road-to-context-layer-maturity` Phase 4
141
+ inputs gate.
142
+
143
+ **Reopener:** if a future inventory shows ≥ 3 shared-context loaders
144
+ and total utilization re-enters the 95–100 % zone after Phase 4
145
+ completes, run the Phase 1.4a feasibility spike and propose 3b via
146
+ contract version bump.
147
+
68
148
  ## Nesting cap — depth 2
69
149
 
70
150
  A rule's `load_context:` may cite a context (depth 1). A context may
@@ -85,6 +85,26 @@ Single-rule contexts that don't fit one of the canonical topics live
85
85
  directly under `contexts/` (see `contexts/model-recommendations.md`,
86
86
  `contexts/skills-and-commands.md`).
87
87
 
88
+ ## Per-rule context-count cap (Q2)
89
+
90
+ A rule's combined count of `load_context:` + `load_context_eager:`
91
+ top-level entries MUST be ≤ **3**. Enforced by
92
+ `scripts/check_always_budget.py` (`MAX_CONTEXTS_PER_RULE`); see
93
+ [`load-context-budget-model.md § Per-rule context-count cap (Q2)`](load-context-budget-model.md#per-rule-context-count-cap-q2--3-contexts-per-rule)
94
+ for the locking contract.
95
+
96
+ A 4th context is the structural signal that the rule should split,
97
+ not load more. Authors hitting the cap should extract a sibling rule
98
+ with its own obligation surface, not stretch the existing one.
99
+
100
+ ## Load order
101
+
102
+ Entries load in **frontmatter list order**, top-to-bottom. Author
103
+ your list in "first read" order so prose citations and frontmatter
104
+ agree. See
105
+ [`load-context-budget-model.md § Load order (Q1)`](load-context-budget-model.md#load-order-q1--file-order-in-frontmatter)
106
+ for the locking rationale.
107
+
88
108
  ## Combined char-budget guard
89
109
 
90
110
  `load_context_eager:` triggers a budget check:
@@ -0,0 +1,138 @@
1
+ ---
2
+ stability: beta
3
+ ---
4
+
5
+ # Memory-visibility v1
6
+
7
+ **Purpose.** Pin the format of the user-facing visibility line that
8
+ every memory-using `/work` and `/implement-ticket` run prints, so the
9
+ user can tell what the agent retrieved and what it ignored.
10
+ Complements [`agent-memory-contract.md`](agent-memory-contract.md):
11
+ that doc describes the **CLI surface and backend states**; this doc
12
+ describes the **operator-facing surface** the engine emits per turn.
13
+
14
+ **Scope.** Defines the line shape, the privacy floor, the opt-out
15
+ toggle, and the interaction with the chat-history heartbeat. Does
16
+ **not** define how memory entries are scored or routed — that is the
17
+ sibling agent-memory package.
18
+
19
+ Last refreshed: 2026-05-04.
20
+
21
+ ## Line shape
22
+
23
+ A single one-line ASCII record, prefixed with the memory icon `🧠`
24
+ and a single space:
25
+
26
+ ```
27
+ 🧠 Memory: <hits>/<asks> · ids=[<comma-separated-ids>]
28
+ ```
29
+
30
+ Examples:
31
+
32
+ ```
33
+ 🧠 Memory: 3/4 · ids=[mem_42, mem_57, mem_91]
34
+ 🧠 Memory: 0/2 · ids=[]
35
+ 🧠 Memory: 5/5 · ids=[mem_a01, mem_a02, mem_a03, …+2]
36
+ ```
37
+
38
+ Cap at 5 ids inline; remainder rendered as `…+N`. The full id list
39
+ lives in the decision-trace JSON
40
+ ([`decision-trace-v1.md`](decision-trace-v1.md)).
41
+
42
+ ## Field semantics
43
+
44
+ | Field | Meaning |
45
+ |---|---|
46
+ | `hits` | Count of `memory_retrieve_*` calls during this turn that returned ≥ 1 entry. |
47
+ | `asks` | Count of `memory_retrieve_*` calls during this turn — both successful and empty. |
48
+ | `ids` | Stable memory entry ids returned across all calls, deduped, ordered by retrieval timestamp. |
49
+
50
+ `hits ≤ asks` is invariant. If `asks == 0`, the engine MUST suppress
51
+ the line entirely — no `0/0` noise.
52
+
53
+ ## Privacy floor
54
+
55
+ The visibility line and the JSON it derives from MUST NOT contain:
56
+
57
+ - Entry **bodies**, summaries, or quoted snippets.
58
+ - Secrets, tokens, environment values, or paths outside the
59
+ package's `agents/state/` and `tests/` allowlist.
60
+ - User identifiers beyond what is already public in the working
61
+ directory's `.agent-settings.yml` (e.g. developer name).
62
+
63
+ The privacy floor is enforced by
64
+ `tests/contracts/test_memory_visibility_redaction.py` — any new
65
+ content path that ships memory output adds a fixture there.
66
+
67
+ ## Opt-out
68
+
69
+ On by default whenever memory is asked at all in a turn. Users can
70
+ suppress the visibility line via:
71
+
72
+ ```yaml
73
+ memory:
74
+ visibility: off
75
+ ```
76
+
77
+ Off-mode does not silence the underlying memory calls; it only stops
78
+ the line from rendering. The decision-trace JSON still records the
79
+ counts and ids for downstream metrics.
80
+
81
+ ## Interaction with chat-history heartbeat
82
+
83
+ The chat-history heartbeat (`📒` marker) and the memory-visibility
84
+ line are **independent**:
85
+
86
+ - Heartbeat fires on cadence boundaries
87
+ (`per_turn` / `per_phase` / `per_tool`).
88
+ - Visibility line fires whenever `asks ≥ 1` for the current turn,
89
+ regardless of cadence.
90
+ - Both render on the same reply when both fire — heartbeat first,
91
+ visibility line second, separated by a single newline.
92
+
93
+ The visibility line is **not** part of the heartbeat payload — that
94
+ keeps the heartbeat contract bytes-stable.
95
+
96
+ ## Cadence interaction
97
+
98
+ | Cost profile | Visibility line | Heartbeat |
99
+ |---|---|---|
100
+ | `lean` | suppress unless `asks ≥ 3` | per-phase |
101
+ | `standard` | always when `asks ≥ 1` | per-turn |
102
+ | `verbose` | always when `asks ≥ 1` | per-tool |
103
+
104
+ Cost-profile lookup respects `.agent-settings.yml`'s `cost_profile`
105
+ key. Default is `standard`.
106
+
107
+ ## Audit-as-memory feed
108
+
109
+ The visibility output produced by the engine is the input to the
110
+ audit-as-memory pipeline (consumed by the sibling distribution +
111
+ adoption work). Concretely:
112
+
113
+ - The engine emits the line + the underlying counts to the
114
+ decision-trace JSON.
115
+ - A consumer hook reads `agents/state/work/<work-id>/decision-trace-*.json`,
116
+ rolls counts up to the session level, and feeds the result back
117
+ into the agent-memory store as an audit entry.
118
+
119
+ This contract pins the **producer** side. The audit-feed consumer
120
+ lives outside the package's stable surface and must read this
121
+ contract before parsing.
122
+
123
+ ## Stability
124
+
125
+ Beta. Breaking changes between v1 and v2 are allowed in a minor
126
+ release if the change appears in `CHANGELOG.md` under a `### Breaking`
127
+ heading. Engines MUST gate on the visibility line shape — clients
128
+ parsing the stream MUST treat unknown trailing fields as forward-
129
+ compat extensions.
130
+
131
+ ## Cross-references
132
+
133
+ - CLI surface and backend states:
134
+ [`agent-memory-contract.md`](agent-memory-contract.md).
135
+ - Decision-trace JSON consumes the same counts:
136
+ [`decision-trace-v1.md`](decision-trace-v1.md).
137
+ - Privacy regression test path:
138
+ `tests/contracts/test_memory_visibility_redaction.py`.