@event4u/agent-config 1.18.0 → 1.20.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/agent-handoff.md +14 -10
- package/.agent-src/commands/chat-history/import.md +170 -0
- package/.agent-src/commands/chat-history/learn.md +178 -0
- package/.agent-src/commands/chat-history/show.md +17 -18
- package/.agent-src/commands/chat-history.md +26 -25
- package/.agent-src/commands/council/default.md +77 -82
- package/.agent-src/commands/create-pr.md +28 -8
- 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/commands/sync-gitignore.md +1 -1
- package/.agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +76 -0
- package/.agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +3 -3
- package/.agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +5 -12
- 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/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 +22 -0
- package/.agent-src/rules/direct-answers.md +11 -2
- 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 +38 -6
- 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-attribution-footers.md +48 -0
- package/.agent-src/rules/no-cheap-questions.md +1 -0
- package/.agent-src/rules/no-roadmap-references.md +2 -1
- package/.agent-src/rules/non-destructive-by-default.md +1 -0
- package/.agent-src/rules/onboarding-gate.md +26 -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 +22 -0
- package/.agent-src/rules/role-mode-adherence.md +2 -2
- package/.agent-src/rules/rule-type-governance.md +1 -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 +50 -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 +22 -5
- package/.agent-src/rules/verify-before-complete.md +1 -0
- package/.agent-src/skills/ai-council/SKILL.md +4 -5
- package/.agent-src/skills/dcf-modeling/SKILL.md +89 -0
- package/.agent-src/skills/funnel-analysis/SKILL.md +100 -0
- package/.agent-src/skills/md-language-check/SKILL.md +1 -1
- package/.agent-src/skills/okr-tree-modeling/SKILL.md +93 -0
- package/.agent-src/skills/rice-prioritization/SKILL.md +100 -0
- package/.agent-src/skills/roadmap-management/SKILL.md +29 -4
- package/.agent-src/skills/subagent-orchestration/SKILL.md +34 -2
- package/.agent-src/skills/unit-economics-modeling/SKILL.md +104 -0
- package/.agent-src/skills/using-git-worktrees/SKILL.md +1 -0
- package/.agent-src/skills/verify-completion-evidence/SKILL.md +8 -1
- package/.agent-src/templates/agent-settings.md +21 -26
- package/.agent-src/templates/roadmaps.md +8 -3
- package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +16 -5
- package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +4 -4
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +4 -4
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.py +7 -51
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.py +1 -2
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.py +1 -2
- 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 +110 -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/.agent-src/templates/skill.md +30 -1
- package/.claude-plugin/marketplace.json +8 -4
- package/AGENTS.md +44 -3
- package/CHANGELOG.md +173 -0
- package/README.md +22 -22
- package/config/agent-settings.template.yml +42 -13
- package/config/gitignore-block.txt +4 -4
- package/docs/architecture.md +3 -3
- package/docs/catalog.md +18 -13
- package/docs/contracts/adr-chat-history-split.md +10 -1
- package/docs/contracts/adr-settings-sync-engine.md +127 -0
- package/docs/contracts/command-clusters.md +1 -1
- package/docs/contracts/cross-wing-handoff.md +133 -0
- package/docs/contracts/decision-trace-v1.md +146 -0
- package/docs/contracts/file-ownership-matrix.json +348 -126
- package/docs/contracts/hook-architecture-v1.md +220 -0
- package/docs/contracts/memory-visibility-v1.md +122 -0
- package/docs/contracts/one-off-script-lifecycle.md +109 -0
- package/docs/contracts/rule-interactions.yml +22 -0
- package/docs/customization.md +2 -1
- package/docs/development.md +4 -1
- package/docs/getting-started.md +21 -29
- package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +1 -1
- package/docs/guidelines/agent-infra/layered-settings.md +32 -13
- package/docs/hook-payload-capture.md +221 -0
- package/docs/migrations/commands-1.15.0.md +17 -12
- package/docs/skills-catalog.md +5 -4
- package/llms.txt +4 -3
- package/package.json +1 -1
- package/scripts/agent-config +45 -1
- package/scripts/ai_council/_default_prices.py +4 -4
- package/scripts/ai_council/bundler.py +3 -3
- package/scripts/ai_council/clients.py +25 -9
- package/scripts/ai_council/modes.py +3 -4
- package/scripts/ai_council/one_off_archive/2026-05/README.md +22 -0
- package/scripts/ai_council/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/pricing.py +10 -9
- package/scripts/ai_council/session.py +92 -0
- package/scripts/build_rule_trigger_matrix.py +1 -9
- package/scripts/capture_showcase_session.py +361 -0
- package/scripts/chat_history.py +963 -597
- package/scripts/check_always_budget.py +7 -2
- package/scripts/check_references.py +12 -2
- package/scripts/context_hygiene_hook.py +14 -6
- package/scripts/council_cli.py +407 -0
- package/scripts/hook_manifest.yaml +217 -0
- package/scripts/hooks/__init__.py +1 -0
- package/scripts/hooks/augment-chat-history.sh +10 -0
- package/scripts/hooks/augment-dispatcher.sh +72 -0
- package/scripts/hooks/cline-dispatcher.sh +86 -0
- package/scripts/hooks/cowork-dispatcher.sh +98 -0
- package/scripts/hooks/cursor-dispatcher.sh +76 -0
- package/scripts/hooks/dispatch_hook.py +383 -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 +157 -0
- package/scripts/install-hooks.sh +2 -2
- package/scripts/install.py +725 -87
- package/scripts/install.sh +38 -1
- package/scripts/lint_handoffs.py +214 -0
- package/scripts/lint_hook_manifest.py +217 -0
- package/scripts/lint_one_off_age.py +184 -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 +13 -8
- package/scripts/readme_linter.py +12 -3
- package/scripts/redact_hook_capture.py +148 -0
- package/scripts/roadmap_progress_hook.py +5 -0
- package/scripts/schemas/skill.schema.json +5 -0
- package/scripts/skill_linter.py +163 -1
- package/scripts/sync_agent_settings.py +32 -129
- package/scripts/sync_yaml_rt.py +734 -0
- package/scripts/update_prices.py +3 -3
- package/scripts/verify_before_complete_hook.py +216 -0
- package/.agent-src/commands/chat-history/checkpoint.md +0 -126
- package/.agent-src/commands/chat-history/clear.md +0 -103
- package/.agent-src/commands/chat-history/resume.md +0 -183
- package/.agent-src/rules/chat-history-cadence.md +0 -109
- package/.agent-src/rules/chat-history-ownership.md +0 -123
- package/.agent-src/rules/chat-history-visibility.md +0 -96
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_heartbeat.py +0 -50
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_turn_check.py +0 -49
- package/scripts/check_phase_coupling.py +0 -148
|
@@ -0,0 +1,220 @@
|
|
|
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`, `cowork`, `cursor`, `cline`, `windsurf`, `gemini`, `copilot`. The `claude` value covers both Claude Code (CLI) and Claude.ai Web; `cowork` covers the Claude desktop app's local-agent-mode runtime separately so chat-history entries can attribute events to Cowork vs CLI Claude Code via the `agent` field. Cowork shares Claude Code's lifecycle vocabulary and payload shape but is upstream-blocked from reading any settings source as of writing (anthropics/claude-code#40495, #27398). The canonical platform identifier is `claude` for the CLI/IDE surface and `cowork` for the desktop sandbox (both match `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.
|
|
214
|
+
|
|
215
|
+
## See also
|
|
216
|
+
|
|
217
|
+
- [`docs/hook-payload-capture.md`](../hook-payload-capture.md) —
|
|
218
|
+
operational how-to for capturing redacted live payloads to upgrade
|
|
219
|
+
a platform's chat-history extractor from `docs-verified` to
|
|
220
|
+
`payload-verified`.
|
|
@@ -0,0 +1,122 @@
|
|
|
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, and the opt-out
|
|
15
|
+
toggle. Does **not** define how memory entries are scored or routed —
|
|
16
|
+
that is the sibling agent-memory package.
|
|
17
|
+
|
|
18
|
+
Last refreshed: 2026-05-04.
|
|
19
|
+
|
|
20
|
+
## Line shape
|
|
21
|
+
|
|
22
|
+
A single one-line ASCII record, prefixed with the memory icon `🧠`
|
|
23
|
+
and a single space:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
🧠 Memory: <hits>/<asks> · ids=[<comma-separated-ids>]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
🧠 Memory: 3/4 · ids=[mem_42, mem_57, mem_91]
|
|
33
|
+
🧠 Memory: 0/2 · ids=[]
|
|
34
|
+
🧠 Memory: 5/5 · ids=[mem_a01, mem_a02, mem_a03, …+2]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Cap at 5 ids inline; remainder rendered as `…+N`. The full id list
|
|
38
|
+
lives in the decision-trace JSON
|
|
39
|
+
([`decision-trace-v1.md`](decision-trace-v1.md)).
|
|
40
|
+
|
|
41
|
+
## Field semantics
|
|
42
|
+
|
|
43
|
+
| Field | Meaning |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `hits` | Count of `memory_retrieve_*` calls during this turn that returned ≥ 1 entry. |
|
|
46
|
+
| `asks` | Count of `memory_retrieve_*` calls during this turn — both successful and empty. |
|
|
47
|
+
| `ids` | Stable memory entry ids returned across all calls, deduped, ordered by retrieval timestamp. |
|
|
48
|
+
|
|
49
|
+
`hits ≤ asks` is invariant. If `asks == 0`, the engine MUST suppress
|
|
50
|
+
the line entirely — no `0/0` noise.
|
|
51
|
+
|
|
52
|
+
## Privacy floor
|
|
53
|
+
|
|
54
|
+
The visibility line and the JSON it derives from MUST NOT contain:
|
|
55
|
+
|
|
56
|
+
- Entry **bodies**, summaries, or quoted snippets.
|
|
57
|
+
- Secrets, tokens, environment values, or paths outside the
|
|
58
|
+
package's `agents/state/` and `tests/` allowlist.
|
|
59
|
+
- User identifiers beyond what is already public in the working
|
|
60
|
+
directory's `.agent-settings.yml` (e.g. developer name).
|
|
61
|
+
|
|
62
|
+
The privacy floor is enforced by
|
|
63
|
+
`tests/contracts/test_memory_visibility_redaction.py` — any new
|
|
64
|
+
content path that ships memory output adds a fixture there.
|
|
65
|
+
|
|
66
|
+
## Opt-out
|
|
67
|
+
|
|
68
|
+
On by default whenever memory is asked at all in a turn. Users can
|
|
69
|
+
suppress the visibility line via:
|
|
70
|
+
|
|
71
|
+
```yaml
|
|
72
|
+
memory:
|
|
73
|
+
visibility: off
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Off-mode does not silence the underlying memory calls; it only stops
|
|
77
|
+
the line from rendering. The decision-trace JSON still records the
|
|
78
|
+
counts and ids for downstream metrics.
|
|
79
|
+
|
|
80
|
+
## Cadence interaction
|
|
81
|
+
|
|
82
|
+
| Cost profile | Visibility line |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `lean` | suppress unless `asks ≥ 3` |
|
|
85
|
+
| `standard` | always when `asks ≥ 1` |
|
|
86
|
+
| `verbose` | always when `asks ≥ 1` |
|
|
87
|
+
|
|
88
|
+
Cost-profile lookup respects `.agent-settings.yml`'s `cost_profile`
|
|
89
|
+
key. Default is `standard`.
|
|
90
|
+
|
|
91
|
+
## Audit-as-memory feed
|
|
92
|
+
|
|
93
|
+
The visibility output produced by the engine is the input to the
|
|
94
|
+
audit-as-memory pipeline (consumed by the sibling distribution +
|
|
95
|
+
adoption work). Concretely:
|
|
96
|
+
|
|
97
|
+
- The engine emits the line + the underlying counts to the
|
|
98
|
+
decision-trace JSON.
|
|
99
|
+
- A consumer hook reads `agents/state/work/<work-id>/decision-trace-*.json`,
|
|
100
|
+
rolls counts up to the session level, and feeds the result back
|
|
101
|
+
into the agent-memory store as an audit entry.
|
|
102
|
+
|
|
103
|
+
This contract pins the **producer** side. The audit-feed consumer
|
|
104
|
+
lives outside the package's stable surface and must read this
|
|
105
|
+
contract before parsing.
|
|
106
|
+
|
|
107
|
+
## Stability
|
|
108
|
+
|
|
109
|
+
Beta. Breaking changes between v1 and v2 are allowed in a minor
|
|
110
|
+
release if the change appears in `CHANGELOG.md` under a `### Breaking`
|
|
111
|
+
heading. Engines MUST gate on the visibility line shape — clients
|
|
112
|
+
parsing the stream MUST treat unknown trailing fields as forward-
|
|
113
|
+
compat extensions.
|
|
114
|
+
|
|
115
|
+
## Cross-references
|
|
116
|
+
|
|
117
|
+
- CLI surface and backend states:
|
|
118
|
+
[`agent-memory-contract.md`](agent-memory-contract.md).
|
|
119
|
+
- Decision-trace JSON consumes the same counts:
|
|
120
|
+
[`decision-trace-v1.md`](decision-trace-v1.md).
|
|
121
|
+
- Privacy regression test path:
|
|
122
|
+
`tests/contracts/test_memory_visibility_redaction.py`.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
stability: beta
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# One-off-script lifecycle
|
|
6
|
+
|
|
7
|
+
**Purpose.** Pin the naming, location, age, and purge policy for
|
|
8
|
+
**one-off scripts** so the package does not accumulate a graveyard
|
|
9
|
+
under `scripts/`. One-off here means: a script written for a
|
|
10
|
+
specific migration, retrofit, audit, or council run, with no ongoing
|
|
11
|
+
caller and no place in the durable Taskfile.
|
|
12
|
+
|
|
13
|
+
**Scope.** Defines the file pattern, the directory, the maximum age,
|
|
14
|
+
the TTL extension mechanism, and the CI purge gate. Does **not**
|
|
15
|
+
specify the content of any specific one-off — that belongs to the
|
|
16
|
+
script itself or the cleanup-mechanics context.
|
|
17
|
+
|
|
18
|
+
Last refreshed: 2026-05-04.
|
|
19
|
+
|
|
20
|
+
## Naming
|
|
21
|
+
|
|
22
|
+
One-off scripts MUST match this regex:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
^_one_off_[a-z0-9-]+\.py$
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The `_one_off_` prefix is the load-bearing signal. Files outside
|
|
29
|
+
this prefix are treated as durable scripts and MUST be referenced by
|
|
30
|
+
the Taskfile or by another script.
|
|
31
|
+
|
|
32
|
+
## Location
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
scripts/_one_off/<YYYY-MM>/_one_off_<slug>.py
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`<YYYY-MM>` is the UTC month the script was first committed. The
|
|
39
|
+
month directory groups one-offs for archival sweeps. Scripts MUST
|
|
40
|
+
NOT live at `scripts/_one_off/_one_off_*.py` (no month) or under
|
|
41
|
+
`scripts/` directly (no `_one_off/`).
|
|
42
|
+
|
|
43
|
+
## TTL
|
|
44
|
+
|
|
45
|
+
| State | Action |
|
|
46
|
+
|---|---|
|
|
47
|
+
| Age ≤ 60 days from month-directory date | active, no warning |
|
|
48
|
+
| 60 < Age ≤ 90 days | warning emitted by `lint_one_off_age.py`, no failure |
|
|
49
|
+
| Age > 90 days | `lint_one_off_age.py` fails CI; the script is purged in the next housekeeping pass |
|
|
50
|
+
|
|
51
|
+
Age = `today − first-of-month(<YYYY-MM>)` in UTC days. The 60-day
|
|
52
|
+
soft floor and 30-day grace window are intentional — they cover one
|
|
53
|
+
release cycle plus a sprint of grace.
|
|
54
|
+
|
|
55
|
+
## TTL extension
|
|
56
|
+
|
|
57
|
+
A one-off MAY extend its TTL exactly once, by adding a frontmatter
|
|
58
|
+
block at the top of the script:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
"""
|
|
62
|
+
---
|
|
63
|
+
ttl_extended_until: 2026-08-31
|
|
64
|
+
ttl_reason: blocked on PROJ-123 — re-runs after cutover
|
|
65
|
+
---
|
|
66
|
+
"""
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The linter respects `ttl_extended_until` if it is ≤ 180 days from
|
|
70
|
+
the file's `<YYYY-MM>` directory date. Beyond 180 days, the linter
|
|
71
|
+
hard-fails — no second extension. The intent is: if a "one-off" is
|
|
72
|
+
still live at six months, it is a durable concern and belongs in
|
|
73
|
+
`scripts/` or a Taskfile group.
|
|
74
|
+
|
|
75
|
+
## Purge mechanism
|
|
76
|
+
|
|
77
|
+
`lint_one_off_age.py` runs in `task ci`. On a clean working tree, it
|
|
78
|
+
prints purge candidates as a list. Purge itself is a separate human-
|
|
79
|
+
or-CI action — `task purge-one-offs` removes flagged files. The
|
|
80
|
+
linter does not auto-delete.
|
|
81
|
+
|
|
82
|
+
## Allowed exceptions
|
|
83
|
+
|
|
84
|
+
Two patterns are exempt from the prefix requirement:
|
|
85
|
+
|
|
86
|
+
- **Bundler / orchestrator helpers** under `scripts/ai_council/`
|
|
87
|
+
that exist to support the council CLI — they are not one-offs even
|
|
88
|
+
though council *runs* are one-offs.
|
|
89
|
+
- **`scripts/_one_off/<YYYY-MM>/README.md`** — a free-form readme is
|
|
90
|
+
allowed in each month directory documenting why the scripts exist.
|
|
91
|
+
|
|
92
|
+
Council run scripts that wrap a question and write the response file
|
|
93
|
+
DO live under `scripts/_one_off/<YYYY-MM>/` and DO follow the prefix
|
|
94
|
+
rule.
|
|
95
|
+
|
|
96
|
+
## Cross-references
|
|
97
|
+
|
|
98
|
+
- The contract that defines council CLI surface (and so what gets
|
|
99
|
+
archived as a one-off): the council CLI section of the package's
|
|
100
|
+
command catalog.
|
|
101
|
+
- The cleanup-mechanics context for housekeeping passes:
|
|
102
|
+
`agents/contexts/cleanup-mechanics.md`.
|
|
103
|
+
- Linter implementation: `scripts/lint_one_off_age.py`.
|
|
104
|
+
|
|
105
|
+
## Stability
|
|
106
|
+
|
|
107
|
+
Beta. Breaking changes (e.g. raising the age cap, changing the
|
|
108
|
+
prefix, or removing TTL extensions) require a minor-version bump and
|
|
109
|
+
a `### Breaking` entry in `CHANGELOG.md`.
|
|
@@ -221,6 +221,28 @@ pairs:
|
|
|
221
221
|
- .agent-src.uncompressed/rules/ask-when-uncertain.md#iron-law--one-question-per-turn-always
|
|
222
222
|
- .agent-src.uncompressed/rules/direct-answers.md#iron-law-3--brevity-by-default
|
|
223
223
|
|
|
224
|
+
- id: scope-x-verify-before-complete
|
|
225
|
+
rules: [verify-before-complete, scope-control]
|
|
226
|
+
relation: complements
|
|
227
|
+
conflict: >-
|
|
228
|
+
Agent has just finished a change that touches user-permission-gated
|
|
229
|
+
operations (push, branch, PR, tag) and is preparing to claim "done"
|
|
230
|
+
in the same turn. Both rules can fire: `verify-before-complete`
|
|
231
|
+
gates the completion claim on fresh evidence; `scope-control`
|
|
232
|
+
gates the git operation on explicit permission this turn.
|
|
233
|
+
resolution: >-
|
|
234
|
+
Both rules apply independently and compose. The
|
|
235
|
+
`verify-before-complete` Iron Law still requires fresh
|
|
236
|
+
verification output in this message before any "done" claim,
|
|
237
|
+
regardless of whether the user has authorised the next git op.
|
|
238
|
+
Conversely, verification passing does not authorise pushing or
|
|
239
|
+
merging — those stay behind the `scope-control` permission gate.
|
|
240
|
+
Skipping either is a rule violation; satisfying one does not
|
|
241
|
+
satisfy the other.
|
|
242
|
+
evidence:
|
|
243
|
+
- .agent-src.uncompressed/rules/verify-before-complete.md#the-iron-law
|
|
244
|
+
- .agent-src.uncompressed/rules/scope-control.md#git-operations--permission-gated
|
|
245
|
+
|
|
224
246
|
- id: language-x-direct-answers
|
|
225
247
|
rules: [language-and-tone, direct-answers]
|
|
226
248
|
relation: complements
|
package/docs/customization.md
CHANGED
|
@@ -53,7 +53,7 @@ those sections.
|
|
|
53
53
|
| `personal.open_edited_files` | `false` | Open edited files in IDE |
|
|
54
54
|
| `personal.ide` | *(empty)* | IDE for file opening (`cursor`, `code`, `phpstorm`) |
|
|
55
55
|
| `pipelines.skill_improvement` | `true` | Post-task learning capture. Included in every profile except `custom`. |
|
|
56
|
-
| `chat_history.enabled` | `true` | Persistent JSONL log at
|
|
56
|
+
| `chat_history.enabled` | `true` | Persistent JSONL log at `agents/.agent-chat-history` for crash recovery. |
|
|
57
57
|
| `chat_history.frequency` | per profile | Logging granularity: `per_turn`, `per_phase`, or `per_tool` (see matrix below). |
|
|
58
58
|
| `chat_history.max_size_kb` | per profile | Max file size before overflow handling (see matrix below). |
|
|
59
59
|
| `chat_history.on_overflow` | per profile | `rotate` drops oldest, `compress` marks for summarization (see matrix below). |
|
|
@@ -66,6 +66,7 @@ those sections.
|
|
|
66
66
|
| `ai_council.cost_budget.max_calls` | `10` | Maximum council members per invocation. |
|
|
67
67
|
| `ai_council.cost_budget.max_total_usd` | `0.0` | Per-invocation USD ceiling. `0` disables (token caps still apply). |
|
|
68
68
|
| `ai_council.cost_budget.daily_limit_usd` | `0.0` | Rolling 24h USD ceiling across all `/council` calls. `0` disables. Ledger lives at `~/.config/agent-config/council-spend.jsonl` (mode 0600). |
|
|
69
|
+
| `ai_council.session_retention_days` | `14` | Auto-prune for `agents/council-sessions/` audit folders. Older session directories are removed on the next `save()`. `0` disables (keep forever). |
|
|
69
70
|
|
|
70
71
|
> **Experimental.** AI Council is not yet validated by external users. API costs apply per consultation.
|
|
71
72
|
|
package/docs/development.md
CHANGED
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
|
|
18
18
|
## Task Commands
|
|
19
19
|
|
|
20
|
-
All commands use [Task](https://taskfile.dev/).
|
|
20
|
+
All commands use [Task](https://taskfile.dev/). The root `Taskfile.yml` orchestrates
|
|
21
|
+
`ci`/`_ci-*` and includes the four task groups under `taskfiles/`
|
|
22
|
+
(`ci-fast.yml`, `content.yml`, `engine.yml`, `release.yml`) with `flatten: true`,
|
|
23
|
+
so every task stays in the root namespace. Run `task --list` for the full list.
|
|
21
24
|
|
|
22
25
|
### CI & Verification
|
|
23
26
|
|
package/docs/getting-started.md
CHANGED
|
@@ -115,7 +115,7 @@ Your agent is now:
|
|
|
115
115
|
- **Respecting your codebase** — no conflicting patterns
|
|
116
116
|
- **Following standards** — consistent code quality
|
|
117
117
|
|
|
118
|
-
This is enforced automatically by
|
|
118
|
+
This is enforced automatically by 56 rules. No configuration needed.
|
|
119
119
|
|
|
120
120
|
---
|
|
121
121
|
|
|
@@ -151,41 +151,33 @@ Your agent now understands slash commands:
|
|
|
151
151
|
| `/optimize skills` | Audit skills, find duplicates, run linter |
|
|
152
152
|
| `/feature plan` | Interactively plan a feature |
|
|
153
153
|
| `/quality-fix` | Run and fix all quality checks |
|
|
154
|
-
| `/chat-history` | Inspect the persistent chat-history log |
|
|
155
|
-
| `/chat-history-resume` | Recover context after a crashed or switched session |
|
|
156
|
-
| `/chat-history-clear` | Wipe the chat-history log (with confirmation) |
|
|
154
|
+
| `/chat-history` | Inspect the persistent chat-history log (read-only `show`) |
|
|
157
155
|
|
|
158
|
-
→ [Browse all
|
|
156
|
+
→ [Browse all 94 active commands](../.agent-src/commands/)
|
|
159
157
|
|
|
160
158
|
---
|
|
161
159
|
|
|
162
|
-
## Crash recovery —
|
|
160
|
+
## Crash recovery — `agents/.agent-chat-history`
|
|
163
161
|
|
|
164
162
|
When `chat_history.enabled: true` in `.agent-settings.yml` (on by default
|
|
165
163
|
for every profile), the agent keeps a JSONL log of your conversation in
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
`
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
-
|
|
182
|
-
|
|
183
|
-
Run `/chat-history-resume` to walk through the prompts explicitly, or
|
|
184
|
-
let the agent ask on the first turn of a new chat. All merge/replace/
|
|
185
|
-
resume paths read the on-disk entries into context before any write.
|
|
186
|
-
|
|
187
|
-
See the [`chat-history` rule](../.agent-src/rules/chat-history-ownership.md) and
|
|
188
|
-
[`scripts/chat_history.py`](../scripts/chat_history.py) for the mechanics.
|
|
164
|
+
`agents/.agent-chat-history`. The file is git-ignored and rotates at the
|
|
165
|
+
size configured in the profile (`128 KB` on `minimal`, `256 KB` on
|
|
166
|
+
`balanced`, `512 KB` on `full`).
|
|
167
|
+
|
|
168
|
+
Logging is **hook-only**: a structural Augment hook fires on
|
|
169
|
+
`session_start` and binds the log to the current session via auto-adopt
|
|
170
|
+
— no agent prompts, no ownership questions. The file is rewritten
|
|
171
|
+
transparently if the fingerprint does not match (fresh chat) and
|
|
172
|
+
otherwise appended to.
|
|
173
|
+
|
|
174
|
+
Run `/chat-history` (a.k.a. `/chat-history show`) any time to inspect
|
|
175
|
+
the log size, last entries, and current fingerprint. For the rare case
|
|
176
|
+
where auto-adopt misfires (corrupted file, hook misconfiguration), run
|
|
177
|
+
`./agent-config chat-history:adopt` as the manual recovery lever.
|
|
178
|
+
|
|
179
|
+
See [`agents/contexts/chat-history-platform-hooks.md`](../agents/contexts/chat-history-platform-hooks.md)
|
|
180
|
+
and [`scripts/chat_history.py`](../scripts/chat_history.py) for the mechanics.
|
|
189
181
|
|
|
190
182
|
---
|
|
191
183
|
|
|
@@ -106,7 +106,7 @@ Agent: Bevor ich die Roadmap übergebe:
|
|
|
106
106
|
- Welcher Branch?
|
|
107
107
|
- Soll ich PRs erwähnen?
|
|
108
108
|
- Welches Modell für die Fortsetzung?
|
|
109
|
-
- Soll ich
|
|
109
|
+
- Soll ich agents/.agent-chat-history zitieren?
|
|
110
110
|
|
|
111
111
|
Antworte als 1, 2, 3, 4.
|
|
112
112
|
```
|
|
@@ -152,27 +152,46 @@ MUST follow these rules. Initial file creation and legacy migration
|
|
|
152
152
|
are owned by `scripts/install.py`; these rules govern every edit
|
|
153
153
|
after that.
|
|
154
154
|
|
|
155
|
+
The contract is **additive merge with user-line preservation** —
|
|
156
|
+
the user's file is the ground truth, the template only contributes
|
|
157
|
+
keys the user is missing. Round-trip parser and merger live in
|
|
158
|
+
[`scripts/sync_yaml_rt.py`](../../scripts/sync_yaml_rt.py); the
|
|
159
|
+
supported YAML subset (block-mappings, scalars, lists, comments,
|
|
160
|
+
CRLF/LF) is documented in its module docstring. The stdlib-only
|
|
161
|
+
choice (vs. `ruamel.yaml`) and its revisit triggers are recorded in
|
|
162
|
+
[`docs/contracts/adr-settings-sync-engine.md`](../../contracts/adr-settings-sync-engine.md).
|
|
163
|
+
|
|
155
164
|
For each section in the template
|
|
156
|
-
([`agent-settings.md`](../../templates/agent-settings.md))
|
|
157
|
-
template order:
|
|
165
|
+
([`agent-settings.md`](../../templates/agent-settings.md)):
|
|
158
166
|
|
|
159
|
-
- Keep the section header and its comments verbatim from the template.
|
|
160
167
|
- For each key under the section:
|
|
161
|
-
- **Key exists in user's file** →
|
|
162
|
-
|
|
163
|
-
- **
|
|
164
|
-
|
|
165
|
-
|
|
168
|
+
- **Key exists in user's file** → keep the user's line **verbatim**
|
|
169
|
+
(value, quoting, inline comment, indent — all preserved).
|
|
170
|
+
- **Key missing** → insert the template's line at the position
|
|
171
|
+
after the user's last preceding sibling that is also in the
|
|
172
|
+
template (max-index insertion).
|
|
173
|
+
- **Unknown sections/keys** the user has added → preserved verbatim
|
|
174
|
+
at their existing position. They are not moved to a trailing
|
|
175
|
+
`_user:` block, not re-prefixed, not flattened.
|
|
166
176
|
|
|
167
177
|
Invariants:
|
|
168
178
|
|
|
169
|
-
-
|
|
170
|
-
|
|
179
|
+
- **User order wins.** Template order is only consulted to decide
|
|
180
|
+
where to insert missing keys; existing user keys are never
|
|
181
|
+
reordered.
|
|
171
182
|
- Existing scalar values are **never overwritten** unless the user
|
|
172
183
|
asked for that specific change.
|
|
173
|
-
- New keys added to the template land with their default value
|
|
174
|
-
|
|
175
|
-
|
|
184
|
+
- New keys added to the template land with their default value and
|
|
185
|
+
the template's leading comments.
|
|
186
|
+
- **User comments are preserved verbatim** on every existing key.
|
|
187
|
+
Template comments only land with keys the merger inserts; once a
|
|
188
|
+
key is in the user's file, its surrounding comments are owned by
|
|
189
|
+
the user.
|
|
190
|
+
- Legacy `_user._user.foo` corruption (accumulated by older buggy
|
|
191
|
+
syncs) heals on the next sync — the leading `_user.` chain is
|
|
192
|
+
stripped and the leaf is re-homed at its template path, or kept
|
|
193
|
+
as a single-level orphan under `_user:` if no template home
|
|
194
|
+
exists.
|
|
176
195
|
- Write with 2-space indent, no tabs, no trailing whitespace.
|
|
177
196
|
- Never commit — `.agent-settings.yml` is git-ignored.
|
|
178
197
|
- If a legacy flat `.agent-settings` (key=value) is still present,
|