devflow-kit 3.8.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.
- package/CHANGELOG.md +120 -0
- package/README.md +5 -5
- package/dist/agents/code.md +4 -4
- package/dist/agents/fix.md +1 -1
- package/dist/agents/git.md +2 -2
- package/dist/agents/skim.md +1 -1
- package/dist/agents/triage.md +7 -0
- package/dist/cli/commands/curation.js +67 -16
- package/dist/cli/commands/gate-level-prompts.js +8 -6
- package/dist/cli/commands/gates.js +5 -4
- package/dist/cli/commands/init.js +8 -7
- package/dist/cli/commands/memory.js +59 -12
- package/dist/cli/commands/uninstall.js +77 -9
- package/dist/commands/bug-analysis.md +2 -2
- package/dist/commands/code-review.md +11 -11
- package/dist/commands/debug.md +2 -2
- package/dist/commands/dynamic-build.md +2 -2
- package/dist/commands/dynamic-plan.md +2 -2
- package/dist/commands/dynamic-profile.md +2 -2
- package/dist/commands/dynamic-tickets.md +2 -2
- package/dist/commands/explore.md +2 -2
- package/dist/commands/implement.md +2 -2
- package/dist/commands/plan.md +2 -2
- package/dist/commands/release.md +2 -2
- package/dist/commands/research.md +2 -2
- package/dist/commands/resolve.md +16 -16
- package/dist/commands/self-review.md +2 -2
- package/dist/core/area-pause-notice.js +44 -0
- package/dist/core/curation-move-skip.js +4 -5
- package/dist/core/curation-queue-cleanup.js +6 -5
- package/dist/core/curation-store.js +1 -0
- package/dist/core/evidence-policy.js +10 -8
- package/dist/core/plugins.js +4 -4
- package/dist/core/tracked-areas.js +138 -14
- package/dist/curation-off/agents/code.md +4 -4
- package/dist/curation-off/agents/fix.md +1 -1
- package/dist/curation-off/agents/triage.md +7 -0
- package/dist/curation-off/commands/code-review.md +11 -11
- package/dist/curation-off/commands/debug.md +2 -2
- package/dist/curation-off/commands/dynamic-build.md +2 -2
- package/dist/curation-off/commands/explore.md +2 -2
- package/dist/curation-off/commands/implement.md +2 -2
- package/dist/curation-off/commands/plan.md +2 -2
- package/dist/curation-off/commands/resolve.md +16 -16
- package/dist/curation-off/commands/self-review.md +2 -2
- package/dist/hud/components/curation-counts.js +27 -0
- package/dist/skills/git/references/pr/post-resolution-summary.md +12 -11
- package/dist/skills/git/references/pr/post-review-summary.md +12 -11
- package/dist/skills/git/references/pr/update-pr-evidence.md +2 -2
- package/dist/skills/git/references/publication-gate.md +6 -7
- package/dist/skills/git/references/tracker/github/manage-debt.md +6 -2
- package/dist/skills/git/references/tracker/jira/manage-debt.md +6 -2
- package/dist/skills/git/references/tracker/linear/manage-debt.md +6 -2
- package/package.json +1 -1
- package/src/assets/agents/code.mds +4 -4
- package/src/assets/agents/evaluate.md +6 -12
- package/src/assets/agents/fix.mds +1 -1
- package/src/assets/agents/git.mds +2 -2
- package/src/assets/agents/skim.mds +1 -1
- package/src/assets/agents/triage.mds +7 -0
- package/src/assets/agents/validate.md +20 -27
- package/src/assets/commands/_partials/_decisions.mds +10 -8
- package/src/assets/commands/_partials/_settings.mds +2 -2
- package/src/assets/commands/code-review.mds +9 -9
- package/src/assets/commands/resolve.mds +14 -14
- package/src/assets/mds/git/_ops.mds +6 -1
- package/src/assets/mds/git/_ops_pr.mds +20 -10
- package/src/assets/mds/git/_ops_steps.mds +10 -0
- package/src/assets/mds/git/_pr.mds +14 -14
- package/src/assets/mds/git/_references.mds +6 -7
- package/src/assets/mds/tracker/_github.mds +1 -4
- package/src/assets/mds/tracker/_jira.mds +1 -4
- package/src/assets/mds/tracker/_linear.mds +1 -4
- package/src/assets/scripts/ci-wait.cjs +389 -92
- package/src/assets/scripts/hooks/background-memory-update +79 -1
- package/src/assets/scripts/hooks/json-helper.cjs +44 -2
- package/src/assets/scripts/hooks/lib/curation-store.cjs +82 -20
- package/src/assets/scripts/hooks/lib/render-decisions.cjs +9 -1
- package/src/assets/scripts/hooks/lib/tracked-areas.cjs +226 -33
- package/src/assets/scripts/hooks/memory-permission +10 -2
- package/src/assets/scripts/hooks/memory-worker +11 -0
- package/src/assets/scripts/hooks/pre-compact-memory +11 -0
- package/src/assets/scripts/hooks/queue-append +218 -1
- package/src/assets/scripts/hooks/session-start-context +89 -37
- package/src/assets/scripts/hooks/session-start-memory +39 -0
- package/src/assets/scripts/pr-evidence.cjs +5 -2
- package/src/assets/scripts/resolve-settings.cjs +244 -30
- package/src/assets/scripts/verify-evidence.cjs +307 -75
- package/src/assets/skills/agent-references/SKILL.md +7 -1
- package/src/assets/skills/agent-references/references/commands/implement-levels.md +6 -4
- package/src/assets/skills/agent-references/references/commands/resolve-levels.md +3 -2
- package/src/assets/skills/agent-references/references/synthesize/review.md +4 -4
- package/src/assets/skills/agent-references/references/triage/severity-tiers.md +43 -0
- package/src/assets/skills/agent-references/references/validate/passed-alone.md +14 -0
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
# finds the old folder gone (the one-time move ran meanwhile) looks again and goes
|
|
30
30
|
# on in the new folder, so a capture never drops a row and never waits. Any other
|
|
31
31
|
# queue's folder is created if absent.
|
|
32
|
+
# A queue in an area that git tracks, or that git did not answer for, is paused: before
|
|
33
|
+
# the row is built, queue_tracked_area asks for the area the queue lies in, and a queue
|
|
34
|
+
# that is not untracked gets no row, no folder and no file. The skip is logged once. The
|
|
35
|
+
# check runs on every call, whether or not the queue's folder exists.
|
|
32
36
|
# After appending, truncates from 200 to the newest 100 lines under a lock
|
|
33
37
|
# (curation_lock_acquire on "<queue_file>.lock", 2s timeout), keeping mode 0600.
|
|
34
38
|
# The append itself is intentionally lock-free (accepted-class race shared with
|
|
@@ -40,7 +44,29 @@
|
|
|
40
44
|
# *_enabled flag is "true", with the root that queue lies below. Each queue is gated
|
|
41
45
|
# independently -- callers compute memory_enabled/curation_enabled themselves (see
|
|
42
46
|
# queue_read_gates below for the at-most-one-fork read of both). With neither queue
|
|
43
|
-
# enabled it builds nothing.
|
|
47
|
+
# enabled it builds nothing. A queue whose area is paused (see queue_tracked_area) reads
|
|
48
|
+
# as not enabled for the call, and nothing is built for it.
|
|
49
|
+
#
|
|
50
|
+
# queue_tracked_area <root> <curation|memory|both> [git|node]
|
|
51
|
+
# Does git track files in the data folders of the area at <root>? Sets
|
|
52
|
+
# DF_TRACKED_AREA_CURATION or DF_TRACKED_AREA_MEMORY (one variable per area) to
|
|
53
|
+
# `tracked`, `untracked` or `unknown`, and always returns 0. The area `both` is one
|
|
54
|
+
# call over the union of the two: it sets both variables, and a listing in it is
|
|
55
|
+
# followed by one call per area, so each variable names its own area. A root is asked
|
|
56
|
+
# once per area in a process. The shell twin of lib/tracked-areas.cjs, held to its
|
|
57
|
+
# answers by tests/decisions/tracked-area-parity.test.ts:
|
|
58
|
+
# - no `.git` entry (a file, a folder or a link) at the root: `untracked`, no call;
|
|
59
|
+
# - a root that is not an absolute path, whose physical parent holds a colon, or that
|
|
60
|
+
# can not be entered: `unknown`;
|
|
61
|
+
# - git mode (the default): one `git ls-files -z` over the area's pathspecs, with the
|
|
62
|
+
# leaf's environment scrub, a ceiling at the root's parent and the fsmonitor command
|
|
63
|
+
# off. Only the first byte of the listing is read. A byte read means `tracked`,
|
|
64
|
+
# whatever git's status (git ends on a closed pipe). With no byte, git status 0
|
|
65
|
+
# means `untracked`, and any other status `unknown`;
|
|
66
|
+
# - node mode: the json-helper `tracked-area` op, which bounds the call in time. Without
|
|
67
|
+
# node it runs as git mode; an answer that is not one of the three words is `unknown`.
|
|
68
|
+
# The capture hooks ask in git mode, with no process but git. The session start asks in
|
|
69
|
+
# node mode, so a hung git is cut.
|
|
44
70
|
#
|
|
45
71
|
# queue_read_gates <manifest_path> [<root>]
|
|
46
72
|
# Reads BOTH switches, `memory` and `curation` (curation is read under both of
|
|
@@ -245,7 +271,194 @@ _qar_trim_overflow() {
|
|
|
245
271
|
return 0
|
|
246
272
|
}
|
|
247
273
|
|
|
274
|
+
# --- The tracked-area gate: no capture into a folder that git tracks ---
|
|
275
|
+
#
|
|
276
|
+
# A folder that git tracks holds the repository's content, not devflow's data, so a
|
|
277
|
+
# capture writes nothing into it, and also when git did not answer (queue_tracked_area,
|
|
278
|
+
# documented above). The check runs before the row is built and before any folder is
|
|
279
|
+
# made, on every call, whether or not the queue's folder exists.
|
|
280
|
+
|
|
281
|
+
# _qta_specs <curation|memory|both> -- sets the array _QTA_SPECS to the pathspecs of the
|
|
282
|
+
# area: the shell twin of AREA_PATHSPECS in lib/tracked-areas.cjs, in the leaf's order and
|
|
283
|
+
# spelling (tests/decisions/tracked-area-parity.test.ts holds the two equal, element by
|
|
284
|
+
# element). `.devflow` is spelled twice, the second time with the fl ligature (U+FB02,
|
|
285
|
+
# bytes ef ac 82), which a case-folding file system opens as the same name. The `.devflow`
|
|
286
|
+
# entry itself (a link, a gitlink or a plain file) stands over every folder: the character
|
|
287
|
+
# class keeps `glob` from matching it as a leading directory. Fails for any other word.
|
|
288
|
+
_qta_specs() {
|
|
289
|
+
local _qs_fl=$'\xef\xac\x82' _qs_dir="" _qs_curation=() _qs_memory=() _qs_entry=()
|
|
290
|
+
for _qs_dir in ".devflow" ".dev${_qs_fl}ow"; do
|
|
291
|
+
_qs_curation+=(
|
|
292
|
+
":(icase,literal)${_qs_dir}/learning"
|
|
293
|
+
":(icase,literal)${_qs_dir}/curation"
|
|
294
|
+
":(icase,glob)${_qs_dir}/learning.backup-*"
|
|
295
|
+
":(icase,glob)${_qs_dir}/learning.backup-*/**"
|
|
296
|
+
)
|
|
297
|
+
_qs_memory+=(":(icase,literal)${_qs_dir}/memory")
|
|
298
|
+
_qs_entry+=(":(icase,glob)[.]${_qs_dir#.}")
|
|
299
|
+
done
|
|
300
|
+
case "${1:-}" in
|
|
301
|
+
curation) _QTA_SPECS=("${_qs_curation[@]}" "${_qs_entry[@]}") ;;
|
|
302
|
+
memory) _QTA_SPECS=("${_qs_memory[@]}" "${_qs_entry[@]}") ;;
|
|
303
|
+
both) _QTA_SPECS=("${_qs_curation[@]}" "${_qs_entry[@]}" "${_qs_memory[@]}") ;;
|
|
304
|
+
*) _QTA_SPECS=(); return 1 ;;
|
|
305
|
+
esac
|
|
306
|
+
return 0
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
# _qta_probe <root> <curation|memory|both> <git|node> -- sets _QTA_ANSWER to `tracked`,
|
|
310
|
+
# `untracked` or `unknown`, as queue_tracked_area documents. Always returns 0.
|
|
311
|
+
#
|
|
312
|
+
# The git mode reads one byte of the listing and ends. Under bash 3.2 with errexit set the
|
|
313
|
+
# pipeline sits in an `if` condition (an empty listing makes `read` fail, and a failing
|
|
314
|
+
# condition does not end the shell), and PIPESTATUS is copied in both arms before any other
|
|
315
|
+
# command resets it: the second element is the reader's status, the first git's. Nothing
|
|
316
|
+
# follows the pipeline in an `||` list. The `cd -P` runs inside the subshell, so the
|
|
317
|
+
# caller's directory never changes, and git runs there with the variables that redirect it
|
|
318
|
+
# removed (the list is GIT_REDIRECT_VARIABLES of the leaf), a fixed locale, no optional
|
|
319
|
+
# lock, and the root's physical parent as its ceiling.
|
|
320
|
+
_qta_probe() {
|
|
321
|
+
local _qp_root="${1:-}" _qp_area="${2:-}" _qp_mode="${3:-git}" _qp_out="" _qp_ceiling="" _qp_c="" _qp_st=()
|
|
322
|
+
_QTA_ANSWER="unknown"
|
|
323
|
+
case "$_qp_root" in /*) ;; *) return 0 ;; esac
|
|
324
|
+
if [ ! -e "$_qp_root/.git" ] && [ ! -L "$_qp_root/.git" ]; then
|
|
325
|
+
_QTA_ANSWER="untracked"
|
|
326
|
+
return 0
|
|
327
|
+
fi
|
|
328
|
+
if [ "$_qp_mode" = "node" ] && [ "${_HAS_NODE:-}" = "true" ] && [ -n "${_JSON_HELPER:-}" ]; then
|
|
329
|
+
case "$_qp_area" in curation|memory|both) ;; *) return 0 ;; esac
|
|
330
|
+
_qp_out=$(node "$_JSON_HELPER" tracked-area "$_qp_area" "$_qp_root" 2>/dev/null) || _qp_out=""
|
|
331
|
+
_qp_out="${_qp_out%%$'\n'*}"
|
|
332
|
+
case "$_qp_out" in tracked|untracked|unknown) _QTA_ANSWER="$_qp_out" ;; esac
|
|
333
|
+
return 0
|
|
334
|
+
fi
|
|
335
|
+
_qta_specs "$_qp_area" || return 0
|
|
336
|
+
if (
|
|
337
|
+
cd -P "$_qp_root" >/dev/null 2>&1 || exit 1
|
|
338
|
+
_qp_ceiling="${PWD%/*}"
|
|
339
|
+
[ -n "$_qp_ceiling" ] || _qp_ceiling="/"
|
|
340
|
+
case "$_qp_ceiling" in *:*) exit 1 ;; esac
|
|
341
|
+
unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_COMMON_DIR GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_NAMESPACE GIT_LITERAL_PATHSPECS GIT_GLOB_PATHSPECS GIT_NOGLOB_PATHSPECS GIT_ICASE_PATHSPECS
|
|
342
|
+
export LC_ALL=C GIT_OPTIONAL_LOCKS=0 GIT_CEILING_DIRECTORIES="$_qp_ceiling"
|
|
343
|
+
exec git --no-literal-pathspecs -c core.fsmonitor=false ls-files -z -- "${_QTA_SPECS[@]}"
|
|
344
|
+
) </dev/null 2>/dev/null | { IFS= read -r -n 1 _qp_c; }; then
|
|
345
|
+
_qp_st=("${PIPESTATUS[@]}")
|
|
346
|
+
else
|
|
347
|
+
_qp_st=("${PIPESTATUS[@]}")
|
|
348
|
+
fi
|
|
349
|
+
if [ "${#_qp_st[@]}" -eq 2 ]; then
|
|
350
|
+
if [ "${_qp_st[1]}" -eq 0 ]; then
|
|
351
|
+
_QTA_ANSWER="tracked"
|
|
352
|
+
elif [ "${_qp_st[0]}" -eq 0 ]; then
|
|
353
|
+
_QTA_ANSWER="untracked"
|
|
354
|
+
fi
|
|
355
|
+
fi
|
|
356
|
+
return 0
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
# _qta_ask <root> <curation|memory> <git|node> -- sets the area's variable, unless this
|
|
360
|
+
# process already asked for that root and area. Always returns 0.
|
|
361
|
+
_qta_ask() {
|
|
362
|
+
case "$2" in
|
|
363
|
+
curation)
|
|
364
|
+
if [ "${_QTA_ASKED_CURATION:-}" != "r$1" ]; then
|
|
365
|
+
_qta_probe "$1" curation "$3"
|
|
366
|
+
DF_TRACKED_AREA_CURATION="$_QTA_ANSWER"
|
|
367
|
+
_QTA_ASKED_CURATION="r$1"
|
|
368
|
+
fi
|
|
369
|
+
;;
|
|
370
|
+
memory)
|
|
371
|
+
if [ "${_QTA_ASKED_MEMORY:-}" != "r$1" ]; then
|
|
372
|
+
_qta_probe "$1" memory "$3"
|
|
373
|
+
DF_TRACKED_AREA_MEMORY="$_QTA_ANSWER"
|
|
374
|
+
_QTA_ASKED_MEMORY="r$1"
|
|
375
|
+
fi
|
|
376
|
+
;;
|
|
377
|
+
esac
|
|
378
|
+
return 0
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
queue_tracked_area() {
|
|
382
|
+
local _qa_root="${1:-}" _qa_area="${2:-}" _qa_mode="${3:-git}"
|
|
383
|
+
case "$_qa_area" in
|
|
384
|
+
curation|memory)
|
|
385
|
+
_qta_ask "$_qa_root" "$_qa_area" "$_qa_mode"
|
|
386
|
+
;;
|
|
387
|
+
both)
|
|
388
|
+
if [ "${_QTA_ASKED_CURATION:-}" != "r$_qa_root" ] || [ "${_QTA_ASKED_MEMORY:-}" != "r$_qa_root" ]; then
|
|
389
|
+
_qta_probe "$_qa_root" both "$_qa_mode"
|
|
390
|
+
if [ "$_QTA_ANSWER" = "tracked" ]; then
|
|
391
|
+
# The union listed something: one call per area says which.
|
|
392
|
+
_QTA_ASKED_CURATION=""
|
|
393
|
+
_QTA_ASKED_MEMORY=""
|
|
394
|
+
_qta_ask "$_qa_root" curation "$_qa_mode"
|
|
395
|
+
_qta_ask "$_qa_root" memory "$_qa_mode"
|
|
396
|
+
else
|
|
397
|
+
DF_TRACKED_AREA_CURATION="$_QTA_ANSWER"
|
|
398
|
+
DF_TRACKED_AREA_MEMORY="$_QTA_ANSWER"
|
|
399
|
+
_QTA_ASKED_CURATION="r$_qa_root"
|
|
400
|
+
_QTA_ASKED_MEMORY="r$_qa_root"
|
|
401
|
+
fi
|
|
402
|
+
fi
|
|
403
|
+
;;
|
|
404
|
+
esac
|
|
405
|
+
return 0
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
# _qar_gate <memory_root> <memory_on> <curation_root> <curation_on> -- the one gate both
|
|
409
|
+
# append functions use. Sets _QAR_MEMORY_ON and _QAR_CURATION_ON to the flags as given,
|
|
410
|
+
# except that a queue whose area is not `untracked` reads "false" (paused), with one fixed
|
|
411
|
+
# log line. Only an enabled queue is asked about. When both are enabled at one root, one
|
|
412
|
+
# call over the union answers for both (queue_tracked_area `both`). Always returns 0.
|
|
413
|
+
_qar_gate() {
|
|
414
|
+
local _qg_mroot="$1" _qg_mon="$2" _qg_croot="$3" _qg_con="$4"
|
|
415
|
+
if [ "$_qg_mon" = "true" ] && [ "$_qg_con" = "true" ] && [ "$_qg_mroot" = "$_qg_croot" ]; then
|
|
416
|
+
queue_tracked_area "$_qg_mroot" both git
|
|
417
|
+
else
|
|
418
|
+
if [ "$_qg_mon" = "true" ]; then queue_tracked_area "$_qg_mroot" memory git; fi
|
|
419
|
+
if [ "$_qg_con" = "true" ]; then queue_tracked_area "$_qg_croot" curation git; fi
|
|
420
|
+
fi
|
|
421
|
+
if [ "$_qg_mon" = "true" ] && [ "${DF_TRACKED_AREA_MEMORY:-unknown}" != "untracked" ]; then
|
|
422
|
+
_qg_mon="false"
|
|
423
|
+
log "Queue skipped: the memory area is paused (git tracks files in its data folders, or did not answer); nothing written"
|
|
424
|
+
fi
|
|
425
|
+
if [ "$_qg_con" = "true" ] && [ "${DF_TRACKED_AREA_CURATION:-unknown}" != "untracked" ]; then
|
|
426
|
+
_qg_con="false"
|
|
427
|
+
log "Queue skipped: the curation area is paused (git tracks files in its data folders, or did not answer); nothing written"
|
|
428
|
+
fi
|
|
429
|
+
_QAR_MEMORY_ON="$_qg_mon"
|
|
430
|
+
_QAR_CURATION_ON="$_qg_con"
|
|
431
|
+
return 0
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
# _qar_area_of <root> <queue_file> -- sets _QAR_AREA to the area the queue lies in:
|
|
435
|
+
# `memory` below <root>/.devflow/memory, `curation` below the ledger folder (either name),
|
|
436
|
+
# and `both` for a queue in neither, which is paused when either area is.
|
|
437
|
+
_qar_area_of() {
|
|
438
|
+
case "$2" in
|
|
439
|
+
"$1"/.devflow/memory/*) _QAR_AREA="memory" ;;
|
|
440
|
+
"$1"/.devflow/learning/*|"$1"/.devflow/curation/*) _QAR_AREA="curation" ;;
|
|
441
|
+
*) _QAR_AREA="both" ;;
|
|
442
|
+
esac
|
|
443
|
+
return 0
|
|
444
|
+
}
|
|
445
|
+
|
|
248
446
|
queue_append_row() {
|
|
447
|
+
_qar_area_of "$1" "$2"
|
|
448
|
+
case "$_QAR_AREA" in
|
|
449
|
+
memory)
|
|
450
|
+
_qar_gate "$1" true "" false
|
|
451
|
+
if [ "$_QAR_MEMORY_ON" != "true" ]; then return 0; fi
|
|
452
|
+
;;
|
|
453
|
+
curation)
|
|
454
|
+
_qar_gate "" false "$1" true
|
|
455
|
+
if [ "$_QAR_CURATION_ON" != "true" ]; then return 0; fi
|
|
456
|
+
;;
|
|
457
|
+
*)
|
|
458
|
+
_qar_gate "$1" true "$1" true
|
|
459
|
+
if [ "$_QAR_MEMORY_ON" != "true" ] || [ "$_QAR_CURATION_ON" != "true" ]; then return 0; fi
|
|
460
|
+
;;
|
|
461
|
+
esac
|
|
249
462
|
_qar_build_row "$3" "$4" "$5" || return 0
|
|
250
463
|
if _qar_append "$1" "$2" "$_QAR_ROW"; then _qar_trim_overflow "$_QAR_FILE"; fi
|
|
251
464
|
return 0
|
|
@@ -257,6 +470,10 @@ queue_append_both() {
|
|
|
257
470
|
local _qab_memory_enabled="$5" _qab_curation_enabled="$6"
|
|
258
471
|
local _qab_role="$7" _qab_content="$8" _qab_ts="$9" _qab_row=""
|
|
259
472
|
|
|
473
|
+
# A paused queue reads as not enabled: the gate comes before the row is built.
|
|
474
|
+
_qar_gate "$_qab_memory_root" "$_qab_memory_enabled" "$_qab_curation_root" "$_qab_curation_enabled"
|
|
475
|
+
_qab_memory_enabled="$_QAR_MEMORY_ON"
|
|
476
|
+
_qab_curation_enabled="$_QAR_CURATION_ON"
|
|
260
477
|
if [ "$_qab_memory_enabled" != "true" ] && [ "$_qab_curation_enabled" != "true" ]; then return 0; fi
|
|
261
478
|
# One row for both queues: it is built once, and each enabled queue takes the same line.
|
|
262
479
|
_qar_build_row "$_qab_role" "$_qab_content" "$_qab_ts" || return 0
|
|
@@ -6,7 +6,10 @@
|
|
|
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
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).
|
|
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.
|
|
@@ -23,11 +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`.
|
|
26
|
-
# Section 4b: Curation
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
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.
|
|
31
35
|
# Section 5: Compaction resume directive — on a `compact` SessionStart, one fixed
|
|
32
36
|
# line telling the model to re-read the devflow command it was running from the
|
|
33
37
|
# phase in progress (D-COMPACT-RESUME-DIRECTIVE).
|
|
@@ -53,6 +57,12 @@
|
|
|
53
57
|
# - Section 6, changed start: ONE node process (claude-md-audit.cjs), and a
|
|
54
58
|
# sibling temp file renamed over the stamp. When none of the roots exists there
|
|
55
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.
|
|
56
66
|
|
|
57
67
|
# Safe no-op fallback: must exist before hook-bootstrap is sourced.
|
|
58
68
|
dbg() { :; }
|
|
@@ -267,12 +277,42 @@ CURATION_ENABLED="$_QG_CURATION"
|
|
|
267
277
|
# root is looked at once per session. A held, refused or skipped move leaves the old
|
|
268
278
|
# folder in place. A skipped move (git tracks files in a data folder or a backup folder,
|
|
269
279
|
# or git did not answer) sets DF_CURATION_MOVE_SKIPPED to `tracked` or `unknown`, and
|
|
270
|
-
#
|
|
280
|
+
# the curation area check below pauses curation on it.
|
|
271
281
|
DF_CURATION_MOVE_SKIPPED=""
|
|
272
282
|
if [ -n "$PROJECT_OK" ]; then
|
|
273
283
|
df_curation_move "$LEDGER_ROOT" retry 2>/dev/null || true
|
|
274
284
|
fi
|
|
275
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
|
|
315
|
+
|
|
276
316
|
# --- Section 1: Project Decisions TL;DR ---
|
|
277
317
|
if [ "$CURATION_ENABLED" = "true" ]; then
|
|
278
318
|
# Heal older installs that have .devflow/ but not .devflow/curation/, never
|
|
@@ -305,10 +345,13 @@ if [ "$CURATION_ENABLED" = "true" ]; then
|
|
|
305
345
|
# as DECISIONS_CONTEXT. It interpolates $LEDGER_ROOT, so it needs that root's
|
|
306
346
|
# shape gate, and it is left out when the index lists no entry: an empty
|
|
307
347
|
# corpus renders `(none)`. It is left out too when a link leads to the index,
|
|
308
|
-
# 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.
|
|
309
352
|
# Builtins only: no fork, save the log line of a refused index.
|
|
310
353
|
_SC_INDEX="$CURATION_DIR/index.md"; DECISIONS_INDEX_LINE=""
|
|
311
|
-
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."
|
|
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."
|
|
312
355
|
if [ -n "$DIRECTIVE_LEDGER_SAFE" ] && df_file_below "$LEDGER_ROOT" "$_SC_INDEX" && [ -s "$_SC_INDEX" ]; then
|
|
313
356
|
_SC_IDX1=""; IFS= read -r _SC_IDX1 < "$_SC_INDEX" || true
|
|
314
357
|
[ "$_SC_IDX1" != "(none)" ] && DECISIONS_INDEX_LINE="Index: $_SC_INDEX"
|
|
@@ -873,39 +916,37 @@ ${LEGACY_SECTION}"
|
|
|
873
916
|
fi
|
|
874
917
|
fi
|
|
875
918
|
|
|
876
|
-
# --- Section 4b: Curation
|
|
877
|
-
#
|
|
878
|
-
#
|
|
879
|
-
#
|
|
880
|
-
#
|
|
881
|
-
#
|
|
882
|
-
#
|
|
883
|
-
#
|
|
884
|
-
#
|
|
885
|
-
#
|
|
886
|
-
|
|
887
|
-
|
|
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
|
|
888
935
|
tracked)
|
|
889
|
-
|
|
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.'
|
|
890
937
|
;;
|
|
891
938
|
unknown)
|
|
892
|
-
|
|
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=""
|
|
893
948
|
;;
|
|
894
949
|
esac
|
|
895
|
-
if [ -n "$MOVE_SKIP_SECTION" ]; then
|
|
896
|
-
case "$SESSION_SOURCE" in
|
|
897
|
-
startup|resume|clear)
|
|
898
|
-
dbg "curation move skipped notice emitted"
|
|
899
|
-
if [ -n "$CONTEXT" ]; then
|
|
900
|
-
CONTEXT="${CONTEXT}
|
|
901
|
-
|
|
902
|
-
${MOVE_SKIP_SECTION}"
|
|
903
|
-
else
|
|
904
|
-
CONTEXT="$MOVE_SKIP_SECTION"
|
|
905
|
-
fi
|
|
906
|
-
;;
|
|
907
|
-
esac
|
|
908
|
-
fi
|
|
909
950
|
|
|
910
951
|
# --- Section 5: Compaction resume directive ---
|
|
911
952
|
# D-COMPACT-RESUME-DIRECTIVE: after a compaction nothing tells the model to re-read
|
|
@@ -1165,6 +1206,17 @@ fi
|
|
|
1165
1206
|
|
|
1166
1207
|
# --- Output ---
|
|
1167
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
|
+
|
|
1168
1220
|
# Only output if we have something to inject or to tell the user. The stamp is
|
|
1169
1221
|
# recorded here too: an audit that found nothing to show still ran, and recording it
|
|
1170
1222
|
# is what makes the next unchanged start free.
|
|
@@ -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
|
|
@@ -91,12 +91,15 @@ const LIMITS = Object.freeze({
|
|
|
91
91
|
* D-MARKERS: the ONLY home of the marker literals. Every other site builds a
|
|
92
92
|
* marker from these fields, and a source guard holds that no literal appears
|
|
93
93
|
* outside this object. The block markers bracket the PR body's test-plan block;
|
|
94
|
-
* the evidence marker is the first line of an evidence comment
|
|
94
|
+
* the evidence marker is the first line of an evidence comment, and its head prefix
|
|
95
|
+
* is what that line starts with whatever its head and key. The head prefix is the
|
|
96
|
+
* evidence open literal plus ' head:', so it adds no literal of its own.
|
|
95
97
|
*/
|
|
96
98
|
const MARKERS = Object.freeze({
|
|
97
99
|
BLOCK_START: '<!-- devflow:test-plan -->',
|
|
98
100
|
BLOCK_END: '<!-- /devflow:test-plan -->',
|
|
99
101
|
EVIDENCE_OPEN: '<!-- devflow:evidence',
|
|
102
|
+
get EVIDENCE_HEAD_PREFIX() { return this.EVIDENCE_OPEN + ' head:'; },
|
|
100
103
|
EVIDENCE_RE: Object.freeze(/^<!-- devflow:evidence head:(?<head>[0-9a-f]{40}) key:(?<key>[0-9a-f]{12}) -->$/),
|
|
101
104
|
});
|
|
102
105
|
|
|
@@ -1250,7 +1253,7 @@ function cell(text) {
|
|
|
1250
1253
|
*/
|
|
1251
1254
|
function stubLines(ev, t) {
|
|
1252
1255
|
return [
|
|
1253
|
-
MARKERS.
|
|
1256
|
+
MARKERS.EVIDENCE_HEAD_PREFIX + ev.head + ' key:' + ev.key + ' -->',
|
|
1254
1257
|
EVIDENCE_HEADING + ' ' + EM_DASH + ' ' + ev.head.slice(0, 7),
|
|
1255
1258
|
tallyLine(t),
|
|
1256
1259
|
'',
|