devflow-kit 3.7.0 → 3.9.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 (205) hide show
  1. package/CHANGELOG.md +479 -1
  2. package/README.md +15 -15
  3. package/dist/agents/code.md +6 -8
  4. package/dist/agents/design.md +1 -1
  5. package/dist/agents/fix.md +7 -8
  6. package/dist/agents/git.md +3 -3
  7. package/dist/agents/harden.md +1 -1
  8. package/dist/agents/refactor.md +2 -4
  9. package/dist/agents/review.md +4 -4
  10. package/dist/agents/skim.md +2 -2
  11. package/dist/agents/triage.md +9 -2
  12. package/dist/cli/commands/capture.js +1 -1
  13. package/dist/cli/commands/curation-move-io.js +102 -0
  14. package/dist/cli/commands/curation-move-step.js +374 -0
  15. package/dist/cli/commands/curation.js +808 -0
  16. package/dist/cli/commands/gate-level-prompts.js +428 -0
  17. package/dist/cli/commands/gates.js +35 -11
  18. package/dist/cli/commands/init-seed.js +11 -8
  19. package/dist/cli/commands/init.js +244 -57
  20. package/dist/cli/commands/memory.js +60 -13
  21. package/dist/cli/commands/prompt-io.js +37 -0
  22. package/dist/cli/commands/uninstall.js +157 -17
  23. package/dist/cli.js +2 -2
  24. package/dist/commands/bug-analysis.md +25 -36
  25. package/dist/commands/code-review.md +40 -44
  26. package/dist/commands/debug.md +5 -5
  27. package/dist/commands/dynamic-build.md +26 -25
  28. package/dist/commands/dynamic-plan.md +5 -5
  29. package/dist/commands/dynamic-profile.md +5 -5
  30. package/dist/commands/dynamic-tickets.md +5 -5
  31. package/dist/commands/explore.md +6 -6
  32. package/dist/commands/implement.md +53 -82
  33. package/dist/commands/plan.md +6 -6
  34. package/dist/commands/release.md +5 -5
  35. package/dist/commands/research.md +6 -6
  36. package/dist/commands/resolve.md +39 -41
  37. package/dist/commands/self-review.md +5 -13
  38. package/dist/core/area-pause-notice.js +44 -0
  39. package/dist/core/assets.js +20 -20
  40. package/dist/core/curate-tuning-config.js +5 -6
  41. package/dist/core/curation-move-notice.js +84 -0
  42. package/dist/core/curation-move-skip.js +35 -0
  43. package/dist/core/curation-move.js +35 -0
  44. package/dist/core/curation-queue-cleanup.js +135 -0
  45. package/dist/core/curation-store.js +99 -0
  46. package/dist/core/{learning-variants.js → curation-variants.js} +65 -41
  47. package/dist/core/echo-value.js +11 -5
  48. package/dist/core/evidence-policy.js +11 -9
  49. package/dist/core/feature-switch.js +95 -19
  50. package/dist/core/fs-atomic.js +1 -1
  51. package/dist/core/gate-level.js +10 -2
  52. package/dist/core/git.js +16 -14
  53. package/dist/core/ledger-root.js +82 -22
  54. package/dist/core/linked-path.js +4 -4
  55. package/dist/core/manifest-lock.js +238 -0
  56. package/dist/core/manifest.js +58 -14
  57. package/dist/core/migrations.js +1 -13
  58. package/dist/core/observations.js +2 -2
  59. package/dist/core/plugins.js +19 -19
  60. package/dist/core/pool.js +26 -0
  61. package/dist/core/project-paths.js +103 -34
  62. package/dist/core/queue-drain.js +2 -2
  63. package/dist/core/tracked-areas.js +159 -0
  64. package/dist/core/tracker.js +1 -1
  65. package/dist/{learning-off → curation-off}/agents/code.md +6 -8
  66. package/dist/{learning-off → curation-off}/agents/fix.md +7 -8
  67. package/dist/{learning-off → curation-off}/agents/refactor.md +2 -4
  68. package/dist/{learning-off → curation-off}/agents/review.md +3 -3
  69. package/dist/{learning-off → curation-off}/agents/triage.md +8 -1
  70. package/dist/{learning-off → curation-off}/commands/bug-analysis.md +20 -31
  71. package/dist/{learning-off → curation-off}/commands/code-review.md +37 -41
  72. package/dist/{learning-off → curation-off}/commands/debug.md +2 -2
  73. package/dist/{learning-off → curation-off}/commands/dynamic-build.md +23 -22
  74. package/dist/{learning-off → curation-off}/commands/explore.md +2 -2
  75. package/dist/{learning-off → curation-off}/commands/implement.md +50 -79
  76. package/dist/{learning-off → curation-off}/commands/plan.md +2 -2
  77. package/dist/{learning-off → curation-off}/commands/resolve.md +36 -38
  78. package/dist/{learning-off → curation-off}/commands/self-review.md +2 -10
  79. package/dist/hud/components/{learning-counts.js → curation-counts.js} +63 -17
  80. package/dist/hud/config.js +1 -1
  81. package/dist/hud/index.js +6 -6
  82. package/dist/hud/render.js +3 -3
  83. package/dist/skills/git/references/pr/post-resolution-summary.md +12 -11
  84. package/dist/skills/git/references/pr/post-review-summary.md +12 -11
  85. package/dist/skills/git/references/pr/update-pr-evidence.md +2 -2
  86. package/dist/skills/git/references/pr/validate-branch.md +8 -5
  87. package/dist/skills/git/references/publication-gate.md +6 -7
  88. package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +13 -5
  89. package/dist/skills/git/references/tracker/github/manage-debt.md +6 -2
  90. package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +13 -5
  91. package/dist/skills/git/references/tracker/jira/manage-debt.md +6 -2
  92. package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +13 -5
  93. package/dist/skills/git/references/tracker/linear/manage-debt.md +6 -2
  94. package/dist/targets/claude-code/{learning-install.js → curation-install.js} +85 -65
  95. package/dist/targets/claude-code/installer.js +15 -15
  96. package/dist/targets/claude-code/language-stamp.js +5 -5
  97. package/dist/targets/claude-code/post-install.js +2 -2
  98. package/dist/targets/claude-code/tracker-servers.js +1 -1
  99. package/package.json +3 -2
  100. package/src/assets/agents/_partials/_code.mds +11 -11
  101. package/src/assets/agents/code.mds +6 -6
  102. package/src/assets/agents/curate.md +8 -8
  103. package/src/assets/agents/debug.mds +6 -6
  104. package/src/assets/agents/design.mds +18 -18
  105. package/src/assets/agents/evaluate.md +6 -12
  106. package/src/assets/agents/fix.mds +7 -6
  107. package/src/assets/agents/git.mds +3 -3
  108. package/src/assets/agents/harden.mds +7 -7
  109. package/src/assets/agents/knowledge.mds +23 -23
  110. package/src/assets/agents/refactor.mds +2 -2
  111. package/src/assets/agents/research.mds +15 -15
  112. package/src/assets/agents/review.mds +43 -43
  113. package/src/assets/agents/skim.mds +11 -11
  114. package/src/assets/agents/triage.mds +29 -22
  115. package/src/assets/agents/validate.md +30 -50
  116. package/src/assets/commands/_partials/_decisions.mds +22 -20
  117. package/src/assets/commands/_partials/_engine.mds +8 -8
  118. package/src/assets/commands/_partials/_knowledge.mds +29 -15
  119. package/src/assets/commands/_partials/_preamble.mds +4 -4
  120. package/src/assets/commands/_partials/_settings.mds +3 -3
  121. package/src/assets/commands/_partials/_wave.mds +7 -7
  122. package/src/assets/commands/bug-analysis.mds +37 -48
  123. package/src/assets/commands/code-review.mds +53 -57
  124. package/src/assets/commands/debug.mds +14 -14
  125. package/src/assets/commands/dynamic-build.mds +38 -37
  126. package/src/assets/commands/dynamic-plan.mds +22 -22
  127. package/src/assets/commands/dynamic-profile.mds +2 -2
  128. package/src/assets/commands/dynamic-tickets.mds +8 -8
  129. package/src/assets/commands/explore.mds +20 -20
  130. package/src/assets/commands/implement.mds +77 -98
  131. package/src/assets/commands/plan.mds +27 -27
  132. package/src/assets/commands/release.mds +12 -12
  133. package/src/assets/commands/research.mds +19 -19
  134. package/src/assets/commands/resolve.mds +66 -60
  135. package/src/assets/commands/self-review.mds +20 -20
  136. package/src/assets/mds/git/_ops.mds +9 -2
  137. package/src/assets/mds/git/_ops_pr.mds +21 -11
  138. package/src/assets/mds/git/_ops_steps.mds +20 -4
  139. package/src/assets/mds/git/_pr.mds +21 -18
  140. package/src/assets/mds/git/_references.mds +6 -7
  141. package/src/assets/mds/tracker/_github.mds +1 -4
  142. package/src/assets/mds/tracker/_jira.mds +1 -4
  143. package/src/assets/mds/tracker/_linear.mds +1 -4
  144. package/src/assets/rules/quality.md +1 -0
  145. package/src/assets/scripts/ci-wait.cjs +389 -92
  146. package/src/assets/scripts/hooks/background-memory-update +81 -3
  147. package/src/assets/scripts/hooks/capture-prompt +12 -12
  148. package/src/assets/scripts/hooks/capture-question +11 -11
  149. package/src/assets/scripts/hooks/capture-turn +12 -12
  150. package/src/assets/scripts/hooks/{learning-lock → curation-lock} +2 -2
  151. package/src/assets/scripts/hooks/curation-move +293 -0
  152. package/src/assets/scripts/hooks/ensure-devflow-init +45 -13
  153. package/src/assets/scripts/hooks/ensure-proxy +4 -4
  154. package/src/assets/scripts/hooks/ensure-root-gitignore +4 -4
  155. package/src/assets/scripts/hooks/git-marker +81 -0
  156. package/src/assets/scripts/hooks/json-helper.cjs +176 -51
  157. package/src/assets/scripts/hooks/json-parse +1 -1
  158. package/src/assets/scripts/hooks/lib/curation-move.cjs +1582 -0
  159. package/src/assets/scripts/hooks/lib/{learning-store.cjs → curation-store.cjs} +308 -122
  160. package/src/assets/scripts/hooks/lib/decisions-format.cjs +3 -3
  161. package/src/assets/scripts/hooks/lib/mkdir-lock.cjs +24 -7
  162. package/src/assets/scripts/hooks/lib/project-paths.cjs +132 -39
  163. package/src/assets/scripts/hooks/lib/render-decisions.cjs +42 -22
  164. package/src/assets/scripts/hooks/lib/tracked-areas.cjs +452 -0
  165. package/src/assets/scripts/hooks/memory-permission +10 -2
  166. package/src/assets/scripts/hooks/memory-worker +12 -1
  167. package/src/assets/scripts/hooks/pre-compact-memory +17 -16
  168. package/src/assets/scripts/hooks/queue-append +444 -102
  169. package/src/assets/scripts/hooks/resolve-project-root +2 -2
  170. package/src/assets/scripts/hooks/session-start-context +198 -72
  171. package/src/assets/scripts/hooks/session-start-memory +42 -43
  172. package/src/assets/scripts/lib/project-config.cjs +12 -5
  173. package/src/assets/scripts/pr-evidence.cjs +5 -2
  174. package/src/assets/scripts/resolve-base.cjs +1043 -0
  175. package/src/assets/scripts/resolve-settings.cjs +283 -45
  176. package/src/assets/scripts/verify-evidence.cjs +307 -75
  177. package/src/assets/skills/agent-references/SKILL.md +11 -2
  178. package/src/assets/skills/agent-references/references/commands/implement-levels.md +29 -0
  179. package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +6 -4
  180. package/src/assets/skills/agent-references/references/commands/resolve-levels.md +25 -0
  181. package/src/assets/skills/agent-references/references/synthesize/review.md +4 -4
  182. package/src/assets/skills/agent-references/references/triage/severity-tiers.md +43 -0
  183. package/src/assets/skills/agent-references/references/validate/passed-alone.md +14 -0
  184. package/src/assets/skills/apply-decisions/SKILL.md +1 -1
  185. package/src/assets/skills/docs-framework/SKILL.md +7 -7
  186. package/src/assets/skills/docs-framework/references/patterns.md +3 -4
  187. package/src/assets/skills/quality-gates/SKILL.md +1 -1
  188. package/src/assets/skills/worktree-support/references/roots.md +2 -2
  189. package/templates/lint-pack/.lint-pack.conf +86 -0
  190. package/templates/lint-pack/README.md +190 -0
  191. package/templates/lint-pack/lint-pack.sh +905 -0
  192. package/dist/cli/commands/learning.js +0 -554
  193. package/dist/core/learning-queue-cleanup.js +0 -27
  194. package/dist/core/learning-store.js +0 -61
  195. /package/dist/{learning-off → curation-off}/agents/debug.md +0 -0
  196. /package/dist/{learning-off → curation-off}/agents/design.md +0 -0
  197. /package/dist/{learning-off → curation-off}/agents/harden.md +0 -0
  198. /package/dist/{learning-off → curation-off}/agents/knowledge.md +0 -0
  199. /package/dist/{learning-off → curation-off}/agents/research.md +0 -0
  200. /package/dist/{learning-off → curation-off}/agents/skim.md +0 -0
  201. /package/dist/{learning-off → curation-off}/commands/dynamic-plan.md +0 -0
  202. /package/dist/{learning-off → curation-off}/commands/dynamic-profile.md +0 -0
  203. /package/dist/{learning-off → curation-off}/commands/dynamic-tickets.md +0 -0
  204. /package/dist/{learning-off → curation-off}/commands/release.md +0 -0
  205. /package/dist/{learning-off → curation-off}/commands/research.md +0 -0
@@ -8,7 +8,7 @@
8
8
  # root, mirroring the TS CLI's getGitRoot() (src/core/git.ts) so the shell
9
9
  # side anchors identically.
10
10
  #
11
- # Sourced by: the memory/learning/session hooks and ensure-devflow-init.
11
+ # Sourced by: the memory/curation/session hooks and ensure-devflow-init.
12
12
  # Sourced helper: uses `return`-free pure functions; _-prefixed locals (never
13
13
  # clobbers caller vars). Safe under `set -e` (git failure is guarded with || true).
14
14
  #
@@ -53,7 +53,7 @@ _df_nongit_root() {
53
53
  # df_resolve_roots <cwd> sets TWO roots in the caller's scope, from ONE git call:
54
54
  # DF_ROOT — the checkout's toplevel: where per-checkout data lives
55
55
  # (memory, the .gitignore carve-out, the knowledge bases).
56
- # DF_LEDGER_ROOT — where the learning ledger and its queue live.
56
+ # DF_LEDGER_ROOT — where the curation ledger and its queue live.
57
57
  #
58
58
  # D-LEDGER-MAIN-WORKTREE: a linked worktree (`git worktree add`, `claude
59
59
  # --worktree`) has its own toplevel, so a ledger anchored there restarts its
@@ -1,17 +1,20 @@
1
1
  #!/bin/bash
2
2
 
3
3
  # SessionStart Hook: Cross-Feature Context Injection
4
- # Always-on hook that injects learning context as additionalContext. Sections
5
- # 1–2 are gated by the `features.learning` switch in ~/.devflow/manifest.json,
4
+ # Always-on hook that injects curation context as additionalContext. Sections
5
+ # 1–2 are gated by the `features.curation` switch in ~/.devflow/manifest.json,
6
6
  # which this checkout's project.json / config.json can only narrow
7
7
  # (D-FEATURES-NARROW-ONLY) — this hook itself is never disabled. They and the .gitignore carve-out run only in a git project that is
8
- # not the home directory (D-HOOKS-GIT-ONLY), and they read the learning ledger of
9
- # the main worktree when this is a linked one (D-LEDGER-MAIN-WORKTREE).
8
+ # not the home directory (D-HOOKS-GIT-ONLY), and they read the curation ledger of
9
+ # the main worktree when this is a linked one (D-LEDGER-MAIN-WORKTREE). They are also
10
+ # paused while git tracks files in a curation data folder of that ledger root, or does
11
+ # not answer: the hook then reads no curation file and makes no curation folder, and one
12
+ # fixed notice goes to the user as a systemMessage (Section 4b).
10
13
  #
11
14
  # Section 1: Project decisions TL;DR (decisions.md / pitfalls.md header lines),
12
15
  # then the path of the decisions index when it lists entries.
13
- # Section 2: Learning maintenance directive — when captured turns are pending in
14
- # the learning queue (or a crashed run left a stale .processing batch), instructs
16
+ # Section 2: Curation maintenance directive — when captured turns are pending in
17
+ # the curation queue (or a crashed run left a stale .processing batch), instructs
15
18
  # the main model to spawn the background Curate agent with the resolved model.
16
19
  # The agent claims the queue itself and queue emptiness is the natural gate,
17
20
  # so there is no throttle here.
@@ -23,6 +26,12 @@
23
26
  # Section 4: Legacy install notice — one line when this repository still carries
24
27
  # a retired project-local devflow install (its .claude/settings.json registers
25
28
  # devflow hooks), pointing at `devflow uninstall --scope local`.
29
+ # Section 4b: Curation pause notice — one fixed systemMessage when curation is on and
30
+ # git tracks files in a curation data folder (`tracked`) or did not answer (`unknown`).
31
+ # The answer is the skipped move's (the `curation-move` shim sets DF_CURATION_MOVE_SKIPPED)
32
+ # or, when the move was not skipped, the curation area check's own fresh answer; on a
33
+ # session that starts, resumes or is cleared the message tells the user why curation is
34
+ # paused.
26
35
  # Section 5: Compaction resume directive — on a `compact` SessionStart, one fixed
27
36
  # line telling the model to re-read the devflow command it was running from the
28
37
  # phase in progress (D-COMPACT-RESUME-DIRECTIVE).
@@ -48,6 +57,12 @@
48
57
  # - Section 6, changed start: ONE node process (claude-md-audit.cjs), and a
49
58
  # sibling temp file renamed over the stamp. When none of the roots exists there
50
59
  # is nothing to audit: the stamp is written with builtins, and node is not started.
60
+ #
61
+ # Cost of the curation area check (after the move, with curation on in a git project):
62
+ # - the move already answered `tracked` or `unknown`: its answer is reused, zero forks;
63
+ # - no `.git` entry at the ledger root: a builtin test, zero forks;
64
+ # - otherwise ONE node process (json-helper tracked-area), which makes one bounded
65
+ # `git ls-files` call; without node the shell runs that one call itself, unbounded.
51
66
 
52
67
  # Safe no-op fallback: must exist before hook-bootstrap is sourced.
53
68
  dbg() { :; }
@@ -75,9 +90,9 @@ source "$SCRIPT_DIR/json-parse" || { echo "session-start-context: failed to sour
75
90
  if [ "$_JSON_AVAILABLE" = "false" ]; then exit 0; fi
76
91
 
77
92
  # One subprocess yields both fields: the working directory and the SessionStart
78
- # `source` (startup|resume|clear|compact) that Section 5 gates on, so the directive
79
- # costs no fork of its own. Sections 3 and 4 keep reading `source` themselves, where
80
- # they gate (startup|clear), so neither depends on this value.
93
+ # `source` (startup|resume|clear|compact) that Sections 4b (startup|resume|clear) and 5
94
+ # (compact) gate on, so neither costs a fork of its own. Sections 3 and 4 keep reading
95
+ # `source` themselves, where they gate (startup|clear), so neither depends on this value.
81
96
  _SC_FIELDS=$(printf '%s' "$INPUT" | json_extract_cwd_field "source")
82
97
  CWD="${_SC_FIELDS%%$'\001'*}"
83
98
  SESSION_SOURCE="${_SC_FIELDS#*$'\001'}"
@@ -91,7 +106,7 @@ dbg "CWD=$CWD"
91
106
 
92
107
  # Normal logging, set up before the carve-out below: its skips, and those of the
93
108
  # ensure-root-gitignore it sources, are written with log(), and with memory and
94
- # learning both off this hook is the only one that reaches the carve-out.
109
+ # curation both off this hook is the only one that reaches the carve-out.
95
110
  source "$SCRIPT_DIR/hook-log-init" "session-start-context"
96
111
 
97
112
  # Anchor .devflow/ to the project root (prevents a stray nested .devflow/ when this
@@ -121,7 +136,7 @@ fi
121
136
 
122
137
  # Ensure the project root .gitignore ignores .devflow/ wholesale. This runs on every
123
138
  # git-project session regardless of feature toggles, so memory-off projects
124
- # (learning/knowledge only) still get .devflow/ ignored — this is the
139
+ # (curation/knowledge only) still get .devflow/ ignored — this is the
125
140
  # memory-independent path that fixes the gitignore/memory coupling. Single
126
141
  # source of truth: ensure-root-gitignore. Soft-fail: a gitignore write must never
127
142
  # block context injection. Marker keeps it O(1). A `.devflow` that is a symbolic
@@ -138,7 +153,7 @@ fi
138
153
  CONTEXT=""
139
154
 
140
155
  # The devflow machine root, always $HOME/.devflow (D-ONE-HOME): Section 3's
141
- # tracker files and the manifest the learning gate reads are machine-wide, not
156
+ # tracker files and the manifest the curation gate reads are machine-wide, not
142
157
  # project-scoped (the project's own .devflow is PROJECT_DEVFLOW_DIR below).
143
158
  TRACKER_DEVFLOW_DIR="$HOME/.devflow"
144
159
 
@@ -163,12 +178,12 @@ TRACKER_DEVFLOW_DIR="$HOME/.devflow"
163
178
  # quote, a backtick, `$` or `;` suppresses the directive that embeds it rather
164
179
  # than interpolating an unproven value. That is the fail-closed direction — the
165
180
  # directive is an optimisation — but it must not be a SILENT one: a refused
166
- # Learning root with pending work emits the fixed `--- LEARNING PAUSED ---`
181
+ # Curation root with pending work emits the fixed `--- CURATION PAUSED ---`
167
182
  # notice instead (Section 2), which interpolates nothing, so the user learns why
168
183
  # the queue never drains rather than finding it capped at 200 rows one day.
169
184
  #
170
185
  # `+` is admitted: Claude Code names a worktree for a slash branch with it
171
- # (`feat/x` → `feat+x`), so refusing it would silence learning in exactly the
186
+ # (`feat/x` → `feat+x`), so refusing it would silence curation in exactly the
172
187
  # linked worktrees users open most, and it is inert inside a double-quoted prompt
173
188
  # string — it closes nothing, escapes nothing and expands nothing.
174
189
  #
@@ -191,8 +206,8 @@ TRACKER_DEVFLOW_DIR="$HOME/.devflow"
191
206
  # worktree's root in a linked worktree (D-LEDGER-MAIN-WORKTREE), so it is the value
192
207
  # checked for them, not the checkout's own; only Section 3 interpolates
193
208
  # $PROJECT_ROOT and $TRACKER_DEVFLOW_DIR. Gating values jointly made a rejected
194
- # ~/.devflow shape suppress the Learning directive as well — a value Section 2
195
- # never embeds, silently disabling the whole learning pipeline for any machine
209
+ # ~/.devflow shape suppress the Curation directive as well — a value Section 2
210
+ # never embeds, silently disabling the whole curation pipeline for any machine
196
211
  # whose home directory carries a space. A gate must refuse a sink its value
197
212
  # actually reaches and no other, or the fail-closed direction stops being the
198
213
  # safe one.
@@ -222,40 +237,103 @@ esac
222
237
 
223
238
  PROJECT_DEVFLOW_DIR="$PROJECT_ROOT/.devflow"
224
239
  LEDGER_DEVFLOW_DIR="$LEDGER_ROOT/.devflow"
225
- LEARNING_DIR="$LEDGER_DEVFLOW_DIR/learning"
240
+ # The ledger folder's names come from the move shim (curation-move), the one shell place
241
+ # that holds them. Sourcing it only defines functions. The source is guarded by a builtin
242
+ # readability test, because bash 3.2 ends a `set -e` shell at a `source` of a missing file,
243
+ # even inside `|| true`. Without the shim the hook cannot name the folder: the move and the
244
+ # curation sections are skipped, and every other section runs.
245
+ [ -r "$SCRIPT_DIR/curation-move" ] && source "$SCRIPT_DIR/curation-move" 2>/dev/null || true
246
+ CURATION_NAMES_OK="true"
247
+ df_curation_move_names 2>/dev/null || CURATION_NAMES_OK=""
248
+ CURATION_DIR="$LEDGER_DEVFLOW_DIR/$DF_CM_NEW_FOLDER"
249
+ if [ -z "$CURATION_NAMES_OK" ]; then
250
+ CURATION_DIR=""
251
+ log "Curation skipped: the ledger folder names are unavailable (curation-move is missing)"
252
+ fi
226
253
 
227
- # --- Learning gate: the machine switch, narrowed by this checkout ---
254
+ # --- Curation gate: the machine switch, narrowed by this checkout ---
228
255
  # D-FEATURES-NARROW-ONLY (see queue-append): ~/.devflow/manifest.json's
229
- # features.learning, narrowed by PROJECT_ROOT's project.json / config.json, is
256
+ # features.curation, narrowed by PROJECT_ROOT's project.json / config.json, is
230
257
  # read by the same helper the capture hooks use, so the directive and the queue
231
- # it drains can never disagree about whether learning is on. The manifest lives
258
+ # it drains can never disagree about whether curation is on. The manifest lives
232
259
  # under the machine root captured above, not the project-scoped
233
260
  # PROJECT_DEVFLOW_DIR. Neither file present, or neither holding a `false`, costs
234
261
  # no fork (D-GATES-FAST-PATH).
235
262
  source "$SCRIPT_DIR/queue-append" || { echo "session-start-context: failed to source queue-append" >&2; exit 1; }
236
263
  queue_read_gates "$TRACKER_DEVFLOW_DIR/manifest.json" "$PROJECT_ROOT"
237
- LEARNING_ENABLED="$_QG_LEARNING"
238
- # Learning is project work: off outside a git project (D-HOOKS-GIT-ONLY above).
239
- [ -n "$PROJECT_OK" ] || LEARNING_ENABLED="false"
264
+ CURATION_ENABLED="$_QG_CURATION"
265
+ # Curation is project work: off outside a git project (D-HOOKS-GIT-ONLY above), and off
266
+ # where the ledger folder has no name.
267
+ [ -n "$PROJECT_OK" ] || CURATION_ENABLED="false"
268
+ [ -n "$CURATION_NAMES_OK" ] || CURATION_ENABLED="false"
269
+
270
+ # --- The one-time ledger folder move (curation-move) ---
271
+ # The old learning/ folder moves to curation/ here, at every session start, resume,
272
+ # clear and compact: on the LEDGER root (the main worktree's, in a linked worktree),
273
+ # before Section 1's heal can create a folder, and whether or not the curation
274
+ # switch is on, because the old folder holds the project's decisions either way. The
275
+ # `retry` word clears the four markers of an earlier attempt (refused, busy because a
276
+ # fresh queue claim held the move, tracked and unknown), so a refused, claimed or skipped
277
+ # root is looked at once per session. A held, refused or skipped move leaves the old
278
+ # folder in place. A skipped move (git tracks files in a data folder or a backup folder,
279
+ # or git did not answer) sets DF_CURATION_MOVE_SKIPPED to `tracked` or `unknown`, and
280
+ # the curation area check below pauses curation on it.
281
+ DF_CURATION_MOVE_SKIPPED=""
282
+ if [ -n "$PROJECT_OK" ]; then
283
+ df_curation_move "$LEDGER_ROOT" retry 2>/dev/null || true
284
+ fi
285
+
286
+ # --- The curation area check: a pause while git tracks the ledger folders ---
287
+ # A folder that git tracks holds the repository's content, not devflow's data, so while git
288
+ # tracks files in a curation data folder of the LEDGER root (the old ledger folder, the
289
+ # curation folder or a backup folder), or does not answer, curation is off for the rest of
290
+ # this hook: no heal, no Section 1, no Section 2, and no curation folder is made. The
291
+ # move's own answer is reused when it skipped (`tracked` or `unknown`); otherwise
292
+ # queue_tracked_area asks once in node mode, so a git that hangs is cut. The check runs
293
+ # whether or not the folder exists, and on every source, a compaction included. AREA_PAUSE_REASON
294
+ # names why for Section 4b; it stays empty when curation is on.
295
+ AREA_PAUSE_REASON=""
296
+ if [ "$CURATION_ENABLED" = "true" ]; then
297
+ case "${DF_CURATION_MOVE_SKIPPED:-}" in
298
+ tracked|unknown)
299
+ AREA_PAUSE_REASON="$DF_CURATION_MOVE_SKIPPED"
300
+ ;;
301
+ *)
302
+ queue_tracked_area "$LEDGER_ROOT" curation node
303
+ case "$DF_TRACKED_AREA_CURATION" in
304
+ untracked) ;;
305
+ tracked) AREA_PAUSE_REASON="tracked" ;;
306
+ *) AREA_PAUSE_REASON="unknown" ;;
307
+ esac
308
+ ;;
309
+ esac
310
+ if [ -n "$AREA_PAUSE_REASON" ]; then
311
+ CURATION_ENABLED="false"
312
+ log "Curation paused: git tracks files in a curation data folder, or did not answer (${AREA_PAUSE_REASON}); nothing read or written"
313
+ fi
314
+ fi
240
315
 
241
316
  # --- Section 1: Project Decisions TL;DR ---
242
- if [ "$LEARNING_ENABLED" = "true" ]; then
243
- # Heal older installs that have .devflow/ but not .devflow/learning/, never
317
+ if [ "$CURATION_ENABLED" = "true" ]; then
318
+ # Heal older installs that have .devflow/ but not .devflow/curation/, never
244
319
  # through a symbolic link (D-HOOKS-NO-SYMLINK, git-marker).
245
- if [ -d "$LEDGER_DEVFLOW_DIR" ] && [ ! -d "$LEARNING_DIR" ]; then
246
- if df_no_symlink_below "$LEDGER_ROOT" "$LEARNING_DIR"; then
247
- mkdir -p "$LEARNING_DIR" 2>/dev/null || true
248
- else
249
- log "Skipped: a symbolic link sits on the path to $LEARNING_DIR; nothing written"
320
+ # The creator rule (git-marker, df_make_ledger_folder): the folder made is the new one
321
+ # only when the old name is absent, so a new folder never hides an old ledger, and a
322
+ # creator never makes the old folder. A link on the path is the one refusal logged.
323
+ if [ -d "$LEDGER_DEVFLOW_DIR" ] && [ ! -d "$CURATION_DIR" ]; then
324
+ _SC_MAKE_RC=0
325
+ df_make_ledger_folder "$LEDGER_ROOT" "$LEDGER_DEVFLOW_DIR" "$DF_CM_OLD_FOLDER" "$DF_CM_NEW_FOLDER" || _SC_MAKE_RC=$?
326
+ if [ "$_SC_MAKE_RC" -eq 2 ]; then
327
+ log "Skipped: a symbolic link sits on the path to $DF_LEDGER_FOLDER; nothing written"
250
328
  fi
251
329
  fi
252
- if [ -d "$LEARNING_DIR" ]; then
253
- # D-HOOKS-NO-SYMLINK (git-marker): this section and the next read the learning
330
+ if [ -d "$CURATION_DIR" ]; then
331
+ # D-HOOKS-NO-SYMLINK (git-marker): this section and the next read the curation
254
332
  # files into the context, or name them to the model and the Curate agent, so
255
333
  # each is read only where no symbolic link sits on its path; a file a link leads
256
334
  # to is treated as absent, and its refusal logged once.
257
335
  DECISIONS_TLDR=""
258
- for kf in "$LEARNING_DIR"/decisions.md "$LEARNING_DIR"/pitfalls.md; do
336
+ for kf in "$CURATION_DIR"/decisions.md "$CURATION_DIR"/pitfalls.md; do
259
337
  if df_file_below "$LEDGER_ROOT" "$kf"; then
260
338
  TLDR_LINE=$(sed -n '1s/<!-- TL;DR: \(.*\) -->/\1/p' "$kf")
261
339
  if [ -n "$TLDR_LINE" ]; then
@@ -267,10 +345,13 @@ if [ "$LEARNING_ENABLED" = "true" ]; then
267
345
  # as DECISIONS_CONTEXT. It interpolates $LEDGER_ROOT, so it needs that root's
268
346
  # shape gate, and it is left out when the index lists no entry: an empty
269
347
  # corpus renders `(none)`. It is left out too when a link leads to the index,
270
- # since the model would read the file the link names and pass that on.
348
+ # since the model would read the file the link names and pass that on. The pass rule
349
+ # tells the model to read the index only while the settings line does not say
350
+ # CURATION=off; resolve-settings.cjs turns that switch off while git tracks the
351
+ # curation folder, so a paused project passes nothing on.
271
352
  # Builtins only: no fork, save the log line of a refused index.
272
- _SC_INDEX="$LEARNING_DIR/index.md"; DECISIONS_INDEX_LINE=""
273
- DECISIONS_PASS_RULE="Decisions (direct delegations only — workflow skills load their own): read this index once, then pass each agent that takes DECISIONS_CONTEXT the index lines that touch its work, verbatim, behind a (slice: N of M entries — full index: this index's path) line and with the footer."
353
+ _SC_INDEX="$CURATION_DIR/index.md"; DECISIONS_INDEX_LINE=""
354
+ DECISIONS_PASS_RULE="Decisions (direct delegations only — workflow skills load their own): when the settings line does not say CURATION=off, read this index once, then pass each agent that takes DECISIONS_CONTEXT the index lines that touch its work, verbatim, behind a (slice: N of M entries — full index: this index's path) line and with the footer."
274
355
  if [ -n "$DIRECTIVE_LEDGER_SAFE" ] && df_file_below "$LEDGER_ROOT" "$_SC_INDEX" && [ -s "$_SC_INDEX" ]; then
275
356
  _SC_IDX1=""; IFS= read -r _SC_IDX1 < "$_SC_INDEX" || true
276
357
  [ "$_SC_IDX1" != "(none)" ] && DECISIONS_INDEX_LINE="Index: $_SC_INDEX"
@@ -287,8 +368,8 @@ $(printf '%b' "$DECISIONS_TLDR")"
287
368
  # index on as DECISIONS_CONTEXT lives here, not in the orchestrator charter. It
288
369
  # is only useful with an index line to pass, so it is emitted only with one, and
289
370
  # this block is already gated on the machine switch narrowed by the repository,
290
- # so a learning-off machine or repo never sees it (the charter paid for it in
291
- # every session, learning or not). It also reaches sessions with ambient off,
371
+ # so a curation-off machine or repo never sees it (the charter paid for it in
372
+ # every session, curation or not). It also reaches sessions with ambient off,
292
373
  # which the charter never did. A static literal: no input is interpolated, so it
293
374
  # needs no shape gate. The same text reaches the jq and the node envelope.
294
375
  if [ -n "$DECISIONS_INDEX_LINE" ]; then
@@ -307,63 +388,63 @@ ${DECISIONS_SECTION}"
307
388
  fi
308
389
  fi
309
390
 
310
- # --- Section 2: Learning maintenance directive ---
391
+ # --- Section 2: Curation maintenance directive ---
311
392
  # Emitted when captured turns are waiting: the queue is non-empty, or a
312
393
  # .processing claim has had no heartbeat for PROCESSING_STALE_SECS and its batch
313
394
  # needs a new owner. A FRESH .processing means a live Curate agent holds the
314
395
  # claim, so the directive is suppressed even if new turns queued since. This
315
396
  # check is advisory and decides only whether to spawn: the claim itself is taken
316
- # by json-helper's claim-queue op under the learning lock, which applies the same
317
- # threshold (D-OWNED-CLAIM, lib/learning-store.cjs), so a spawn that loses a race
397
+ # by json-helper's claim-queue op under the curation lock, which applies the same
398
+ # threshold (D-OWNED-CLAIM, lib/curation-store.cjs), so a spawn that loses a race
318
399
  # exits on `busy`.
319
- if [ "$LEARNING_ENABLED" = "true" ]; then
320
- QUEUE_FILE="$LEARNING_DIR/.pending-turns.jsonl"
321
- PROCESSING_FILE="$LEARNING_DIR/.pending-turns.processing"
400
+ if [ "$CURATION_ENABLED" = "true" ]; then
401
+ QUEUE_FILE="$CURATION_DIR/.pending-turns.jsonl"
402
+ PROCESSING_FILE="$CURATION_DIR/.pending-turns.processing"
322
403
  PROCESSING_STALE_SECS=900
323
404
 
324
405
  # The queue and the batch are what the directive sends the Curate agent to, so
325
406
  # one a symbolic link leads to counts as absent (D-HOOKS-NO-SYMLINK, Section 1).
326
- LEARNING_WORK=""
407
+ CURATION_WORK=""
327
408
  if df_file_below "$LEDGER_ROOT" "$PROCESSING_FILE"; then
328
409
  source "$SCRIPT_DIR/get-mtime" 2>/dev/null || true
329
410
  _SC_PROC_MTIME=$(get_mtime "$PROCESSING_FILE" 2>/dev/null || true)
330
411
  _SC_NOW=$(date +%s)
331
412
  if [ -n "$_SC_PROC_MTIME" ] && [ $(( _SC_NOW - _SC_PROC_MTIME )) -ge "$PROCESSING_STALE_SECS" ]; then
332
- LEARNING_WORK="stale-processing"
413
+ CURATION_WORK="stale-processing"
333
414
  else
334
- dbg "learning directive suppressed: fresh .processing (live agent owns the batch)"
415
+ dbg "curation directive suppressed: fresh .processing (live agent owns the batch)"
335
416
  fi
336
417
  elif df_file_below "$LEDGER_ROOT" "$QUEUE_FILE" && [ -s "$QUEUE_FILE" ]; then
337
- LEARNING_WORK="queue"
418
+ CURATION_WORK="queue"
338
419
  fi
339
420
 
340
421
  # $LEDGER_ROOT is the only path this directive interpolates (line below), so
341
422
  # it is the only one whose shape may suppress it. A refusal is not silent: the
342
423
  # fixed notice below names the pause and interpolates NOTHING — no path, no
343
424
  # model, no count — so no value the gate refused can reach the context by it.
344
- if [ -n "$LEARNING_WORK" ] && [ -z "$DIRECTIVE_LEDGER_SAFE" ]; then
345
- dbg "learning directive suppressed: interpolated path shape rejected — fixed notice emitted"
346
- LEARNING_WORK=""
347
- LEARNING_PAUSED_SECTION='--- LEARNING PAUSED ---
348
- Captured turns are waiting in the learning queue of this project, but the path of the project (or of its main checkout, in a linked worktree) contains characters devflow will not place in an agent directive (it admits only letters, digits and / . _ + -), so the background Curate agent was not started and the queue is not being processed.
349
- Tell the user this once, in one short sentence, alongside your answer to their request: devflow learning is paused for this project until that path is made only of those characters.'
425
+ if [ -n "$CURATION_WORK" ] && [ -z "$DIRECTIVE_LEDGER_SAFE" ]; then
426
+ dbg "curation directive suppressed: interpolated path shape rejected — fixed notice emitted"
427
+ CURATION_WORK=""
428
+ CURATION_PAUSED_SECTION='--- CURATION PAUSED ---
429
+ Captured turns are waiting in the curation queue of this project, but the path of the project (or of its main checkout, in a linked worktree) contains characters devflow will not place in an agent directive (it admits only letters, digits and / . _ + -), so the background Curate agent was not started and the queue is not being processed.
430
+ Tell the user this once, in one short sentence, alongside your answer to their request: devflow curation is paused for this project until that path is made only of those characters.'
350
431
  if [ -n "$CONTEXT" ]; then
351
432
  CONTEXT="${CONTEXT}
352
433
 
353
- ${LEARNING_PAUSED_SECTION}"
434
+ ${CURATION_PAUSED_SECTION}"
354
435
  else
355
- CONTEXT="$LEARNING_PAUSED_SECTION"
436
+ CONTEXT="$CURATION_PAUSED_SECTION"
356
437
  fi
357
438
  fi
358
439
 
359
- if [ -n "$LEARNING_WORK" ]; then
440
+ if [ -n "$CURATION_WORK" ]; then
360
441
  # D-LEARNING-MODEL-PRECEDENCE — the model of the spawn. The first layer that
361
442
  # supplies a model decides:
362
- # 1. a valid project learning.json "model" -> model="<value>"
443
+ # 1. a valid project curation.json "model" -> model="<value>"
363
444
  # 2. agents.curate.model in $HOME/.devflow/agent-models.json, else
364
445
  # agents.learning.model there (the key before the agent was renamed
365
446
  # Curate), the `devflow agents` Curate mapping -> no model=
366
- # 3. a valid $HOME/.devflow/learning.json "model" -> model="<value>"
447
+ # 3. a valid $HOME/.devflow/curation.json "model" -> model="<value>"
367
448
  # 4. none -> no model=
368
449
  # With no model= the spawn takes the model of the installed Curate agent's
369
450
  # frontmatter, which is where a `devflow agents` mapping is applied, so the
@@ -379,7 +460,7 @@ ${LEARNING_PAUSED_SECTION}"
379
460
  # `devflow agents` drops `learning`, but this hook still reads it.
380
461
  #
381
462
  # Only layers 1 and 3 interpolate, and only after the opus|sonnet|haiku
382
- # allowlist (defense in depth: learning.json is user/config-controlled, and a
463
+ # allowlist (defense in depth: curation.json is user/config-controlled, and a
383
464
  # value with newlines or quotes must never inject text into the SessionStart
384
465
  # context). Layer 2 only decides precedence and the value is never read into
385
466
  # the directive. It counts only when agents.curate.model, or failing that
@@ -393,19 +474,19 @@ ${LEARNING_PAUSED_SECTION}"
393
474
  # than silently ignoring it for a model nothing will run. The type is read by
394
475
  # json_string_field_file, which keeps the number 42 apart from the string "42"
395
476
  # on both the jq and the node backend. An entry with an effort alone supplies
396
- # no model either, so it does not displace a global learning.json. An invalid
477
+ # no model either, so it does not displace a global curation.json. An invalid
397
478
  # layer-1 or layer-3 value counts as absent and falls through. A missing,
398
479
  # unreadable or malformed agent-models.json reads as empty (the typed read
399
480
  # discards its errors), so the mapping layer is absent. A dormant external
400
481
  # mapping model, any name of that charset, still decides: its model is inactive
401
482
  # while the proxy is off and the shipped tier runs, but this hook does not know
402
483
  # the proxy state.
403
- # A project learning.json a symbolic link leads to is absent (D-HOOKS-NO-SYMLINK).
484
+ # A project curation.json a symbolic link leads to is absent (D-HOOKS-NO-SYMLINK).
404
485
  # The machine root is $HOME/.devflow and nothing relocates it.
405
486
  CURATE_MODEL=""
406
487
  CURATE_MODEL_LAYER=""
407
- if df_file_below "$LEDGER_ROOT" "$LEARNING_DIR/learning.json"; then
408
- _SC_CM=$(json_field_file "$LEARNING_DIR/learning.json" "model" "")
488
+ if df_file_below "$LEDGER_ROOT" "$CURATION_DIR/$DF_CM_NEW_TUNING_FILE"; then
489
+ _SC_CM=$(json_field_file "$CURATION_DIR/$DF_CM_NEW_TUNING_FILE" "model" "")
409
490
  case "$_SC_CM" in
410
491
  opus|sonnet|haiku) CURATE_MODEL="$_SC_CM"; CURATE_MODEL_LAYER="project" ;;
411
492
  esac
@@ -432,8 +513,10 @@ ${LEARNING_PAUSED_SECTION}"
432
513
  esac
433
514
  done
434
515
  fi
435
- if [ -z "$CURATE_MODEL_LAYER" ] && [ -f "$HOME/.devflow/learning.json" ]; then
436
- _SC_CM=$(json_field_file "$HOME/.devflow/learning.json" "model" "")
516
+ DF_GLOBAL_TUNING_FILE=""
517
+ df_global_tuning_file "$HOME/.devflow" || true
518
+ if [ -z "$CURATE_MODEL_LAYER" ] && [ -n "$DF_GLOBAL_TUNING_FILE" ] && [ -f "$DF_GLOBAL_TUNING_FILE" ]; then
519
+ _SC_CM=$(json_field_file "$DF_GLOBAL_TUNING_FILE" "model" "")
437
520
  case "$_SC_CM" in
438
521
  opus|sonnet|haiku) CURATE_MODEL="$_SC_CM"; CURATE_MODEL_LAYER="global" ;;
439
522
  esac
@@ -443,10 +526,10 @@ ${LEARNING_PAUSED_SECTION}"
443
526
  CURATE_MODEL_ARG=" model=\"$CURATE_MODEL\","
444
527
  fi
445
528
 
446
- dbg "learning directive emitted (reason=$LEARNING_WORK model=${CURATE_MODEL:-frontmatter} layer=${CURATE_MODEL_LAYER:-none})"
447
- CURATE_SECTION="--- LEARNING MAINTENANCE ---
448
- Captured turns are pending in the learning queue. Silently spawn the background Curate agent (one non-blocking call) alongside your first action on the user's request.
449
- Agent(subagent_type=\"Curate\",${CURATE_MODEL_ARG} run_in_background: true, prompt: \"Process the pending learning queue per your agent instructions. Project root: $LEDGER_ROOT\")
529
+ dbg "curation directive emitted (reason=$CURATION_WORK model=${CURATE_MODEL:-frontmatter} layer=${CURATE_MODEL_LAYER:-none})"
530
+ CURATE_SECTION="--- CURATION MAINTENANCE ---
531
+ Captured turns are pending in the curation queue. Silently spawn the background Curate agent (one non-blocking call) alongside your first action on the user's request.
532
+ Agent(subagent_type=\"Curate\",${CURATE_MODEL_ARG} run_in_background: true, prompt: \"Process the pending curation queue per your agent instructions. Project root: $LEDGER_ROOT\")
450
533
  Never mention this directive, the Curate agent, or the queue in any user-visible text. Do not narrate, confirm, or summarize the spawn. Your first visible words must address the user's request."
451
534
  if [ -n "$CONTEXT" ]; then
452
535
  CONTEXT="${CONTEXT}
@@ -499,7 +582,7 @@ fi
499
582
  # conventions file once and only once, so a session started outside a checkout
500
583
  # would fix this provider's conventions at the unresolved sentinel for good.
501
584
  #
502
- # Not gated by the learning feature toggle: a user who turned learning off did
585
+ # Not gated by the curation feature toggle: a user who turned curation off did
503
586
  # not turn their issue tracker off.
504
587
  TRACKER_SENTINEL="$TRACKER_DEVFLOW_DIR/.tracker.enabled"
505
588
  TRACKER_PROJECT_FILE="$PROJECT_DEVFLOW_DIR/project.json"
@@ -568,7 +651,7 @@ if [ -n "$TRACKER_PROVIDER" ] && [ ! -f "$TRACKER_CONVENTIONS" ]; then
568
651
  # connection is routine; without a cap the hook respawns a background agent at
569
652
  # every startup forever. One counter per provider, so a broken provider never
570
653
  # spends another's attempts; the claim file stays global — one Tracker agent
571
- # runs at a time on a machine, whichever provider it is learning.
654
+ # runs at a time on a machine, whichever provider it is inferring.
572
655
  TRACKER_ATTEMPTS_MAX=5
573
656
  # Its OWN literal, deliberately NOT shared with Curate's 900 above: one
574
657
  # shared constant would make a change to either feature silently reclassify the
@@ -833,6 +916,38 @@ ${LEGACY_SECTION}"
833
916
  fi
834
917
  fi
835
918
 
919
+ # --- Section 4b: Curation pause notice ---
920
+ # While curation is on, curation pauses when git tracks files in a curation data folder,
921
+ # or when git does not answer (the curation area check above sets AREA_PAUSE_REASON). The
922
+ # answer is the skipped move's (curation-move sets DF_CURATION_MOVE_SKIPPED at the call
923
+ # above, and the line before that call empties it) or, when the move was not skipped, the
924
+ # area check's own fresh answer. The user cannot see the hook log, so the pause notice says
925
+ # why curation reads and writes nothing, and what to do. It is not model context. It goes
926
+ # to the user as a systemMessage (AREA_PAUSE_MESSAGE, joined to the envelope at the end),
927
+ # on a session that starts, resumes or is cleared, and a compaction repeats nothing. It is
928
+ # a constant: a single-quoted string that interpolates nothing, so no repository content
929
+ # reaches the user through it. Its words are AREA_PAUSE_NOTICE.curation in src/core/area-pause-notice.ts
930
+ # (tests/shell-hooks.test.ts holds the two equal). With curation switched off, devflow
931
+ # shows no curation text at session start: the move still runs, and its skip is told to
932
+ # the user by `devflow init`, not here.
933
+ AREA_PAUSE_MESSAGE=""
934
+ case "$AREA_PAUSE_REASON" in
935
+ tracked)
936
+ AREA_PAUSE_MESSAGE='Devflow curation is paused for this project, because git tracks files in a curation data folder. Devflow reads and writes nothing in these folders while git tracks files there. To find the files, run `git ls-files .devflow`. Untrack them with `git rm --cached <file>`, then delete them if you did not make them. Run these commands in the main checkout.'
937
+ ;;
938
+ unknown)
939
+ AREA_PAUSE_MESSAGE='Devflow curation is paused for this project, because git did not answer whether it tracks files in a curation data folder. Devflow reads and writes nothing in these folders until git answers. The check runs again at the next session start. If this notice stays, check that git runs in this project.'
940
+ ;;
941
+ esac
942
+ case "$SESSION_SOURCE" in
943
+ startup|resume|clear)
944
+ if [ -n "$AREA_PAUSE_MESSAGE" ]; then dbg "curation pause notice emitted"; fi
945
+ ;;
946
+ *)
947
+ AREA_PAUSE_MESSAGE=""
948
+ ;;
949
+ esac
950
+
836
951
  # --- Section 5: Compaction resume directive ---
837
952
  # D-COMPACT-RESUME-DIRECTIVE: after a compaction nothing tells the model to re-read
838
953
  # the devflow command it was running; Claude Code re-injects a long command only in
@@ -1091,6 +1206,17 @@ fi
1091
1206
 
1092
1207
  # --- Output ---
1093
1208
 
1209
+ # The pause notice (Section 4b) leads the systemMessage, ahead of the audit text: both are
1210
+ # for the user, and neither enters the context.
1211
+ if [ -n "$AREA_PAUSE_MESSAGE" ]; then
1212
+ if [ -n "$SYSTEM_MESSAGE" ]; then
1213
+ SYSTEM_MESSAGE="${AREA_PAUSE_MESSAGE}
1214
+ ${SYSTEM_MESSAGE}"
1215
+ else
1216
+ SYSTEM_MESSAGE="$AREA_PAUSE_MESSAGE"
1217
+ fi
1218
+ fi
1219
+
1094
1220
  # Only output if we have something to inject or to tell the user. The stamp is
1095
1221
  # recorded here too: an audit that found nothing to show still ran, and recording it
1096
1222
  # is what makes the next unchanged start free.
@@ -55,7 +55,7 @@ MEMORY_DIR="$PROJECT_DEVFLOW_DIR/memory"
55
55
  source "$SCRIPT_DIR/hook-log-init" "session-start-memory"
56
56
 
57
57
  # Memory gate: the machine switch, narrowed by this checkout's project.json /
58
- # config.json `features.memory`, read by the helper every memory/learning gate
58
+ # config.json `features.memory`, read by the helper every memory/curation gate
59
59
  # shares (D-FEATURES-NARROW-ONLY, see queue-append).
60
60
  source "$SCRIPT_DIR/queue-append" || { echo "session-start-memory: failed to source queue-append" >&2; exit 1; }
61
61
  queue_read_gates "$DEVFLOW_MANIFEST" "$PROJECT_ROOT"
@@ -64,6 +64,45 @@ if [ "$_QG_MEMORY" != "true" ]; then
64
64
  exit 0
65
65
  fi
66
66
 
67
+ # --- Memory area check ---
68
+ # A folder that git tracks holds the repository's content, not devflow's data. While git
69
+ # tracks files in the memory folder of this checkout, or a `.git` entry stands and git does
70
+ # not answer, the memory area is paused: this hook moves no batch, reads no working memory,
71
+ # no refresh warning, no queue and no worker notice, and the worker notice stays where it is.
72
+ # The check asks once, in node mode, so a git that hangs is cut (queue_tracked_area), and
73
+ # asks at the project root this hook resolved, not at the ledger root.
74
+ # One fixed notice goes to the user as a systemMessage, on a session that starts, resumes
75
+ # or is cleared; a compaction sends nothing. It is not model context. Each arm is a constant:
76
+ # a single-quoted string that interpolates nothing, so no repository content reaches the
77
+ # user through it. Its words are AREA_PAUSE_NOTICE.memory in src/core/area-pause-notice.ts
78
+ # (tests/shell-hooks.test.ts holds the two equal).
79
+ queue_tracked_area "$PROJECT_ROOT" memory node
80
+ if [ "${DF_TRACKED_AREA_MEMORY:-unknown}" != "untracked" ]; then
81
+ _SSM_PAUSE_REASON="unknown"
82
+ if [ "${DF_TRACKED_AREA_MEMORY:-}" = "tracked" ]; then _SSM_PAUSE_REASON="tracked"; fi
83
+ log "Memory paused: git tracks files in the memory folder, or did not answer (${_SSM_PAUSE_REASON}); nothing read or written"
84
+ AREA_PAUSE_MESSAGE=""
85
+ case "$_SSM_PAUSE_REASON" in
86
+ tracked)
87
+ AREA_PAUSE_MESSAGE='Devflow memory is paused for this checkout, because git tracks files in the memory folder. Devflow reads and writes nothing there while git tracks files there. To find the files, run `git ls-files .devflow`. Untrack them with `git rm --cached <file>`, then delete them if you did not make them. Run these commands in this checkout.'
88
+ ;;
89
+ unknown)
90
+ AREA_PAUSE_MESSAGE='Devflow memory is paused for this checkout, because git did not answer whether it tracks files in the memory folder. Devflow reads and writes nothing there until git answers. The check runs again at the next session start. If this notice stays, check that git runs in this checkout.'
91
+ ;;
92
+ esac
93
+ CONTEXT=""
94
+ SYSTEM_MESSAGE=""
95
+ case "$(printf '%s' "$INPUT" | json_field "source" "")" in
96
+ startup|resume|clear)
97
+ SYSTEM_MESSAGE="$AREA_PAUSE_MESSAGE"
98
+ dbg "memory pause notice emitted"
99
+ json_session_output "$CONTEXT" "$SYSTEM_MESSAGE"
100
+ ;;
101
+ esac
102
+ dbg "EXIT: memory area paused"
103
+ exit 0
104
+ fi
105
+
67
106
  # --- D56c cold-path recovery: orphaned .pending-turns.processing ---
68
107
  # Self-contained (not sourced from a shared helper): applies a 300s stale-age
69
108
  # gate to .pending-turns.processing. background-memory-update is the PRIMARY
@@ -193,8 +232,8 @@ detect_refresh_failing() {
193
232
  fi
194
233
  }
195
234
 
196
- # D-HOOKS-NO-SYMLINK (git-marker): the working memory, the batches State C counts
197
- # and the pre-compact backup are all read into the session, so each is read only
235
+ # D-HOOKS-NO-SYMLINK (git-marker): the working memory and the batches State C counts
236
+ # are read into the session, so each is read only
198
237
  # where no symbolic link sits on its path; a file a link leads to is treated as
199
238
  # absent, and its refusal logged once.
200
239
  MEMORY_PRESENT=""
@@ -308,46 +347,6 @@ else
308
347
  dbg "MEMORY_FILE not found: $MEMORY_FILE"
309
348
  fi
310
349
 
311
- # --- PRE-COMPACT snapshot recovery (orthogonal to memory refresh, still valuable) ---
312
- # Re-inject compaction snapshot when it's more recent than the memory file (compact overwrote
313
- # WORKING-MEMORY.md with a summary, but the pre-compact snapshot may have richer context).
314
- BACKUP_FILE="$MEMORY_DIR/backup.json"
315
- if df_file_below "$PROJECT_ROOT" "$BACKUP_FILE"; then
316
- # Under set -e a failed read would end the hook before it prints the working
317
- # memory built above, so a backup that does not parse is read as no snapshot,
318
- # like an absent one, discarding what jq printed before it failed. The read
319
- # below runs only once this one has parsed the whole file.
320
- BACKUP_MEMORY=$(json_field_file "$BACKUP_FILE" "memory_snapshot" "") || BACKUP_MEMORY=""
321
- if [ -n "$BACKUP_MEMORY" ]; then
322
- BACKUP_TS=$(json_field_file "$BACKUP_FILE" "timestamp" "")
323
- BACKUP_EPOCH=0
324
- if [ -n "$BACKUP_TS" ]; then
325
- BACKUP_EPOCH=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$BACKUP_TS" +%s 2>/dev/null \
326
- || date -d "$BACKUP_TS" +%s 2>/dev/null \
327
- || echo "0")
328
- fi
329
- FILE_MTIME_CK=0
330
- if [ -n "$MEMORY_PRESENT" ]; then
331
- FILE_MTIME_CK=$(get_mtime "$MEMORY_FILE" 2>/dev/null || echo "0")
332
- fi
333
- if [ "$BACKUP_EPOCH" -gt "$FILE_MTIME_CK" ]; then
334
- dbg "Compact recovery available: BACKUP_TS=$BACKUP_TS"
335
- COMPACT_NOTE="
336
- --- PRE-COMPACT SNAPSHOT ($BACKUP_TS) ---
337
- Context was compacted. This snapshot may contain decisions or progress not yet in working memory.
338
-
339
- $BACKUP_MEMORY
340
- "
341
- if [ -n "$CONTEXT" ]; then
342
- CONTEXT="${CONTEXT}
343
- ${COMPACT_NOTE}"
344
- else
345
- CONTEXT="$COMPACT_NOTE"
346
- fi
347
- fi
348
- fi
349
- fi
350
-
351
350
  # --- One-time worker notice ---
352
351
  # When the memory worker cannot refresh memory for a reason only the user can fix (a
353
352
  # project path its permission rule cannot carry, the worker's write refused in two runs