@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.
- package/.agent-src/commands/council/default.md +74 -76
- package/.agent-src/commands/feature/roadmap.md +22 -0
- package/.agent-src/commands/roadmap/create.md +38 -6
- package/.agent-src/commands/roadmap/execute.md +36 -9
- package/.agent-src/rules/agent-authority.md +1 -0
- package/.agent-src/rules/agent-docs.md +1 -0
- package/.agent-src/rules/analysis-skill-routing.md +1 -0
- package/.agent-src/rules/architecture.md +1 -0
- package/.agent-src/rules/artifact-drafting-protocol.md +1 -0
- package/.agent-src/rules/artifact-engagement-recording.md +1 -0
- package/.agent-src/rules/ask-when-uncertain.md +1 -0
- package/.agent-src/rules/augment-portability.md +1 -0
- package/.agent-src/rules/augment-source-of-truth.md +1 -0
- package/.agent-src/rules/autonomous-execution.md +1 -0
- package/.agent-src/rules/capture-learnings.md +1 -0
- package/.agent-src/rules/chat-history-cadence.md +34 -0
- package/.agent-src/rules/chat-history-ownership.md +1 -0
- package/.agent-src/rules/chat-history-visibility.md +1 -0
- package/.agent-src/rules/cli-output-handling.md +2 -2
- package/.agent-src/rules/command-suggestion-policy.md +1 -0
- package/.agent-src/rules/commit-conventions.md +1 -0
- package/.agent-src/rules/commit-policy.md +1 -0
- package/.agent-src/rules/context-hygiene.md +28 -0
- package/.agent-src/rules/direct-answers.md +18 -26
- package/.agent-src/rules/docker-commands.md +1 -0
- package/.agent-src/rules/docs-sync.md +1 -0
- package/.agent-src/rules/downstream-changes.md +1 -0
- package/.agent-src/rules/e2e-testing.md +1 -0
- package/.agent-src/rules/guidelines.md +1 -0
- package/.agent-src/rules/improve-before-implement.md +1 -0
- package/.agent-src/rules/language-and-tone.md +1 -0
- package/.agent-src/rules/laravel-translations.md +1 -0
- package/.agent-src/rules/markdown-safe-codeblocks.md +1 -0
- package/.agent-src/rules/minimal-safe-diff.md +1 -0
- package/.agent-src/rules/missing-tool-handling.md +1 -0
- package/.agent-src/rules/model-recommendation.md +1 -0
- package/.agent-src/rules/no-cheap-questions.md +15 -21
- package/.agent-src/rules/no-roadmap-references.md +1 -0
- package/.agent-src/rules/non-destructive-by-default.md +1 -0
- package/.agent-src/rules/onboarding-gate.md +33 -0
- package/.agent-src/rules/package-ci-checks.md +1 -0
- package/.agent-src/rules/php-coding.md +1 -0
- package/.agent-src/rules/preservation-guard.md +1 -0
- package/.agent-src/rules/review-routing-awareness.md +1 -0
- package/.agent-src/rules/reviewer-awareness.md +1 -0
- package/.agent-src/rules/roadmap-progress-sync.md +49 -0
- package/.agent-src/rules/role-mode-adherence.md +2 -2
- package/.agent-src/rules/rule-type-governance.md +29 -0
- package/.agent-src/rules/runtime-safety.md +1 -0
- package/.agent-src/rules/scope-control.md +1 -0
- package/.agent-src/rules/security-sensitive-stop.md +1 -0
- package/.agent-src/rules/size-enforcement.md +1 -0
- package/.agent-src/rules/skill-improvement-trigger.md +1 -0
- package/.agent-src/rules/skill-quality.md +1 -0
- package/.agent-src/rules/slash-command-routing-policy.md +39 -0
- package/.agent-src/rules/think-before-action.md +1 -0
- package/.agent-src/rules/token-efficiency.md +1 -0
- package/.agent-src/rules/tool-safety.md +1 -0
- package/.agent-src/rules/ui-audit-gate.md +1 -0
- package/.agent-src/rules/upstream-proposal.md +1 -0
- package/.agent-src/rules/user-interaction.md +1 -0
- package/.agent-src/rules/verify-before-complete.md +1 -0
- package/.agent-src/skills/roadmap-management/SKILL.md +29 -4
- package/.agent-src/skills/verify-completion-evidence/SKILL.md +8 -1
- package/.agent-src/templates/agent-settings.md +16 -0
- package/.agent-src/templates/roadmaps.md +12 -3
- package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +9 -0
- package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +4 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +4 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.py +163 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.py +111 -0
- package/.agent-src/templates/scripts/work_engine/hooks/settings.py +36 -0
- package/.agent-src/templates/scripts/work_engine/scoring/decision_trace.py +141 -0
- package/.agent-src/templates/scripts/work_engine/scoring/memory_visibility.py +125 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +97 -0
- package/README.md +20 -20
- package/config/agent-settings.template.yml +23 -0
- package/docs/architecture.md +1 -1
- package/docs/catalog.md +5 -2
- package/docs/contracts/adr-settings-sync-engine.md +127 -0
- package/docs/contracts/decision-trace-v1.md +146 -0
- package/docs/contracts/file-ownership-matrix.json +7 -0
- package/docs/contracts/hook-architecture-v1.md +213 -0
- package/docs/contracts/load-context-budget-model.md +80 -0
- package/docs/contracts/load-context-schema.md +20 -0
- package/docs/contracts/memory-visibility-v1.md +138 -0
- package/docs/contracts/one-off-script-lifecycle.md +109 -0
- package/docs/contracts/roadmap-complexity-standard.md +137 -0
- package/docs/contracts/rule-interactions.yml +22 -0
- package/docs/customization.md +1 -0
- package/docs/development.md +4 -1
- package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +134 -0
- package/docs/guidelines/agent-infra/direct-answers-demos.md +145 -0
- package/docs/guidelines/agent-infra/layered-settings.md +32 -13
- package/docs/guidelines/agent-infra/verify-before-complete-demos.md +128 -0
- package/package.json +1 -1
- package/scripts/agent-config +64 -0
- package/scripts/ai_council/bundler.py +3 -3
- package/scripts/ai_council/clients.py +24 -8
- package/scripts/ai_council/one_off_archive/2026-05/README.md +67 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_budget_v2_audit.py +206 -0
- package/scripts/ai_council/{_one_off_roundtrip.py → one_off_archive/2026-05/_one_off_roundtrip.py} +13 -8
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_tier_retrofit.py +180 -0
- package/scripts/ai_council/session.py +92 -0
- package/scripts/build_rule_trigger_matrix.py +360 -0
- package/scripts/capture_showcase_session.py +361 -0
- package/scripts/chat_history.py +11 -1
- package/scripts/check_always_budget.py +46 -2
- package/scripts/check_one_off_location.py +81 -0
- package/scripts/check_references.py +6 -0
- package/scripts/compress.py +5 -2
- package/scripts/context_hygiene_hook.py +181 -0
- package/scripts/council_cli.py +357 -0
- package/scripts/hook_manifest.yaml +184 -0
- package/scripts/hooks/__init__.py +1 -0
- package/scripts/hooks/augment-context-hygiene.sh +55 -0
- package/scripts/hooks/augment-dispatcher.sh +72 -0
- package/scripts/hooks/augment-onboarding-gate.sh +55 -0
- package/scripts/hooks/cline-dispatcher.sh +86 -0
- package/scripts/hooks/cursor-dispatcher.sh +76 -0
- package/scripts/hooks/dispatch_hook.py +348 -0
- package/scripts/hooks/envelope.py +98 -0
- package/scripts/hooks/gemini-dispatcher.sh +117 -0
- package/scripts/hooks/state_io.py +122 -0
- package/scripts/hooks/windsurf-dispatcher.sh +123 -0
- package/scripts/hooks_status.py +146 -0
- package/scripts/install.py +728 -51
- package/scripts/install.sh +1 -1
- package/scripts/lint_examples.py +98 -0
- package/scripts/lint_hook_manifest.py +216 -0
- package/scripts/lint_one_off_age.py +184 -0
- package/scripts/lint_roadmap_complexity.py +127 -0
- package/scripts/lint_rule_tiers.py +78 -0
- package/scripts/lint_showcase_sessions.py +148 -0
- package/scripts/minimal_safe_diff_hook.py +245 -0
- package/scripts/onboarding_gate_hook.py +142 -0
- package/scripts/readme_linter.py +12 -3
- package/scripts/roadmap_progress_hook.py +5 -0
- package/scripts/schemas/rule.schema.json +5 -0
- package/scripts/sync_agent_settings.py +32 -129
- package/scripts/sync_yaml_rt.py +734 -0
- package/scripts/verify_before_complete_hook.py +216 -0
- /package/scripts/ai_council/{_one_off_2a4_acceptance.py → one_off_archive/2026-05/_one_off_2a4_acceptance.py} +0 -0
- /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
- /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
- /package/scripts/ai_council/{_one_off_followups_review.py → one_off_archive/2026-05/_one_off_followups_review.py} +0 -0
- /package/scripts/ai_council/{_one_off_nondestructive_inline_audit.py → one_off_archive/2026-05/_one_off_nondestructive_inline_audit.py} +0 -0
- /package/scripts/{_one_off_phase4_dispatch_latency.py → ai_council/one_off_archive/2026-05/_one_off_phase4_dispatch_latency.py} +0 -0
- /package/scripts/{_one_off_phase6_trigger_jaccard.py → ai_council/one_off_archive/2026-05/_one_off_phase6_trigger_jaccard.py} +0 -0
- /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
- /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
- /package/scripts/ai_council/{_one_off_rebalancing_audit.py → one_off_archive/2026-05/_one_off_rebalancing_audit.py} +0 -0
- /package/scripts/ai_council/{_one_off_rule_hardening_v1.py → one_off_archive/2026-05/_one_off_rule_hardening_v1.py} +0 -0
- /package/scripts/ai_council/{_one_off_structural_open_questions.py → one_off_archive/2026-05/_one_off_structural_open_questions.py} +0 -0
- /package/scripts/ai_council/{_one_off_structural_optimization.py → one_off_archive/2026-05/_one_off_structural_optimization.py} +0 -0
- /package/scripts/ai_council/{_one_off_structural_v3_gaps.py → one_off_archive/2026-05/_one_off_structural_v3_gaps.py} +0 -0
- /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`.
|