@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.
Files changed (181) hide show
  1. package/.agent-src/commands/agent-handoff.md +14 -10
  2. package/.agent-src/commands/chat-history/import.md +170 -0
  3. package/.agent-src/commands/chat-history/learn.md +178 -0
  4. package/.agent-src/commands/chat-history/show.md +17 -18
  5. package/.agent-src/commands/chat-history.md +26 -25
  6. package/.agent-src/commands/council/default.md +77 -82
  7. package/.agent-src/commands/create-pr.md +28 -8
  8. package/.agent-src/commands/feature/roadmap.md +22 -0
  9. package/.agent-src/commands/roadmap/create.md +38 -6
  10. package/.agent-src/commands/roadmap/execute.md +36 -9
  11. package/.agent-src/commands/sync-gitignore.md +1 -1
  12. package/.agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +76 -0
  13. package/.agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +3 -3
  14. package/.agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +5 -12
  15. package/.agent-src/rules/agent-authority.md +1 -0
  16. package/.agent-src/rules/agent-docs.md +1 -0
  17. package/.agent-src/rules/analysis-skill-routing.md +1 -0
  18. package/.agent-src/rules/architecture.md +1 -0
  19. package/.agent-src/rules/artifact-drafting-protocol.md +1 -0
  20. package/.agent-src/rules/artifact-engagement-recording.md +1 -0
  21. package/.agent-src/rules/ask-when-uncertain.md +1 -0
  22. package/.agent-src/rules/augment-portability.md +1 -0
  23. package/.agent-src/rules/augment-source-of-truth.md +1 -0
  24. package/.agent-src/rules/autonomous-execution.md +1 -0
  25. package/.agent-src/rules/capture-learnings.md +1 -0
  26. package/.agent-src/rules/cli-output-handling.md +2 -2
  27. package/.agent-src/rules/command-suggestion-policy.md +1 -0
  28. package/.agent-src/rules/commit-conventions.md +1 -0
  29. package/.agent-src/rules/commit-policy.md +1 -0
  30. package/.agent-src/rules/context-hygiene.md +22 -0
  31. package/.agent-src/rules/direct-answers.md +11 -2
  32. package/.agent-src/rules/docker-commands.md +1 -0
  33. package/.agent-src/rules/docs-sync.md +1 -0
  34. package/.agent-src/rules/downstream-changes.md +1 -0
  35. package/.agent-src/rules/e2e-testing.md +1 -0
  36. package/.agent-src/rules/guidelines.md +1 -0
  37. package/.agent-src/rules/improve-before-implement.md +1 -0
  38. package/.agent-src/rules/language-and-tone.md +38 -6
  39. package/.agent-src/rules/laravel-translations.md +1 -0
  40. package/.agent-src/rules/markdown-safe-codeblocks.md +1 -0
  41. package/.agent-src/rules/minimal-safe-diff.md +1 -0
  42. package/.agent-src/rules/missing-tool-handling.md +1 -0
  43. package/.agent-src/rules/model-recommendation.md +1 -0
  44. package/.agent-src/rules/no-attribution-footers.md +48 -0
  45. package/.agent-src/rules/no-cheap-questions.md +1 -0
  46. package/.agent-src/rules/no-roadmap-references.md +2 -1
  47. package/.agent-src/rules/non-destructive-by-default.md +1 -0
  48. package/.agent-src/rules/onboarding-gate.md +26 -0
  49. package/.agent-src/rules/package-ci-checks.md +1 -0
  50. package/.agent-src/rules/php-coding.md +1 -0
  51. package/.agent-src/rules/preservation-guard.md +1 -0
  52. package/.agent-src/rules/review-routing-awareness.md +1 -0
  53. package/.agent-src/rules/reviewer-awareness.md +1 -0
  54. package/.agent-src/rules/roadmap-progress-sync.md +22 -0
  55. package/.agent-src/rules/role-mode-adherence.md +2 -2
  56. package/.agent-src/rules/rule-type-governance.md +1 -0
  57. package/.agent-src/rules/runtime-safety.md +1 -0
  58. package/.agent-src/rules/scope-control.md +1 -0
  59. package/.agent-src/rules/security-sensitive-stop.md +1 -0
  60. package/.agent-src/rules/size-enforcement.md +1 -0
  61. package/.agent-src/rules/skill-improvement-trigger.md +1 -0
  62. package/.agent-src/rules/skill-quality.md +50 -0
  63. package/.agent-src/rules/slash-command-routing-policy.md +39 -0
  64. package/.agent-src/rules/think-before-action.md +1 -0
  65. package/.agent-src/rules/token-efficiency.md +1 -0
  66. package/.agent-src/rules/tool-safety.md +1 -0
  67. package/.agent-src/rules/ui-audit-gate.md +1 -0
  68. package/.agent-src/rules/upstream-proposal.md +1 -0
  69. package/.agent-src/rules/user-interaction.md +22 -5
  70. package/.agent-src/rules/verify-before-complete.md +1 -0
  71. package/.agent-src/skills/ai-council/SKILL.md +4 -5
  72. package/.agent-src/skills/dcf-modeling/SKILL.md +89 -0
  73. package/.agent-src/skills/funnel-analysis/SKILL.md +100 -0
  74. package/.agent-src/skills/md-language-check/SKILL.md +1 -1
  75. package/.agent-src/skills/okr-tree-modeling/SKILL.md +93 -0
  76. package/.agent-src/skills/rice-prioritization/SKILL.md +100 -0
  77. package/.agent-src/skills/roadmap-management/SKILL.md +29 -4
  78. package/.agent-src/skills/subagent-orchestration/SKILL.md +34 -2
  79. package/.agent-src/skills/unit-economics-modeling/SKILL.md +104 -0
  80. package/.agent-src/skills/using-git-worktrees/SKILL.md +1 -0
  81. package/.agent-src/skills/verify-completion-evidence/SKILL.md +8 -1
  82. package/.agent-src/templates/agent-settings.md +21 -26
  83. package/.agent-src/templates/roadmaps.md +8 -3
  84. package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +16 -5
  85. package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +4 -4
  86. package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +4 -4
  87. package/.agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.py +7 -51
  88. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.py +1 -2
  89. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.py +1 -2
  90. package/.agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.py +163 -0
  91. package/.agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.py +110 -0
  92. package/.agent-src/templates/scripts/work_engine/hooks/settings.py +36 -0
  93. package/.agent-src/templates/scripts/work_engine/scoring/decision_trace.py +141 -0
  94. package/.agent-src/templates/scripts/work_engine/scoring/memory_visibility.py +125 -0
  95. package/.agent-src/templates/skill.md +30 -1
  96. package/.claude-plugin/marketplace.json +8 -4
  97. package/AGENTS.md +44 -3
  98. package/CHANGELOG.md +173 -0
  99. package/README.md +22 -22
  100. package/config/agent-settings.template.yml +42 -13
  101. package/config/gitignore-block.txt +4 -4
  102. package/docs/architecture.md +3 -3
  103. package/docs/catalog.md +18 -13
  104. package/docs/contracts/adr-chat-history-split.md +10 -1
  105. package/docs/contracts/adr-settings-sync-engine.md +127 -0
  106. package/docs/contracts/command-clusters.md +1 -1
  107. package/docs/contracts/cross-wing-handoff.md +133 -0
  108. package/docs/contracts/decision-trace-v1.md +146 -0
  109. package/docs/contracts/file-ownership-matrix.json +348 -126
  110. package/docs/contracts/hook-architecture-v1.md +220 -0
  111. package/docs/contracts/memory-visibility-v1.md +122 -0
  112. package/docs/contracts/one-off-script-lifecycle.md +109 -0
  113. package/docs/contracts/rule-interactions.yml +22 -0
  114. package/docs/customization.md +2 -1
  115. package/docs/development.md +4 -1
  116. package/docs/getting-started.md +21 -29
  117. package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +1 -1
  118. package/docs/guidelines/agent-infra/layered-settings.md +32 -13
  119. package/docs/hook-payload-capture.md +221 -0
  120. package/docs/migrations/commands-1.15.0.md +17 -12
  121. package/docs/skills-catalog.md +5 -4
  122. package/llms.txt +4 -3
  123. package/package.json +1 -1
  124. package/scripts/agent-config +45 -1
  125. package/scripts/ai_council/_default_prices.py +4 -4
  126. package/scripts/ai_council/bundler.py +3 -3
  127. package/scripts/ai_council/clients.py +25 -9
  128. package/scripts/ai_council/modes.py +3 -4
  129. package/scripts/ai_council/one_off_archive/2026-05/README.md +22 -0
  130. package/scripts/ai_council/one_off_archive/2026-05/_one_off_roundtrip.py +13 -8
  131. package/scripts/ai_council/one_off_archive/2026-05/_one_off_tier_retrofit.py +180 -0
  132. package/scripts/ai_council/pricing.py +10 -9
  133. package/scripts/ai_council/session.py +92 -0
  134. package/scripts/build_rule_trigger_matrix.py +1 -9
  135. package/scripts/capture_showcase_session.py +361 -0
  136. package/scripts/chat_history.py +963 -597
  137. package/scripts/check_always_budget.py +7 -2
  138. package/scripts/check_references.py +12 -2
  139. package/scripts/context_hygiene_hook.py +14 -6
  140. package/scripts/council_cli.py +407 -0
  141. package/scripts/hook_manifest.yaml +217 -0
  142. package/scripts/hooks/__init__.py +1 -0
  143. package/scripts/hooks/augment-chat-history.sh +10 -0
  144. package/scripts/hooks/augment-dispatcher.sh +72 -0
  145. package/scripts/hooks/cline-dispatcher.sh +86 -0
  146. package/scripts/hooks/cowork-dispatcher.sh +98 -0
  147. package/scripts/hooks/cursor-dispatcher.sh +76 -0
  148. package/scripts/hooks/dispatch_hook.py +383 -0
  149. package/scripts/hooks/envelope.py +98 -0
  150. package/scripts/hooks/gemini-dispatcher.sh +117 -0
  151. package/scripts/hooks/state_io.py +122 -0
  152. package/scripts/hooks/windsurf-dispatcher.sh +123 -0
  153. package/scripts/hooks_status.py +157 -0
  154. package/scripts/install-hooks.sh +2 -2
  155. package/scripts/install.py +725 -87
  156. package/scripts/install.sh +38 -1
  157. package/scripts/lint_handoffs.py +214 -0
  158. package/scripts/lint_hook_manifest.py +217 -0
  159. package/scripts/lint_one_off_age.py +184 -0
  160. package/scripts/lint_rule_tiers.py +78 -0
  161. package/scripts/lint_showcase_sessions.py +148 -0
  162. package/scripts/minimal_safe_diff_hook.py +245 -0
  163. package/scripts/onboarding_gate_hook.py +13 -8
  164. package/scripts/readme_linter.py +12 -3
  165. package/scripts/redact_hook_capture.py +148 -0
  166. package/scripts/roadmap_progress_hook.py +5 -0
  167. package/scripts/schemas/skill.schema.json +5 -0
  168. package/scripts/skill_linter.py +163 -1
  169. package/scripts/sync_agent_settings.py +32 -129
  170. package/scripts/sync_yaml_rt.py +734 -0
  171. package/scripts/update_prices.py +3 -3
  172. package/scripts/verify_before_complete_hook.py +216 -0
  173. package/.agent-src/commands/chat-history/checkpoint.md +0 -126
  174. package/.agent-src/commands/chat-history/clear.md +0 -103
  175. package/.agent-src/commands/chat-history/resume.md +0 -183
  176. package/.agent-src/rules/chat-history-cadence.md +0 -109
  177. package/.agent-src/rules/chat-history-ownership.md +0 -123
  178. package/.agent-src/rules/chat-history-visibility.md +0 -96
  179. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_heartbeat.py +0 -50
  180. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_turn_check.py +0 -49
  181. package/scripts/check_phase_coupling.py +0 -148
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: subagent-orchestration
3
- description: "Use when orchestrating implementer/judge subagents — five modes (do-and-judge, do-in-steps, do-in-parallel, do-competitively, judge-with-debate) — models from .agent-settings.yml."
3
+ description: "Use when orchestrating implementer/judge subagents — six modes (do-and-judge, do-in-steps, do-in-parallel, do-competitively, judge-with-debate, do-in-worktrees) — models from .agent-settings.yml."
4
4
  source: package
5
5
  ---
6
6
 
@@ -44,7 +44,7 @@ judge is a fresh pair of eyes. If `.agent-settings.yml` resolves to
44
44
  identical implementer and judge models, surface the mismatch before
45
45
  running — do not silently continue.
46
46
 
47
- ## The five modes
47
+ ## The six modes
48
48
 
49
49
  Each mode has a decision row: when to use, when not, and the expected
50
50
  model pairing. Defaults come from
@@ -100,6 +100,38 @@ migration, public API) where a single judge is too easy to fool.
100
100
  |---|---|---|
101
101
  | Security, data integrity, public API change | Routine internal refactor | judges = same tier (2x); meta-judge = one tier up |
102
102
 
103
+ ### 6. do-in-worktrees
104
+
105
+ Cross-wing or cross-skill chain executed across isolated git
106
+ worktrees — each handoff in the chain runs in its own worktree, so
107
+ the workspace state of one step never leaks into the next. Operationalizes
108
+ the worktree boundary clause in
109
+ [`docs/contracts/cross-wing-handoff.md`](../../../docs/contracts/cross-wing-handoff.md)
110
+ § 3. State-machine layer only — worktree creation/destruction lives
111
+ in [`using-git-worktrees`](../using-git-worktrees/SKILL.md) and
112
+ [`finishing-a-development-branch`](../finishing-a-development-branch/SKILL.md).
113
+
114
+ | When to use | When not | Model pairing |
115
+ |---|---|---|
116
+ | Multi-step cross-wing chain (≥2 senior skills, each ≥30 min) where one step's open files / branch state would confuse the next | Fast iteration where each step < 30 min — worktree overhead exceeds isolation benefit | implementers = same tier per step; judge = one tier up at chain end |
117
+
118
+ **Handoff shape:** initiator-skill emits the typed output declared in
119
+ its `## Output` block → control passes to delegated-skill in a fresh
120
+ worktree → delegated-skill consumes the input shape declared in its
121
+ `## Input` (or `## When the agent should load this`) block. The
122
+ handoff is auditable; `lint_handoffs.py` validates the chain.
123
+
124
+ **Example chain (W3 launch):** `positioning` (worktree A) →
125
+ `messaging-architecture` (worktree B, consumes positioning's
126
+ `positioning-statement.md`) → `gtm-launch` (worktree C, consumes
127
+ both prior artifacts). Each worktree carries one branch; the chain
128
+ end produces a single integration PR.
129
+
130
+ **Anti-pattern:** do not use for fast iteration loops where each
131
+ step is under ~30 minutes. The branch-creation, context-switch, and
132
+ worktree-cleanup cost dominates. Stick with mode 1 (do-and-judge)
133
+ or mode 2 (do-in-steps) for those.
134
+
103
135
  ## Procedure
104
136
 
105
137
  ### 1. Inspect the task shape
@@ -0,0 +1,104 @@
1
+ ---
2
+ name: unit-economics-modeling
3
+ description: "Use when modeling CAC, LTV, gross-margin payback, or contribution margin per customer — for SaaS, marketplace, or transactional businesses."
4
+ status: active
5
+ tier: senior
6
+ source: package
7
+ ---
8
+
9
+ # unit-economics-modeling
10
+
11
+ ## When to use
12
+
13
+ - A board ask: "is this business unit-economic?" — needs CAC / LTV / payback, not vibes.
14
+ - A new channel is scaling and the question is whether the CAC payback period is sustainable.
15
+ - A pricing or packaging change needs to be tested against contribution margin per cohort.
16
+
17
+ Do NOT use for full-business intrinsic-value modeling, OKR setting, funnel-stage diagnosis, or backlog ranking (see Related Skills).
18
+
19
+ ## Procedure
20
+
21
+ ### Step 0: Inspect
22
+
23
+ 1. Confirm the business shape — SaaS / marketplace / transactional. The three canonical cases differ in **revenue recognition** and **churn definition**, not in arithmetic.
24
+ 2. Confirm a fully-loaded CAC is computable: paid spend + sales comp + content/SEO allocation + tooling. Marketing-spend-only CAC is a vanity metric.
25
+
26
+ ### Step 1: Compute CAC per channel
27
+
28
+ 1. CAC = `(fully-loaded acquisition spend in window) / (new paying customers acquired in same window)`. Match window to sales-cycle length, not calendar quarter.
29
+ 2. Compute by channel **and** blended. Blended-only hides the channel that is breaking the average.
30
+ 3. Anti-pattern: counting trial signups as customers. Customer = first paid charge cleared.
31
+
32
+ ### Step 2: Compute gross margin
33
+
34
+ 1. Gross margin = `(revenue − COGS) / revenue`. COGS includes hosting, payment fees, third-party APIs the customer's usage drives, and direct customer-success cost.
35
+ 2. Gross margin must be **per dollar of revenue**, not per customer. Per-customer gross margin is contribution margin (Step 3).
36
+ 3. SaaS healthy band: 70–85%. Marketplace: 15–40%. Transactional: 5–25%. Outside these — the business is mislabelled or the COGS allocation is wrong.
37
+
38
+ ### Step 3: Compute LTV
39
+
40
+ 1. Pick the canonical formula for the case:
41
+ - **SaaS:** `LTV = ARPA × gross_margin / monthly_churn_rate`. Use net-dollar churn for self-serve, gross logo churn for high-touch.
42
+ - **Marketplace:** `LTV = take_rate × GMV_per_user × retention_curve_AUC` over 24 months. Steady-state extrapolation is dishonest below 24 months of cohort data.
43
+ - **Transactional:** `LTV = avg_order_value × gross_margin × purchases_per_year × avg_lifetime_years`.
44
+ 2. Cap implied lifetime at 5 years for any business with < 3 years of cohort history. Anything longer is a fairy tale.
45
+ 3. State the formula used inline. Do not let the reader infer.
46
+
47
+ ### Step 4: Compute payback and ratio
48
+
49
+ 1. **CAC payback** (months) = `CAC / (ARPA × gross_margin)` for SaaS; analogue for marketplace and transactional. Healthy SaaS: ≤ 12 months.
50
+ 2. **LTV / CAC ratio**: target ≥ 3.0. Below 1.5 is acquisition-loss territory; above 5.0 means under-investment in growth (or bad LTV math).
51
+ 3. Both numbers, not one. Payback drives capital efficiency; ratio drives long-run economics.
52
+
53
+ ### Step 5: Cohort the answer
54
+
55
+ 1. Run Steps 1–4 by signup-quarter cohort. Trends matter more than the point estimate.
56
+ 2. If LTV/CAC is improving but payback is lengthening, you are buying retention with discounting — flag.
57
+ 3. If both deteriorate, the channel mix has shifted to a worse channel — segment by channel to find the leak.
58
+
59
+ ### Step 6: Validate
60
+
61
+ 1. Sanity-check LTV against revenue retention. If implied LTV > 8× annual revenue per customer with monthly churn > 2%, the math is wrong.
62
+ 2. Sanity-check CAC against fully-loaded P&L. If channel CACs sum to less than total acquisition spend, allocations are missing.
63
+
64
+ ## Gotcha
65
+
66
+ - Marketing-spend-only CAC is the most common deception. Sales comp, BDR salaries, content production, and tooling all belong in fully-loaded CAC.
67
+ - Net-dollar retention > 100% does not justify ignoring logo churn — they answer different questions.
68
+ - ARPA averaged across plan tiers hides churn concentrated in one tier. Compute per tier when tiers differ in price by more than 2×.
69
+ - Payback period using contribution margin (post variable-cost) is honest; payback using gross revenue is the kind of math VCs see in pitch decks and discount on sight.
70
+
71
+ ## Do NOT
72
+
73
+ - Do NOT extrapolate LTV beyond observable cohort data without saying so explicitly.
74
+ - Do NOT mix freemium activation rates with paid CAC; they live in different universes.
75
+ - Do NOT report a single LTV/CAC for a business with multiple distinct customer segments — segment first.
76
+
77
+ ## Related Skills
78
+
79
+ **WHEN to use this**
80
+
81
+ - The question is per-customer economics (CAC, LTV, payback, contribution margin).
82
+ - The decision is whether to scale a channel or pricing tier.
83
+
84
+ **WHEN NOT to use this**
85
+
86
+ - Whole-business intrinsic value with terminal value — route to [`dcf-modeling`](../dcf-modeling/SKILL.md).
87
+ - Diagnosing where conversion drops — route to [`funnel-analysis`](../funnel-analysis/SKILL.md).
88
+ - Ranking competing initiatives — route to [`rice-prioritization`](../rice-prioritization/SKILL.md).
89
+ - Setting team objectives that move these metrics — route to [`okr-tree-modeling`](../okr-tree-modeling/SKILL.md).
90
+
91
+ ## When the agent should load this
92
+
93
+ - "What's our LTV / CAC?"
94
+ - "Is this channel paying back fast enough?"
95
+ - "Compute unit economics for this pricing tier."
96
+ - "Are we unit-economic at this CAC?"
97
+ - "Cohort our payback period."
98
+
99
+ ## Output
100
+
101
+ 1. **`unit-econ-table.md`** — table per channel and blended: CAC · ARPA · gross margin · payback months · LTV · LTV/CAC. With cohort columns (last 4 quarters).
102
+ 2. **`assumptions.md`** — formula chosen (SaaS / marketplace / transactional), churn definition, COGS allocation method, lifetime cap. One bullet per choice.
103
+ 3. **`cohort-trend.md`** — trend chart (ASCII or markdown table) of CAC, payback, LTV/CAC over the last 4–8 cohorts. Annotate channel-mix shifts.
104
+ 4. **`sanity-checks.md`** — explicit cross-checks (LTV vs annual revenue, channel CAC sum vs P&L). Flag any that fail with a one-line investigation pointer.
@@ -15,6 +15,7 @@ source: package
15
15
  * Experimenting with a refactor that may be thrown away — a throwaway
16
16
  worktree is cheaper than a throwaway commit
17
17
  * A long-running build or test suite is busy in the current worktree
18
+ * `subagent-orchestration` mode 6 (`do-in-worktrees`) was selected for a cross-wing chain — this skill is the executor that creates the per-step isolated worktrees the chain expects
18
19
 
19
20
  Do NOT use when:
20
21
 
@@ -128,7 +128,12 @@ When reporting completion to the user:
128
128
  3. **Result** — numeric breakdown (tests passed/failed/skipped, errors,
129
129
  warnings)
130
130
  4. **Caveats** — anything the output flagged but you chose to accept
131
- 5. **Next step** — e.g. "Ready for `/commit`" or "Awaiting review"
131
+ 5. **Untracked files** — if `git status --short` shows any untracked
132
+ files in the working tree, list them verbatim in the report. This
133
+ prevents silently-shipped artefacts (logs, scratch scripts, ad-hoc
134
+ notes) from disappearing into a future commit. Empty list means
135
+ omit the section.
136
+ 6. **Next step** — e.g. "Ready for `/commit`" or "Awaiting review"
132
137
 
133
138
  ## Gotchas
134
139
 
@@ -188,3 +193,5 @@ Before sending a completion message:
188
193
  * [ ] No warnings or skips are hidden
189
194
  * [ ] Targeted tests green → full suite green → quality pipeline clean
190
195
  * [ ] `git status` reflects only the intended change set
196
+ * [ ] If `git status --short` shows untracked files, the report lists
197
+ them verbatim under "Untracked files"
@@ -122,7 +122,7 @@ eloquent:
122
122
 
123
123
  # --- Chat history (crash recovery) ---
124
124
  #
125
- # Persistent JSONL log at .agent-chat-history (project root, git-ignored).
125
+ # Persistent JSONL log at agents/.agent-chat-history (project root, git-ignored).
126
126
  # Keeps a durable record of the conversation so a crashed or switched
127
127
  # agent session can be resumed. See scripts/chat_history.py for the API.
128
128
  #
@@ -141,26 +141,6 @@ chat_history:
141
141
  # Overflow behavior: rotate (drop oldest) | compress (summarize)
142
142
  on_overflow: rotate
143
143
 
144
- # Heartbeat marker visibility: on | off | hybrid
145
- # on — print marker every reply (~20 tokens/reply, legacy)
146
- # off — never print (zero tokens, no drift signal)
147
- # hybrid — print only on drift (missing/foreign/returning); silent otherwise
148
- # YAML 1.1 booleanizes bare on/off — both are accepted, no quoting needed.
149
- heartbeat: hybrid
150
-
151
- # Population path: hook | checkpoint | manual
152
- # hook — platform fires lifecycle hooks; agent observes only
153
- # (Claude Code, Augment CLI, Cursor 1.7+, Cline non-Windows,
154
- # Windsurf, Gemini CLI). scripts/install.py wires hooks.
155
- # checkpoint — agent invokes /chat-history-checkpoint at phase boundaries
156
- # (Augment IDE plugin, Cursor < 1.7, Cline on Windows).
157
- # Cooperative three-gate Iron Law applies.
158
- # manual — rule is inert (cloud surfaces). Persistence is local-only.
159
- # Default `checkpoint` is the safest cooperative fallback. HOOK platforms
160
- # set this to `hook` automatically when scripts/install.py merges the
161
- # platform's settings file.
162
- path: checkpoint
163
-
164
144
  # --- Work-engine hooks ---
165
145
  #
166
146
  # Lifecycle hook surface of the `work_engine` Python engine
@@ -197,7 +177,7 @@ hooks:
197
177
  # routing drift.
198
178
  directive_set_guard: true
199
179
 
200
- # Chat-history hooks — populate .agent-chat-history structurally from
180
+ # Chat-history hooks — populate agents/.agent-chat-history structurally from
201
181
  # the engine. Gated by BOTH this block AND the global
202
182
  # chat_history.enabled above; either off → no chat-history hook
203
183
  # registers. Keep both on for the HOOK path; flip either off to fall
@@ -217,6 +197,21 @@ pipelines:
217
197
  # Included by every cost_profile except `custom`.
218
198
  skill_improvement: true
219
199
 
200
+ # --- Roadmap execution ---
201
+ #
202
+ # Controls when /roadmap execute runs the project's quality pipeline.
203
+ # Step checkboxes and the dashboard are ALWAYS updated in the same
204
+ # response — that cadence is governed by `roadmap-progress-sync` and
205
+ # is non-negotiable. This setting only governs *quality tool runs*.
206
+ roadmap:
207
+ # When to run quality tools during /roadmap execute.
208
+ # end_of_roadmap = once, before archiving (default — fastest, fewest tokens)
209
+ # per_phase = once after every completed phase
210
+ # per_step = after every completed step (legacy; highest token cost)
211
+ # Iron Law `verify-before-complete` still applies — fresh output is
212
+ # mandatory before any "roadmap complete" claim, regardless of cadence.
213
+ quality_cadence: end_of_roadmap
214
+
220
215
  # --- Subagent orchestration ---
221
216
  subagents:
222
217
  # Model for implementer subagents (empty = same tier as the session model)
@@ -348,20 +343,20 @@ lives under `personal:` in YAML.
348
343
  | `project.improvement_pr_branch_prefix` | string | `improve/agent-` | Branch prefix for agent improvement PRs. |
349
344
  | `github.pr_reply_method` | `replies_endpoint`, `create_review_comment`, `auto` | `create_review_comment` | GitHub API method for replying to PR review comments. `auto` detects on first use. |
350
345
  | `eloquent.access_style` | `getters_setters`, `get_attribute`, `magic_properties` | `getters_setters` | How to access Eloquent model attributes. See `eloquent` skill for details. |
351
- | `chat_history.enabled` | `true`, `false` | `true` | Persist chat events to `.agent-chat-history` (JSONL) for crash recovery. |
346
+ | `chat_history.enabled` | `true`, `false` | `true` | Persist chat events to `agents/.agent-chat-history` (JSONL) for crash recovery. |
352
347
  | `chat_history.frequency` | `per_turn`, `per_phase`, `per_tool` | per profile | Logging granularity. Defaults: `minimal`→`per_turn`, `balanced`→`per_phase`, `full`→`per_tool`. |
353
348
  | `chat_history.max_size_kb` | integer | per profile | Max file size before overflow handling. Defaults: `minimal`→`128`, `balanced`→`256`, `full`→`512`. |
354
349
  | `chat_history.on_overflow` | `rotate`, `compress` | per profile | On overflow: `rotate` drops oldest entries; `compress` marks the file for summarization on the next turn. Defaults: `minimal`/`balanced`→`rotate`, `full`→`compress`. |
355
- | `chat_history.heartbeat` | `on`, `off`, `hybrid` | `hybrid` | Visibility of the `📒 chat-history:` marker. `on` = every reply (~20 tokens), `off` = silent, `hybrid` = print only on drift states (`missing`/`foreign`/`returning`). YAML `on`/`off` accepted bare. |
356
- | `chat_history.path` | `hook`, `checkpoint`, `manual` | `checkpoint` | Population path. `hook` = platform fires lifecycle hooks; `checkpoint` = agent invokes `/chat-history-checkpoint` at phase boundaries; `manual` = rule inert (cloud). `scripts/install.py` flips this to `hook` when the platform's hook config is deployed. See [`agents/contexts/chat-history-platform-hooks.md`](../../../agents/contexts/chat-history-platform-hooks.md). |
350
+ | `chat_history.text_limits.{user,agent,tool,phase}` | integer (chars) | `user=0`, `agent=5000`, `tool=200`, `phase=200` | Per-entry-type text-length cap. `0` = verbatim, no slice. `N > 0` = collapse whitespace, slice to N chars, append `" [+K chars]"` so the log self-reports truncation. Defaults match `DEFAULT_TEXT_LIMITS` in `scripts/chat_history.py`. |
357
351
  | `hooks.enabled` | `true`, `false` | `false` | Master switch for the work-engine hook layer. When `false` (default) the registry stays empty and golden replay is byte-stable. See [`agents/contexts/work-engine-hooks.md`](../../../agents/contexts/work-engine-hooks.md). |
358
352
  | `hooks.trace` | `true`, `false` | `false` | Emit per-event trace lines on stderr. Useful for debugging; off by default because it is noisy. |
359
353
  | `hooks.halt_surface_audit` | `true`, `false` | `true` | Defense-in-depth check that every halt surfaced by the dispatcher carries the expected shape. Cheap. |
360
354
  | `hooks.state_shape_validation` | `true`, `false` | `true` | Re-run the state schema validator on `AFTER_LOAD` and `BEFORE_SAVE`. Cheap, catches drift. |
361
355
  | `hooks.directive_set_guard` | `true`, `false` | `true` | Verify the dispatcher-resolved directive set matches the input envelope intent. Cheap, catches routing drift. |
362
- | `hooks.chat_history.enabled` | `true`, `false` | `true` | Register the four chat-history hooks (turn-check, append, halt-append, heartbeat). Gated by **both** this flag AND `chat_history.enabled`; either off → no chat-history hook registers. |
356
+ | `hooks.chat_history.enabled` | `true`, `false` | `true` | Register chat-history hooks (`append` on `after_step`, `halt_append` on `on_halt`). Gated by **both** this flag AND `chat_history.enabled`; either off → no chat-history hook registers. Schema v4: every entry self-identifies via 16-char session fingerprint, no ownership/sidecar layer. |
363
357
  | `hooks.chat_history.script` | path | `scripts/chat_history.py` | Override path to the chat-history CLI. Set only when the script lives outside the standard location. |
364
358
  | `pipelines.skill_improvement` | `true`, `false` | `true` | When `true`: propose learning capture after meaningful tasks. When `false`: silent. Included in every profile except `custom`. |
359
+ | `roadmap.quality_cadence` | `end_of_roadmap`, `per_phase`, `per_step` | `end_of_roadmap` | When `/roadmap execute` runs the project's quality pipeline. Default skips per-step / per-phase runs and gates only the final archival. `per_phase` runs once after every phase; `per_step` is the legacy verbose mode. Step checkboxes and the dashboard are always updated regardless. `verify-before-complete` still requires fresh output before any "roadmap complete" claim. |
365
360
  | `subagents.implementer_model` | model alias or empty | _(empty)_ | Model for implementer subagents. Empty = same tier as session model. See [subagent-configuration](../contexts/subagent-configuration.md). |
366
361
  | `subagents.judge_model` | model alias or empty | _(empty)_ | Model for judge subagents. Empty = one tier above implementer (opus if sonnet, sonnet if haiku). |
367
362
  | `subagents.max_parallel` | integer | `3` | Maximum parallel subagent invocations. `1` serializes. |
@@ -39,11 +39,16 @@ Templates for roadmap files stored in `agents/roadmaps/` or `app/Modules/{Module
39
39
 
40
40
  ---
41
41
 
42
- ## Quality Gates (always apply)
42
+ ## Quality Gates (always apply at completion)
43
43
 
44
- Every roadmap must pass these before it is considered done:
44
+ Every roadmap must pass the project's quality pipeline before it is
45
+ considered done. **When** the pipeline runs during `/roadmap execute` is
46
+ governed by `roadmap.quality_cadence` in `.agent-settings.yml`
47
+ (`end_of_roadmap` default → once before archival; `per_phase` → after
48
+ every phase; `per_step` → after every step). Either way, a final fresh
49
+ run is mandatory before "complete" per `verify-before-complete`.
45
50
 
46
- Run the project's quality pipeline and test suite. Common commands:
51
+ Common commands:
47
52
 
48
53
  ```bash
49
54
  # PHP projects (inside Docker container if applicable)
@@ -17,10 +17,10 @@ from .hooks import HookRegistry
17
17
  from .hooks.builtin import (
18
18
  ChatHistoryAppendHook,
19
19
  ChatHistoryHaltAppendHook,
20
- ChatHistoryHeartbeatHook,
21
- ChatHistoryTurnCheckHook,
20
+ DecisionTraceHook,
22
21
  DirectiveSetGuardHook,
23
22
  HaltSurfaceAuditHook,
23
+ MemoryVisibilityHook,
24
24
  StateShapeValidationHook,
25
25
  TraceHook,
26
26
  )
@@ -56,6 +56,13 @@ def _build_hook_registry(args: argparse.Namespace) -> HookRegistry:
56
56
  StateShapeValidationHook().register(registry)
57
57
  if settings.directive_set_guard:
58
58
  DirectiveSetGuardHook().register(registry)
59
+ if settings.decision_trace:
60
+ DecisionTraceHook().register(registry)
61
+ if settings.memory_visibility:
62
+ MemoryVisibilityHook(
63
+ cost_profile=settings.cost_profile,
64
+ visibility_off=settings.memory_visibility_off,
65
+ ).register(registry)
59
66
  if settings.chat_history_enabled:
60
67
  _register_chat_history_hooks(registry, settings)
61
68
 
@@ -65,12 +72,16 @@ def _build_hook_registry(args: argparse.Namespace) -> HookRegistry:
65
72
  def _register_chat_history_hooks(
66
73
  registry: HookRegistry, settings: HookSettings,
67
74
  ) -> None:
68
- """Register the four chat-history hooks bound to the configured script."""
75
+ """Register the structural chat-history hooks bound to the configured script.
76
+
77
+ Hook-only contract (post road-to-chat-history-hook-only): only the
78
+ append + halt-append hooks remain; cooperative ``turn-check`` /
79
+ ``heartbeat`` hooks were removed when the cooperative always-rules
80
+ were retired.
81
+ """
69
82
  script = Path(settings.chat_history_script)
70
- ChatHistoryTurnCheckHook(script).register(registry)
71
83
  ChatHistoryAppendHook(script).register(registry)
72
84
  ChatHistoryHaltAppendHook(script).register(registry)
73
- ChatHistoryHeartbeatHook(script).register(registry)
74
85
 
75
86
 
76
87
  __all__ = ["_build_hook_registry", "_register_chat_history_hooks"]
@@ -22,10 +22,10 @@ from __future__ import annotations
22
22
  from .builtin import (
23
23
  ChatHistoryAppendHook,
24
24
  ChatHistoryHaltAppendHook,
25
- ChatHistoryHeartbeatHook,
26
- ChatHistoryTurnCheckHook,
25
+ DecisionTraceHook,
27
26
  DirectiveSetGuardHook,
28
27
  HaltSurfaceAuditHook,
28
+ MemoryVisibilityHook,
29
29
  StateShapeValidationHook,
30
30
  TraceHook,
31
31
  )
@@ -38,8 +38,7 @@ from .runner import HookRunner
38
38
  __all__ = [
39
39
  "ChatHistoryAppendHook",
40
40
  "ChatHistoryHaltAppendHook",
41
- "ChatHistoryHeartbeatHook",
42
- "ChatHistoryTurnCheckHook",
41
+ "DecisionTraceHook",
43
42
  "DirectiveSetGuardHook",
44
43
  "HaltSurfaceAuditHook",
45
44
  "HookCallback",
@@ -49,6 +48,7 @@ __all__ = [
49
48
  "HookHalt",
50
49
  "HookRegistry",
51
50
  "HookRunner",
51
+ "MemoryVisibilityHook",
52
52
  "StateShapeValidationHook",
53
53
  "TraceHook",
54
54
  ]
@@ -13,20 +13,20 @@ from __future__ import annotations
13
13
 
14
14
  from .chat_history_append import ChatHistoryAppendHook
15
15
  from .chat_history_halt_append import ChatHistoryHaltAppendHook
16
- from .chat_history_heartbeat import ChatHistoryHeartbeatHook
17
- from .chat_history_turn_check import ChatHistoryTurnCheckHook
16
+ from .decision_trace import DecisionTraceHook
18
17
  from .directive_set_guard import DirectiveSetGuardHook
19
18
  from .halt_surface_audit import HaltSurfaceAuditHook
19
+ from .memory_visibility import MemoryVisibilityHook
20
20
  from .state_shape_validation import StateShapeValidationHook
21
21
  from .trace import TraceHook
22
22
 
23
23
  __all__ = [
24
24
  "ChatHistoryAppendHook",
25
25
  "ChatHistoryHaltAppendHook",
26
- "ChatHistoryHeartbeatHook",
27
- "ChatHistoryTurnCheckHook",
26
+ "DecisionTraceHook",
28
27
  "DirectiveSetGuardHook",
29
28
  "HaltSurfaceAuditHook",
29
+ "MemoryVisibilityHook",
30
30
  "StateShapeValidationHook",
31
31
  "TraceHook",
32
32
  ]
@@ -12,9 +12,6 @@ import sys
12
12
  from pathlib import Path
13
13
  from typing import Callable, Sequence
14
14
 
15
- from ..context import HookContext
16
- from ..exceptions import HookError
17
-
18
15
  ProcessRunner = Callable[[Sequence[str]], "subprocess.CompletedProcess[str]"]
19
16
  """Callable that runs a subprocess. Production default: ``_default_runner``."""
20
17
 
@@ -28,65 +25,24 @@ def _default_runner(cmd: Sequence[str]) -> "subprocess.CompletedProcess[str]":
28
25
  return subprocess.run(list(cmd), capture_output=True, text=True, check=False)
29
26
 
30
27
 
31
- def _derive_first_user_msg(ctx: HookContext) -> str | None:
32
- """Pull a stable first-user-msg out of the available context.
28
+ class _ChatHistoryHookBase:
29
+ """Shared plumbing script path and runner.
33
30
 
34
- CLI-layer events carry ``ctx.work`` (the v1 envelope); dispatcher-layer
35
- events (``before_step`` / ``after_step`` / ``on_halt``) carry only
36
- ``ctx.delivery`` (the legacy :class:`DeliveryState`). Both shapes feed
37
- the same ``id: title`` / ``raw`` derivation so chat-history entries
38
- stay stable across the lifecycle. Returns ``None`` when the shape is
39
- unknown — callers raise ``HookError`` so the runner converts it to
40
- a warning.
31
+ Schema v4 derives session attribution from the platform ``session_id``
32
+ (passed by the platform-hook dispatcher), not from a derived
33
+ first-user-msg. work-engine internal hooks have no platform session
34
+ in scope, so they omit ``--session-id`` and entries land in the
35
+ ``<unknown>`` session bucket.
41
36
  """
42
- work = ctx.work
43
- if work is not None and getattr(work, "input", None) is not None:
44
- inp = work.input
45
- data = getattr(inp, "data", None) or {}
46
- kind = getattr(inp, "kind", None)
47
- if kind == "prompt":
48
- raw = data.get("raw")
49
- if raw:
50
- return str(raw)
51
- elif kind == "ticket":
52
- joined = _ticket_msg(data)
53
- if joined:
54
- return joined
55
-
56
- delivery = ctx.delivery
57
- if delivery is not None:
58
- ticket = getattr(delivery, "ticket", None) or {}
59
- joined = _ticket_msg(ticket)
60
- if joined:
61
- return joined
62
- return None
63
-
64
-
65
- def _ticket_msg(ticket: dict) -> str:
66
- ticket_id = ticket.get("id") or ""
67
- title = ticket.get("title") or ""
68
- return f"{ticket_id}: {title}".strip(": ").strip()
69
-
70
-
71
- class _ChatHistoryHookBase:
72
- """Shared plumbing — script path, runner, and first-msg derivation."""
73
37
 
74
38
  def __init__(
75
39
  self,
76
40
  script_path: Path,
77
41
  *,
78
42
  runner: ProcessRunner | None = None,
79
- first_user_msg: str | None = None,
80
43
  ) -> None:
81
44
  self.script_path = Path(script_path)
82
45
  self._runner = runner or _default_runner
83
- self._fixed_msg = first_user_msg
84
-
85
- def _resolve_msg(self, ctx: HookContext) -> str:
86
- msg = self._fixed_msg or _derive_first_user_msg(ctx)
87
- if not msg:
88
- raise HookError("chat-history hook: cannot derive first-user-msg")
89
- return msg
90
46
 
91
47
  def _invoke(self, *args: str) -> "subprocess.CompletedProcess[str]":
92
48
  cmd = [sys.executable, str(self.script_path), *args]
@@ -29,10 +29,9 @@ class ChatHistoryAppendHook(_ChatHistoryHookBase):
29
29
  result = ctx.result
30
30
  if result is None or getattr(result, "outcome", None) != Outcome.SUCCESS:
31
31
  return
32
- msg = self._resolve_msg(ctx)
33
32
  payload: dict[str, Any] = {"step": ctx.step_name or "<unknown>"}
34
33
  proc = self._invoke(
35
- "append", "--first-user-msg", msg,
34
+ "append",
36
35
  "--type", "phase", "--json", json.dumps(payload),
37
36
  )
38
37
  if proc.returncode != EXIT_OK:
@@ -22,7 +22,6 @@ class ChatHistoryHaltAppendHook(_ChatHistoryHookBase):
22
22
  registry.register(HookEvent.ON_HALT, self._on_halt)
23
23
 
24
24
  def _on_halt(self, ctx: HookContext) -> None:
25
- msg = self._resolve_msg(ctx)
26
25
  questions: list[str] = []
27
26
  if ctx.result is not None:
28
27
  questions = list(getattr(ctx.result, "questions", []) or [])
@@ -30,7 +29,7 @@ class ChatHistoryHaltAppendHook(_ChatHistoryHookBase):
30
29
  questions = list(getattr(ctx.delivery, "questions", []) or [])
31
30
  payload = {"step": ctx.step_name or "<unknown>", "questions": questions}
32
31
  proc = self._invoke(
33
- "append", "--first-user-msg", msg,
32
+ "append",
34
33
  "--type", "decision", "--json", json.dumps(payload),
35
34
  )
36
35
  if proc.returncode != EXIT_OK: