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
@@ -3,7 +3,9 @@
3
3
  # ignores .devflow/. Called from capture-prompt, capture-turn, capture-question,
4
4
  # memory-worker, and pre-compact-memory. Idempotent.
5
5
  # Returns non-zero, having written nothing, outside a git project, at HOME, or when
6
- # .devflow is a symbolic link.
6
+ # .devflow is a symbolic link. Also non-zero when it is given no root, when the ledger
7
+ # folder check fails (df_ledger_folder), and when the scaffold folders cannot be made.
8
+ # A missing curation-move shim is no failure: the ledger folder step is skipped.
7
9
  # Usage: source ensure-devflow-init "$CWD"
8
10
 
9
11
  [ -z "$1" ] && return 1
@@ -24,12 +26,14 @@ _EDI_LEDGER="${DF_LEDGER_ROOT:-$_EDI_ROOT}"
24
26
  # `.git` of its own, so it is refused rather than scaffolded. This is the one gate every capture and
25
27
  # memory caller (capture-prompt, capture-turn, capture-question, memory-worker,
26
28
  # pre-compact-memory) passes through before it writes, and each of them treats a
27
- # non-zero return as "stop" (`|| exit 0`), so memory and learning stop together
29
+ # non-zero return as "stop" (`|| exit 0`), so memory and curation stop together
28
30
  # in a non-git directory. ensure-root-gitignore itself stays ungated: it is a
29
31
  # pure writer its parity suite runs in plain directories, and both of
30
- # its callers now decide before reaching it. A helper that failed to source is a
31
- # command not found — the refusing branch, so the failure mode writes nothing.
32
- source "$_EDI_DIR/git-marker" 2>/dev/null || true
32
+ # its callers now decide before reaching it. A helper that is missing is a
33
+ # command not found — the refusing branch, so the failure mode writes nothing. Each
34
+ # source is guarded by a builtin readability test: bash 3.2 under `set -e` ends the
35
+ # shell at a `source` of a missing file, even inside `|| true`.
36
+ [ -r "$_EDI_DIR/git-marker" ] && source "$_EDI_DIR/git-marker" 2>/dev/null || true
33
37
  df_is_project_root "$_EDI_ROOT" 2>/dev/null || return 1
34
38
 
35
39
  _DEVFLOW_DIR="$_EDI_ROOT/.devflow"
@@ -45,32 +49,60 @@ if ! df_no_symlink_below "$_EDI_ROOT" "$_DEVFLOW_DIR"; then
45
49
  return 1
46
50
  fi
47
51
 
48
- # learning/ is scaffolded only where the ledger lives. In a linked worktree whose
52
+ # The one-time move of the old learning/ ledger folder to curation/ (curation-move):
53
+ # before anything below reads or creates the ledger folder, on the LEDGER root (the
54
+ # main worktree's, in a linked worktree), and whether or not the curation switch is
55
+ # on. A move another process holds, or a refused one, leaves the old folder where it
56
+ # is; the creator rule below then takes the old folder as the ledger folder. A shim that
57
+ # is missing skips the move, and the ledger folder step below.
58
+ [ -r "$_EDI_DIR/curation-move" ] && source "$_EDI_DIR/curation-move" 2>/dev/null || true
59
+ df_curation_move "$_EDI_LEDGER" 2>/dev/null || true
60
+
61
+ # curation/ is scaffolded only where the ledger lives. In a linked worktree whose
49
62
  # main checkout holds the ledger (D-LEDGER-MAIN-WORKTREE, resolve-project-root) a
50
- # learning/ here would be an empty directory nothing writes to; the capture hooks
51
- # create the ledger's own learning/ at the ledger root when they append to it.
52
- _EDI_LEARNING="$_DEVFLOW_DIR/learning"
53
- [ "$_EDI_LEDGER" = "$_EDI_ROOT" ] || _EDI_LEARNING=""
63
+ # curation/ here would be an empty directory nothing writes to; the capture hooks
64
+ # create the ledger's own curation/ at the ledger root when they append to it.
65
+ # The creator rule (git-marker, df_make_ledger_folder): the folder made is the new one
66
+ # only when the old name is absent, so a new folder never hides an old ledger, and a
67
+ # creator never makes the old folder. The names come from the move shim
68
+ # (df_curation_move_names), the one shell place that holds them. Either name satisfies
69
+ # the fast path below, so a root whose old folder is still there (held, refused or not
70
+ # yet tried) forks no mkdir.
71
+ # Without the shim there are no names, and this step has no ledger folder to check or
72
+ # make: the rest of the scaffold goes on.
73
+ _EDI_CURATION=""
74
+ if df_curation_move_names 2>/dev/null; then
75
+ df_ledger_folder "$_DEVFLOW_DIR" "$DF_CM_OLD_FOLDER" "$DF_CM_NEW_FOLDER" || return 1
76
+ _EDI_CURATION="$DF_LEDGER_FOLDER"
77
+ [ "$_EDI_LEDGER" = "$_EDI_ROOT" ] || _EDI_CURATION=""
78
+ fi
54
79
 
55
80
  # Fast-path: if all subdirectories already exist, skip mkdir and gitignore setup.
56
81
  # The marker is the CURRENT carve-out version (D-GITIGNORE-V6), bumped with both
57
82
  # writers' stamps, so a v5-stamped project misses this once and gains the
58
83
  # `!.devflow/project.json` line.
59
84
  if [ -d "$_DEVFLOW_DIR/memory" ] && [ -d "$_DEVFLOW_DIR/docs" ] && \
60
- { [ -z "$_EDI_LEARNING" ] || [ -d "$_EDI_LEARNING" ]; } && \
85
+ { [ -z "$_EDI_CURATION" ] || [ -d "$_EDI_CURATION" ]; } && \
61
86
  [ -d "$_DEVFLOW_DIR/features" ] && \
62
87
  [ -f "$_DEVFLOW_DIR/.root-gitignore-configured-v6" ]; then
63
88
  return 0
64
89
  fi
65
90
 
66
- # Create all subdirectories
91
+ # Create all subdirectories. The ledger folder is made by its own guarded creator, never
92
+ # by this mkdir: a probe that saw the old folder must not bring it back after a move.
67
93
  mkdir -p \
68
94
  "$_DEVFLOW_DIR/memory" \
69
- ${_EDI_LEARNING:+"$_EDI_LEARNING"} \
70
95
  "$_DEVFLOW_DIR/features" \
71
96
  "$_DEVFLOW_DIR/docs" \
72
97
  2>/dev/null || return 1
73
98
 
99
+ # A ledger folder that could not be made leaves the memory scaffold as it is, and the
100
+ # next event looks again. A link on its path makes the capture hooks refuse its queue;
101
+ # after a failed mkdir, the append makes the folder again, as its creator.
102
+ if [ -n "$_EDI_CURATION" ]; then
103
+ df_make_ledger_folder "$_EDI_ROOT" "$_DEVFLOW_DIR" "$DF_CM_OLD_FOLDER" "$DF_CM_NEW_FOLDER" || true
104
+ fi
105
+
74
106
  # One-time root .gitignore setup — delegated to the sibling ensure-root-gitignore
75
107
  # helper (single source of truth) so the always-on, memory-independent
76
108
  # session-start-context hook applies the identical rule. _EDI_DIR and _EDI_ROOT
@@ -22,7 +22,7 @@
22
22
  dbg() { :; }
23
23
 
24
24
  # Re-entrancy guard — must come before hook-bootstrap to minimize overhead
25
- # in background worker sessions (claude -p for memory / learning workers fire hooks too)
25
+ # in background worker sessions (claude -p for memory / curation workers fire hooks too)
26
26
  if [ "${DEVFLOW_BG_UPDATER:-}" = "1" ]; then exit 0; fi
27
27
 
28
28
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
@@ -266,11 +266,11 @@ fi
266
266
  # Acquire spawn lock — prevents concurrent sessions from double-spawning the relay
267
267
  # Stale break: 30s (lock abandoned by crashed hook)
268
268
  source "$SCRIPT_DIR/get-mtime" 2>/dev/null || true
269
- source "$SCRIPT_DIR/learning-lock" 2>/dev/null || true
269
+ source "$SCRIPT_DIR/curation-lock" 2>/dev/null || true
270
270
 
271
271
  SPAWN_LOCK="$MACHINE_DEVFLOW_DIR/.proxy-spawn.lock"
272
272
 
273
- if ! learning_lock_acquire "$SPAWN_LOCK" 2 2>/dev/null; then
273
+ if ! curation_lock_acquire "$SPAWN_LOCK" 2 2>/dev/null; then
274
274
  # Another session is racing to start the proxy — give it a moment then re-probe
275
275
  log "spawn lock busy — another session may be starting proxy"
276
276
  sleep 1
@@ -339,7 +339,7 @@ while [ "$_i" -lt 80 ]; do
339
339
  _i=$(( _i + 1 ))
340
340
  done
341
341
 
342
- learning_lock_release "$SPAWN_LOCK"
342
+ curation_lock_release "$SPAWN_LOCK"
343
343
 
344
344
  if [ "$_RELAY_UP" = "true" ]; then
345
345
  log "relay ready after $(( _i + 1 )) probe(s)"
@@ -2,7 +2,7 @@
2
2
  # ensure-root-gitignore — single source of truth for the project root .gitignore
3
3
  # rules that govern .devflow/.
4
4
  #
5
- # .devflow/ holds per-developer runtime state (memory, learning, docs, locks) —
5
+ # .devflow/ holds per-developer runtime state (memory, curation, docs, locks) —
6
6
  # local by default. FOUR exceptions are shared via git:
7
7
  # 1. Feature knowledge bases under .devflow/features/: index.md and every
8
8
  # {slug}/KNOWLEDGE.md (tracked, committed by the Knowledge agent at workflow end).
@@ -22,9 +22,9 @@
22
22
  # so git never descends and later negations are dead. Hence the multi-line block.
23
23
  #
24
24
  # Sourced by:
25
- # - ensure-devflow-init (reached via the memory/learning hooks)
25
+ # - ensure-devflow-init (reached via the memory/curation hooks)
26
26
  # - session-start-context (always-on, memory-independent — covers memory-off
27
- # projects that still use learning/knowledge)
27
+ # projects that still use curation/knowledge)
28
28
  # Both reach this one writer so the rule is applied identically everywhere; this
29
29
  # decouples git-tracking of .devflow/ from any single feature toggle.
30
30
  #
@@ -201,7 +201,7 @@ mkdir -p "$_ERG_DEVFLOW_DIR" 2>/dev/null || return 1
201
201
  # DEVFLOW_GITIGNORE_BLOCK_WITHOUT_CLAUDEIGNORE in
202
202
  # src/targets/claude-code/post-install.ts.
203
203
  printf -v _ERG_BLOCK_NO_CI '%s\n' \
204
- '# Devflow runtime data — local by default (memory, learning, docs, locks).' \
204
+ '# Devflow runtime data — local by default (memory, curation, docs, locks).' \
205
205
  '# Shared via git: feature knowledge bases under .devflow/features/ (index.md and' \
206
206
  '# every {slug}/KNOWLEDGE.md), .devflow/conventions.md (naming authority),' \
207
207
  '# .devflow/policy.json (retired; presence only) and .devflow/project.json (team settings).' \
@@ -133,3 +133,84 @@ df_file_below() {
133
133
  fi
134
134
  return 1
135
135
  }
136
+
137
+ # df_ledger_folder <devflow_dir> <old> [<new>] — sets DF_LEDGER_FOLDER to the path of
138
+ # the ledger folder a creator may create or write into: <devflow_dir>/<old> while that
139
+ # name is present (a folder, a file or a link, dangling or not), else
140
+ # <devflow_dir>/<new>. <new> defaults to <old>, so a caller that names only the old
141
+ # folder gets the old folder's path in every state.
142
+ #
143
+ # The creator rule: a creator makes the new folder only when the old name is absent.
144
+ # A new folder made beside a present old one would hide the old ledger from every
145
+ # reader, so the session-start heal, the ensure-devflow-init scaffold and queue
146
+ # append all take their folder from here. It answers a path and creates nothing; the
147
+ # caller checks the path with df_no_symlink_below and creates it.
148
+ #
149
+ # Zero forks: builtin tests and parameter expansion only. It always returns 0; read
150
+ # the answer from DF_LEDGER_FOLDER, not from a command substitution.
151
+ df_ledger_folder() {
152
+ local _dir="$1" _old="$2" _new="${3:-$2}"
153
+ if [ -e "$_dir/$_old" ] || [ -L "$_dir/$_old" ]; then
154
+ DF_LEDGER_FOLDER="$_dir/$_old"
155
+ else
156
+ DF_LEDGER_FOLDER="$_dir/$_new"
157
+ fi
158
+ return 0
159
+ }
160
+
161
+ # df_ledger_queue <queue_file> <old> [<new>] — sets DF_LEDGER_QUEUE to the queue file
162
+ # a creator may append to. A queue file whose folder is named <old> or <new> is
163
+ # answered under the folder df_ledger_folder picks for that folder's parent; a queue
164
+ # in any other folder (the memory queue) and a path with fewer than two parts come
165
+ # back unchanged. <new> defaults to <old>. Creates nothing; zero forks; returns 0.
166
+ df_ledger_queue() {
167
+ local _file="$1" _old="$2" _new="${3:-$2}" _folder="" _parent="" _name=""
168
+ DF_LEDGER_QUEUE="$_file"
169
+ case "$_file" in */*/*) ;; *) return 0 ;; esac
170
+ _folder="${_file%/*}"
171
+ _parent="${_folder%/*}"
172
+ _name="${_folder##*/}"
173
+ case "$_name" in
174
+ "$_old"|"$_new")
175
+ df_ledger_folder "$_parent" "$_old" "$_new"
176
+ DF_LEDGER_QUEUE="$DF_LEDGER_FOLDER/${_file##*/}"
177
+ ;;
178
+ esac
179
+ return 0
180
+ }
181
+
182
+ # df_make_ledger_folder <root> <devflow_dir> <old> <new> — the one creator of the
183
+ # ledger folder for a hook. It sets DF_LEDGER_FOLDER as df_ledger_folder does, then
184
+ # makes the NEW folder and only the new one, and only where the old name is absent at
185
+ # the probe and no link sits on the way down from <root> (df_no_symlink_below). Where
186
+ # the old name is present it makes nothing, so a creator never makes the old folder:
187
+ # a `mkdir -p` on it would bring back an empty old folder beside the new one after a
188
+ # move had renamed it away. It logs nothing, so each hook words its own skip once.
189
+ # A predicate: it returns 0 when nothing is left to make or the folder was made, 2 for
190
+ # a link on the path (nothing is made), and 1 for an empty argument or a failed mkdir.
191
+ # Call it in an `if` or an `||` list, never as a plain statement under `set -e`.
192
+ #
193
+ # The remaining window is the gap between the probe and the mkdir. A move that
194
+ # finishes inside it leaves no folder this time, and the next event makes the new one.
195
+ # An old folder that appears inside it, from an older install or by hand, leaves the
196
+ # new folder beside it, and the next move merges the two. No data is lost either way:
197
+ # the helper only ever makes an empty folder, and never writes into or removes one.
198
+ df_make_ledger_folder() {
199
+ local _root="$1" _dir="$2" _old="$3" _new="$4"
200
+ [ -n "$_root" ] && [ -n "$_dir" ] && [ -n "$_old" ] && [ -n "$_new" ] || return 1
201
+ df_ledger_folder "$_dir" "$_old" "$_new"
202
+ [ "$DF_LEDGER_FOLDER" = "$_dir/$_new" ] || return 0
203
+ if ! df_no_symlink_below "$_root" "$DF_LEDGER_FOLDER"; then return 2; fi
204
+ if [ -d "$DF_LEDGER_FOLDER" ]; then return 0; fi
205
+ mkdir -p "$DF_LEDGER_FOLDER" 2>/dev/null || return 1
206
+ return 0
207
+ }
208
+
209
+ # df_global_tuning_file <machine_dir> — sets DF_GLOBAL_TUNING_FILE to the global
210
+ # curate-agent tuning file inside the machine directory (~/.devflow). The shell
211
+ # twin of getGlobalCurateTuningConfigPath in src/core/project-paths.ts: the one
212
+ # place a hook names the file. Zero forks; returns 0.
213
+ df_global_tuning_file() {
214
+ DF_GLOBAL_TUNING_FILE="$1/curation.json"
215
+ return 0
216
+ }