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
@@ -2,10 +2,10 @@
2
2
 
3
3
  // src/assets/scripts/hooks/json-helper.cjs
4
4
  // Provides jq-equivalent operations for hooks when jq is not installed, and the
5
- // learning ops the Curate agent runs from the project root.
5
+ // curation ops the Curate agent runs from the project root.
6
6
  // SECURITY: This is a local CLI helper invoked only by shell hooks and the
7
7
  // Curate agent with controlled arguments. No operation takes a file path: a
8
- // file's content arrives on stdin (json-parse redirects it), and the learning ops
8
+ // file's content arrives on stdin (json-parse redirects it), and the curation ops
9
9
  // build every path from the current directory.
10
10
  // Usage: node json-helper.cjs <operation> [args...]
11
11
  //
@@ -37,13 +37,24 @@
37
37
  // show <anchor|obs_id> Read-only: print one entry as pretty JSON
38
38
  // claim-due Hand out the entries due for maintenance, leased
39
39
  // for a day, after the ref their claims are checked at
40
- // claim-queue Claim the learning queue for this run; prints
40
+ // claim-queue Claim the curation queue for this run; prints
41
41
  // claimed <token>[ takeover] | busy | none
42
42
  // release-claim <token> Release the claim the token owns; prints
43
43
  // released | not-owner | gone
44
+ // move-ledger <root> Move a project's old ledger folder to the new name,
45
+ // once; prints none | busy | busy claim |
46
+ // skipped tracked | skipped unknown | moved <mode>
47
+ // and its backup, cause and notes
48
+ // tracked-area <curation|memory|both> <root>
49
+ // Does git track files in the data folders of the area
50
+ // at the root? Prints tracked | untracked | unknown, and
51
+ // for unknown a second line `cause <word>`
44
52
  //
45
- // Every learning op above except claim-queue and release-claim first refreshes
46
- // the mtime of an existing queue claim — the heartbeat (D-OWNED-CLAIM).
53
+ // Every curation op above except claim-queue, release-claim, move-ledger and tracked-area
54
+ // first refreshes the mtime of an existing queue claim — the heartbeat (D-OWNED-CLAIM).
55
+ // Where git tracks files in the curation data folders, or does not say whether it does,
56
+ // every curation op refuses with kind `tracked`, exit 1 and an empty stdout, and changes
57
+ // nothing; the answer is asked once per process.
47
58
 
48
59
  'use strict';
49
60
 
@@ -53,21 +64,50 @@ const { constants: { MAX_STRING_LENGTH } } = require('buffer');
53
64
  const op = process.argv[2];
54
65
  const args = process.argv.slice(3);
55
66
 
56
- /** The learning modules, once loaded; see learning(). */
57
- let learningModules = null;
67
+ /** The curation modules, once loaded; see curation(). */
68
+ let curationModules = null;
58
69
 
59
70
  /**
60
- * The learning store, loaded on first use and memoized. The generic ops never
71
+ * The curation store, loaded on first use and memoized. The generic ops never
61
72
  * call it, so a hook that falls back from jq to node never pays for loading it,
62
73
  * and the store loads the renderer only when an op renders.
63
74
  *
64
75
  * @returns {{ store: object }}
65
76
  */
66
- function learning() {
67
- if (learningModules === null) {
68
- learningModules = { store: require('./lib/learning-store.cjs') };
77
+ function curation() {
78
+ if (curationModules === null) {
79
+ curationModules = { store: require('./lib/curation-store.cjs') };
69
80
  }
70
- return learningModules;
81
+ return curationModules;
82
+ }
83
+
84
+ /** The ledger move module, once loaded; see curationMove(). */
85
+ let curationMoveModule = null;
86
+
87
+ /**
88
+ * The ledger move module, loaded on first use and memoized. Only the move op calls
89
+ * it, so no other curation op loads the move code.
90
+ *
91
+ * @returns {object}
92
+ */
93
+ function curationMove() {
94
+ if (curationMoveModule === null) curationMoveModule = require('./lib/curation-move.cjs');
95
+ return curationMoveModule;
96
+ }
97
+
98
+ /** The tracked-areas check, once loaded; see trackedAreas(). */
99
+ let trackedAreasModule = null;
100
+
101
+ /**
102
+ * The tracked-areas check, loaded on first use and memoized. Only the tracked-area op
103
+ * loads it from here: a leaf that requires node built-ins and the path helpers, never
104
+ * the store.
105
+ *
106
+ * @returns {object}
107
+ */
108
+ function trackedAreas() {
109
+ if (trackedAreasModule === null) trackedAreasModule = require('./lib/tracked-areas.cjs');
110
+ return trackedAreasModule;
71
111
  }
72
112
 
73
113
  /** The bytes each read of stdin asks for. */
@@ -84,7 +124,7 @@ const STDIN_WAIT_CELL = new Int32Array(new SharedArrayBuffer(4));
84
124
 
85
125
  /**
86
126
  * Read stdin to its end, keeping at most `maxBytes`. Every op that reads stdin
87
- * reads it here, so the generic ops and the learning ops cannot read it two ways.
127
+ * reads it here, so the generic ops and the curation ops cannot read it two ways.
88
128
  * Never throws: a failure is a refusal.
89
129
  *
90
130
  * D-STDIN-FD0: stdin is read from file descriptor 0 itself, in reads repeated
@@ -168,14 +208,16 @@ function parseArgs(argList) {
168
208
  }
169
209
 
170
210
  // ---------------------------------------------------------------------------
171
- // Learning-op adapter
211
+ // Curation-op adapter
172
212
  // ---------------------------------------------------------------------------
173
213
 
174
214
  /**
175
- * Print a learning op's Result: `format(value)` and a newline on stdout, or the
215
+ * Print a curation op's Result: `format(value)` and a newline on stdout, or the
176
216
  * error message and a newline on stderr. Returns the exit code for the op to set
177
217
  * as process.exitCode, so the process exits once, after the op has returned and
178
- * every lock it took is released.
218
+ * every lock it took is released. The error message keeps its line structure (a
219
+ * refusal lists one problem per line), and each line has no control character, as it
220
+ * may name a file the repository holds. At most REFUSAL_MAX_LINES lines print.
179
221
  *
180
222
  * @param {{ ok: true, value: unknown } | { ok: false, error: { message: string } }} result
181
223
  * @param {(value: any) => string} format - the stdout text for the value
@@ -186,10 +228,43 @@ function emit(result, format) {
186
228
  process.stdout.write(`${format(result.value)}\n`);
187
229
  return 0;
188
230
  }
189
- process.stderr.write(`${result.error.message}\n`);
231
+ process.stderr.write(`${refusalText(result.error.message)}\n`);
190
232
  return 1;
191
233
  }
192
234
 
235
+ /** The most lines of one refusal message that print. */
236
+ const REFUSAL_MAX_LINES = 200;
237
+
238
+ /**
239
+ * A refusal message with each line scrubbed (singleLine) and the line structure kept.
240
+ * Past REFUSAL_MAX_LINES lines, the rest become one `... N more lines` line.
241
+ *
242
+ * @param {string} message
243
+ * @returns {string}
244
+ */
245
+ function refusalText(message) {
246
+ const { singleLine } = curation().store;
247
+ const lines = String(message).split('\n');
248
+ if (lines.length <= REFUSAL_MAX_LINES) return lines.map(line => singleLine(line)).join('\n');
249
+ const shown = lines.slice(0, REFUSAL_MAX_LINES).map(line => singleLine(line));
250
+ return [...shown, `... ${lines.length - REFUSAL_MAX_LINES} more lines`].join('\n');
251
+ }
252
+
253
+ /**
254
+ * The first line of move-ledger's answer: the state, with `moved` followed by its mode,
255
+ * `busy` followed by `claim` when a fresh queue claim holds the move, and `skipped`
256
+ * followed by its reason (`tracked` or `unknown`).
257
+ *
258
+ * @param {{ state: string, mode?: string, reason?: string }} moved
259
+ * @returns {string}
260
+ */
261
+ function moveAnswerWord(moved) {
262
+ if (moved.state === 'moved') return `moved ${moved.mode}`;
263
+ if (moved.state === 'busy' && moved.reason === 'claim') return 'busy claim';
264
+ if (moved.state === 'skipped') return `skipped ${moved.reason}`;
265
+ return moved.state;
266
+ }
267
+
193
268
  /**
194
269
  * Refuse a malformed command line: print `<op>: usage: <usage>` on stderr and exit 1,
195
270
  * before the op takes any lock.
@@ -202,11 +277,11 @@ function exitWithUsage(usage) {
202
277
  process.exit(1);
203
278
  }
204
279
 
205
- /** The most stdin a learning op reads: far above any valid input, so a runaway writer is refused, not parsed. */
280
+ /** The most stdin a curation op reads: far above any valid input, so a runaway writer is refused, not parsed. */
206
281
  const STDIN_JSON_MAX_BYTES = 64 * 1024;
207
282
 
208
283
  /**
209
- * A learning op's stdin as one JSON object: the one way text reaches a learning
284
+ * A curation op's stdin as one JSON object: the one way text reaches a curation
210
285
  * op, so no field of it ever passes through argv or a shell word. Never throws.
211
286
  *
212
287
  * @param {string} opName - for the message
@@ -235,30 +310,30 @@ const PUT_MODES = new Map([['--create', 'create'], ['--update', 'update'], ['--r
235
310
  const ENTRY_TYPES = new Set(['decision', 'pitfall']);
236
311
 
237
312
  /**
238
- * The learning ops whose run sends the claim heartbeat first (D-OWNED-CLAIM).
313
+ * The curation ops whose run sends the claim heartbeat first (D-OWNED-CLAIM).
239
314
  * claim-queue and release-claim manage the claim themselves, and the generic ops
240
- * never touch it. A new learning op joins this set.
315
+ * never touch it. A new curation op joins this set.
241
316
  */
242
- const LEARNING_OPS = new Set([
317
+ const CURATION_OPS = new Set([
243
318
  'assign-anchor', 'retire-anchor', 'restore-anchor', 'refresh-anchor', 'rotate-observations',
244
319
  'put-observation', 'list', 'show', 'claim-due',
245
320
  ]);
246
321
 
247
322
  /** Send the claim heartbeat; a failure is reported on stderr and never stops the op. */
248
323
  function heartbeat(root) {
249
- const beat = learning().store.touchClaim(root);
324
+ const beat = curation().store.touchClaim(root);
250
325
  if (!beat.ok) process.stderr.write(`${beat.error.message}\n`);
251
326
  }
252
327
 
253
- // The learning ops run from the project root and take no path to a learning file:
328
+ // The curation ops run from the project root and take no path to a curation file:
254
329
  // each builds its paths from the current directory. Every op that writes takes the
255
- // store's one learning lock through withDecisionsLock (D-ONE-LEARNING-LOCK) and
256
- // refuses, creating nothing, when .devflow/learning/ is absent (D-NO-STRAY-TREE)
330
+ // store's one curation lock through withDecisionsLock (D-ONE-LEARNING-LOCK) and
331
+ // refuses, creating nothing, when .devflow/curation/ is absent (D-NO-STRAY-TREE)
257
332
  // or when it, or .devflow, is a symbolic link (D-NO-LINKED-TREE).
258
333
  // A locked body returns its Result; emit prints it once the lock is released.
259
334
  if (require.main === module) {
260
335
  try {
261
- if (LEARNING_OPS.has(op)) heartbeat(process.cwd());
336
+ if (CURATION_OPS.has(op)) heartbeat(process.cwd());
262
337
  switch (op) {
263
338
  case 'get-field': {
264
339
  const input = JSON.parse(readStdin());
@@ -338,7 +413,6 @@ try {
338
413
  console.log(JSON.stringify({
339
414
  timestamp: data.ts || '',
340
415
  trigger: 'pre-compact',
341
- memory_snapshot: data.memory || '',
342
416
  git: {
343
417
  branch: data.branch || '',
344
418
  status: data.status || '',
@@ -353,12 +427,12 @@ try {
353
427
  // assign-anchor <decision|pitfall> <obs_id>
354
428
  // Promote a v2 observation no ledger row carries to the next entry of its
355
429
  // type, skipping each number a tracked file cites (assignAnchor,
356
- // learning-store.cjs: D-LEDGER-REGISTRY, D-E4-SKIP). It never writes the log.
430
+ // curation-store.cjs: D-LEDGER-REGISTRY, D-E4-SKIP). It never writes the log.
357
431
  // stdout: the anchor; stderr: `assign-anchor: skipped <anchor>, cited in
358
432
  // <path>:<line>` for each number skipped
359
433
  // -------------------------------------------------------------------------
360
434
  case 'assign-anchor': {
361
- const { store } = learning();
435
+ const { store } = curation();
362
436
  if (args.length !== 2 || !ENTRY_TYPES.has(args[0]) || !store.OBS_ID_RE.test(args[1])) {
363
437
  exitWithUsage('assign-anchor <decision|pitfall> <obs_id> (run from the project root)');
364
438
  }
@@ -377,12 +451,12 @@ try {
377
451
  // Make an active entry inactive with the one JSON object on stdin its status
378
452
  // takes: { reason } for Retired and Deprecated, { by } for Superseded, and
379
453
  // { at, quote } for Encoded, the quote checked at the verify ref
380
- // (retireAnchor, learning-store.cjs: D-ENCODED-QUOTE).
454
+ // (retireAnchor, curation-store.cjs: D-ENCODED-QUOTE).
381
455
  // stdout: the new status in lower case and the anchor, then `repointed
382
456
  // <anchor>` for each entry re-pointed to the successor
383
457
  // -------------------------------------------------------------------------
384
458
  case 'retire-anchor': {
385
- const { store } = learning();
459
+ const { store } = curation();
386
460
  if (args.length !== 2 || !store.ANCHOR_ID_RE.test(args[0]) || !store.INACTIVE_STATUSES.includes(args[1])) {
387
461
  exitWithUsage('retire-anchor <anchor> <Encoded|Superseded|Retired|Deprecated> (one JSON object on stdin; run from the project root)');
388
462
  }
@@ -400,11 +474,11 @@ try {
400
474
  // Make an inactive entry active again, its notes, last_verified and
401
475
  // last_attempt cleared so it is due for maintenance again, ordered after
402
476
  // integrity problems and legacy entries, or among them when it is one
403
- // (restoreAnchor, learning-store.cjs: D-DUE-ORDER).
477
+ // (restoreAnchor, curation-store.cjs: D-DUE-ORDER).
404
478
  // stdout: restored <anchor>
405
479
  // -------------------------------------------------------------------------
406
480
  case 'restore-anchor': {
407
- const { store } = learning();
481
+ const { store } = curation();
408
482
  if (args.length !== 1 || !store.ANCHOR_ID_RE.test(args[0])) {
409
483
  exitWithUsage('restore-anchor <anchor> (run from the project root)');
410
484
  }
@@ -415,12 +489,12 @@ try {
415
489
  // -------------------------------------------------------------------------
416
490
  // refresh-anchor <anchor> [<anchor>...] [--verified]
417
491
  // Re-project active v2 entries from their log rows, or under --verified stamp
418
- // them verified today; all or nothing (refreshAnchors, learning-store.cjs).
492
+ // them verified today; all or nothing (refreshAnchors, curation-store.cjs).
419
493
  // stdout: `reprojected <anchor>` or `unchanged <anchor>` per anchor, or
420
494
  // `verified <anchor>` under --verified, in the order given
421
495
  // -------------------------------------------------------------------------
422
496
  case 'refresh-anchor': {
423
- const { store } = learning();
497
+ const { store } = curation();
424
498
  const verifiedFlags = args.filter(arg => arg === '--verified').length;
425
499
  const anchors = args.filter(arg => arg !== '--verified');
426
500
  if (verifiedFlags > 1 || anchors.length === 0 || !anchors.every(arg => store.ANCHOR_ID_RE.test(arg))) {
@@ -435,13 +509,13 @@ try {
435
509
  // rotate-observations
436
510
  // Archive the log rows no ledger row carries once 30 days have passed since
437
511
  // their last activity, and delete the usage telemetry's leftovers
438
- // (D-ROTATE-UNREFERENCED, learning-store.cjs). Takes no argument: the log and
512
+ // (D-ROTATE-UNREFERENCED, curation-store.cjs). Takes no argument: the log and
439
513
  // the archive are the project root's.
440
514
  // stdout: rotated <N> observations
441
515
  // -------------------------------------------------------------------------
442
516
  case 'rotate-observations': {
443
517
  if (args.length > 0) exitWithUsage('rotate-observations (no arguments; run from the project root)');
444
- const result = learning().store.rotateObservations(process.cwd());
518
+ const result = curation().store.rotateObservations(process.cwd());
445
519
  process.exitCode = emit(result, ({ rotated }) => `rotated ${rotated} observations`);
446
520
  break;
447
521
  }
@@ -449,7 +523,7 @@ try {
449
523
  // -------------------------------------------------------------------------
450
524
  // put-observation --create|--update|--reinforce
451
525
  // Store one observation from the JSON object on stdin (D-PUT-NOT-MERGE,
452
- // D-PUT-REPROJECTS, learning-store.cjs). Exactly one mode flag; no other argv.
526
+ // D-PUT-REPROJECTS, curation-store.cjs). Exactly one mode flag; no other argv.
453
527
  // stdout: created <id> | updated <id> | unchanged <id> | reinforced <id> <n>,
454
528
  // then one `reprojected <anchor>` line per entry re-projected
455
529
  // -------------------------------------------------------------------------
@@ -459,7 +533,7 @@ try {
459
533
  exitWithUsage('put-observation --create|--update|--reinforce (one JSON object on stdin; run from the project root)');
460
534
  }
461
535
  const input = readStdinJson('put-observation');
462
- const result = input.ok ? learning().store.putObservation(process.cwd(), mode, input.value) : input;
536
+ const result = input.ok ? curation().store.putObservation(process.cwd(), mode, input.value) : input;
463
537
  process.exitCode = emit(result, put => [
464
538
  put.outcome === 'reinforced' ? `reinforced ${put.id} ${put.observations}` : `${put.outcome} ${put.id}`,
465
539
  ...put.reprojected.map(anchorId => `reprojected ${anchorId}`),
@@ -470,25 +544,25 @@ try {
470
544
  // -------------------------------------------------------------------------
471
545
  // list
472
546
  // Print the ledger and the log by section, read-only (readListing and
473
- // formatListing, learning-store.cjs). Takes no argument.
547
+ // formatListing, curation-store.cjs). Takes no argument.
474
548
  // stdout: the ACTIVE, INACTIVE, OBSERVATIONS and INTEGRITY sections, then
475
549
  // MALFORMED when lines were skipped
476
550
  // -------------------------------------------------------------------------
477
551
  case 'list': {
478
552
  if (args.length > 0) exitWithUsage('list (no arguments; run from the project root)');
479
- const { store } = learning();
553
+ const { store } = curation();
480
554
  process.exitCode = emit(store.readListing(process.cwd()), store.formatListing);
481
555
  break;
482
556
  }
483
557
 
484
558
  // -------------------------------------------------------------------------
485
559
  // show <anchor|obs_id>
486
- // Print one entry, read-only (showByKey, learning-store.cjs).
560
+ // Print one entry, read-only (showByKey, curation-store.cjs).
487
561
  // stdout: pretty JSON { key, ledger, log, history_versions, flags }, plus
488
562
  // malformed when lines were skipped
489
563
  // -------------------------------------------------------------------------
490
564
  case 'show': {
491
- const { store } = learning();
565
+ const { store } = curation();
492
566
  if (args.length !== 1 || !(store.ANCHOR_ID_RE.test(args[0]) || store.OBS_ID_RE.test(args[0]))) {
493
567
  exitWithUsage('show <anchor|obs_id> (run from the project root)');
494
568
  }
@@ -499,13 +573,13 @@ try {
499
573
  // -------------------------------------------------------------------------
500
574
  // claim-due
501
575
  // Hand out the entries due for maintenance and lease each for a day
502
- // (claimDue, learning-store.cjs: D-DUE-ORDER, D-VERIFY-REF). Takes no argument.
576
+ // (claimDue, curation-store.cjs: D-DUE-ORDER, D-VERIFY-REF). Takes no argument.
503
577
  // stdout: ref <origin/HEAD|HEAD> <sha12>, or ref none; then one
504
578
  // `<anchor> <reason> <bytes>` line per entry handed out, or due none
505
579
  // -------------------------------------------------------------------------
506
580
  case 'claim-due': {
507
581
  if (args.length > 0) exitWithUsage('claim-due (no arguments; run from the project root)');
508
- const { store } = learning();
582
+ const { store } = curation();
509
583
  process.exitCode = emit(store.claimDue(process.cwd()), ({ ref, due }) => [
510
584
  ref === null ? 'ref none' : `ref ${ref.ref} ${ref.commit.slice(0, 12)}`,
511
585
  ...(due.length > 0 ? due.map(entry => `${store.singleLine(entry.anchor_id)} ${entry.reason} ${entry.bytes}`) : ['due none']),
@@ -515,13 +589,13 @@ try {
515
589
 
516
590
  // -------------------------------------------------------------------------
517
591
  // claim-queue
518
- // Claim the learning queue for this run (D-OWNED-CLAIM, learning-store.cjs).
592
+ // Claim the curation queue for this run (D-OWNED-CLAIM, curation-store.cjs).
519
593
  // Takes no argument; mints the token itself.
520
594
  // stdout: claimed <token> | claimed <token> takeover | busy | none
521
595
  // -------------------------------------------------------------------------
522
596
  case 'claim-queue': {
523
597
  if (args.length > 0) exitWithUsage('claim-queue (no arguments; run from the project root)');
524
- const result = learning().store.claimQueue(process.cwd());
598
+ const result = curation().store.claimQueue(process.cwd());
525
599
  process.exitCode = emit(result, claim => (claim.state === 'claimed'
526
600
  ? `claimed ${claim.token}${claim.takeover ? ' takeover' : ''}`
527
601
  : claim.state));
@@ -530,11 +604,11 @@ try {
530
604
 
531
605
  // -------------------------------------------------------------------------
532
606
  // release-claim <token>
533
- // Release the claim the token owns (D-OWNED-CLAIM, learning-store.cjs).
607
+ // Release the claim the token owns (D-OWNED-CLAIM, curation-store.cjs).
534
608
  // stdout: released | not-owner | gone
535
609
  // -------------------------------------------------------------------------
536
610
  case 'release-claim': {
537
- const { store } = learning();
611
+ const { store } = curation();
538
612
  if (args.length !== 1 || !store.CLAIM_TOKEN_RE.test(args[0])) {
539
613
  exitWithUsage('release-claim <token> (the 16 hex characters claim-queue printed)');
540
614
  }
@@ -542,12 +616,63 @@ try {
542
616
  break;
543
617
  }
544
618
 
619
+ // -------------------------------------------------------------------------
620
+ // move-ledger <root>
621
+ // Move the project's old ledger folder to the new name, once (moveLedger,
622
+ // curation-move.cjs). Not a curation op: it sends no heartbeat, because a
623
+ // heartbeat would refresh a claim the move has to judge stale. Unlike the
624
+ // curation ops it takes the root as an argument, so a caller outside the project
625
+ // root can name it; the root stays one argv word, never a shell word. The two
626
+ // folder names are the path helpers' own: no argument names them.
627
+ // stdout: none | busy | busy claim | skipped tracked | skipped unknown | moved <mode>,
628
+ // then `backup <name>` when a backup was made, `cause <word>` for `skipped unknown`
629
+ // (one fixed word that says why git did not answer), and one `note <text>` line for
630
+ // each note. `busy claim` means a fresh queue claim holds the move, which can last 15
631
+ // minutes; plain `busy` means a lock does. `skipped tracked` means git tracks files in
632
+ // a curation data folder, `skipped unknown` that git did not answer: nothing changed.
633
+ // Every line is scrubbed here (singleLine):
634
+ // a note may name a file the repository holds, and no line may carry a control
635
+ // character or break into two.
636
+ // -------------------------------------------------------------------------
637
+ case 'move-ledger': {
638
+ if (args.length !== 1) exitWithUsage('move-ledger <root> (the root is an absolute path)');
639
+ const { store } = curation();
640
+ process.exitCode = emit(curationMove().moveLedger(args[0]), moved => [
641
+ moveAnswerWord(moved),
642
+ ...(moved.state === 'moved' && moved.backup !== null ? [`backup ${moved.backup}`] : []),
643
+ ...(moved.state === 'skipped' && moved.cause !== undefined ? [`cause ${moved.cause}`] : []),
644
+ ...moved.notes.map(note => `note ${note}`),
645
+ ].map(line => store.singleLine(line)).join('\n'));
646
+ break;
647
+ }
648
+
649
+ // -------------------------------------------------------------------------
650
+ // tracked-area <curation|memory|both> <root>
651
+ // Does git track files in the data folders of the area at the root
652
+ // (inspectTrackedAreaOnce, tracked-areas.cjs)? Not a curation op: it sends no
653
+ // heartbeat and loads only the check. The root is an argument, so the shell gate
654
+ // names the root it decided on; it stays one argv word, never a shell word. The
655
+ // answer is asked once per root and area for the life of the process.
656
+ // stdout: tracked | untracked | unknown, and for unknown a second line
657
+ // `cause <word>` (one fixed word that says why git did not answer)
658
+ // -------------------------------------------------------------------------
659
+ case 'tracked-area': {
660
+ const { AREA_PATHSPECS, inspectTrackedAreaOnce } = trackedAreas();
661
+ if (args.length !== 2 || !Object.prototype.hasOwnProperty.call(AREA_PATHSPECS, args[0])) {
662
+ exitWithUsage('tracked-area <curation|memory|both> <root> (the root is an absolute path)');
663
+ }
664
+ const { answer, cause } = inspectTrackedAreaOnce(args[1], args[0]);
665
+ process.stdout.write(answer === 'unknown' ? `unknown\ncause ${cause}\n` : `${answer}\n`);
666
+ break;
667
+ }
668
+
545
669
  default:
546
670
  process.stderr.write(`json-helper: unknown operation "${op}"\n`);
547
671
  process.exit(1);
548
672
  }
549
673
  } catch (err) {
550
- process.stderr.write(`json-helper error: ${err && err.message ? err.message : String(err)}\n`);
674
+ const reason = err && err.message ? err.message : String(err);
675
+ process.stderr.write(`json-helper error: ${curationModules === null ? reason : curationModules.store.singleLine(reason)}\n`);
551
676
  process.exit(1);
552
677
  }
553
678
  } // end if (require.main === module)
@@ -170,7 +170,7 @@ json_prompt_output() {
170
170
  # Build pre-compact backup JSON. Usage: json_backup_construct --arg ts X --arg branch Y ...
171
171
  json_backup_construct() {
172
172
  if [ "$_HAS_JQ" = "true" ]; then
173
- jq -n "$@" '{timestamp: $ts, trigger: "pre-compact", memory_snapshot: $memory, git: {branch: $branch, status: $status, log: $log, diff_stat: $diff}}'
173
+ jq -n "$@" '{timestamp: $ts, trigger: "pre-compact", git: {branch: $branch, status: $status, log: $log, diff_stat: $diff}}'
174
174
  else
175
175
  node "$_JSON_HELPER" backup-construct "$@"
176
176
  fi