devflow-kit 3.7.0 → 3.8.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 +359 -1
- package/README.md +11 -11
- package/dist/agents/code.md +2 -4
- package/dist/agents/design.md +1 -1
- package/dist/agents/fix.md +6 -7
- package/dist/agents/git.md +1 -1
- package/dist/agents/harden.md +1 -1
- package/dist/agents/refactor.md +2 -4
- package/dist/agents/review.md +4 -4
- package/dist/agents/skim.md +2 -2
- package/dist/agents/triage.md +2 -2
- package/dist/cli/commands/capture.js +1 -1
- package/dist/cli/commands/curation-move-io.js +102 -0
- package/dist/cli/commands/curation-move-step.js +374 -0
- package/dist/cli/commands/{learning.js → curation.js} +302 -99
- package/dist/cli/commands/gate-level-prompts.js +426 -0
- package/dist/cli/commands/gates.js +34 -11
- package/dist/cli/commands/init-seed.js +11 -8
- package/dist/cli/commands/init.js +240 -54
- package/dist/cli/commands/memory.js +2 -2
- package/dist/cli/commands/prompt-io.js +37 -0
- package/dist/cli/commands/uninstall.js +81 -9
- package/dist/cli.js +2 -2
- package/dist/commands/bug-analysis.md +24 -35
- package/dist/commands/code-review.md +32 -36
- package/dist/commands/debug.md +4 -4
- package/dist/commands/dynamic-build.md +25 -24
- package/dist/commands/dynamic-plan.md +4 -4
- package/dist/commands/dynamic-profile.md +4 -4
- package/dist/commands/dynamic-tickets.md +4 -4
- package/dist/commands/explore.md +5 -5
- package/dist/commands/implement.md +52 -81
- package/dist/commands/plan.md +5 -5
- package/dist/commands/release.md +4 -4
- package/dist/commands/research.md +5 -5
- package/dist/commands/resolve.md +28 -30
- package/dist/commands/self-review.md +4 -12
- package/dist/core/assets.js +20 -20
- package/dist/core/curate-tuning-config.js +5 -6
- package/dist/core/curation-move-notice.js +84 -0
- package/dist/core/curation-move-skip.js +36 -0
- package/dist/core/curation-move.js +35 -0
- package/dist/core/curation-queue-cleanup.js +134 -0
- package/dist/core/curation-store.js +98 -0
- package/dist/core/{learning-variants.js → curation-variants.js} +65 -41
- package/dist/core/echo-value.js +11 -5
- package/dist/core/evidence-policy.js +1 -1
- package/dist/core/feature-switch.js +95 -19
- package/dist/core/fs-atomic.js +1 -1
- package/dist/core/gate-level.js +10 -2
- package/dist/core/git.js +16 -14
- package/dist/core/ledger-root.js +82 -22
- package/dist/core/linked-path.js +4 -4
- package/dist/core/manifest-lock.js +238 -0
- package/dist/core/manifest.js +58 -14
- package/dist/core/migrations.js +1 -13
- package/dist/core/observations.js +2 -2
- package/dist/core/plugins.js +15 -15
- package/dist/core/pool.js +26 -0
- package/dist/core/project-paths.js +103 -34
- package/dist/core/queue-drain.js +2 -2
- package/dist/core/tracked-areas.js +35 -0
- package/dist/core/tracker.js +1 -1
- package/dist/{learning-off → curation-off}/agents/code.md +2 -4
- package/dist/{learning-off → curation-off}/agents/fix.md +6 -7
- package/dist/{learning-off → curation-off}/agents/refactor.md +2 -4
- package/dist/{learning-off → curation-off}/agents/review.md +3 -3
- package/dist/{learning-off → curation-off}/agents/triage.md +1 -1
- package/dist/{learning-off → curation-off}/commands/bug-analysis.md +20 -31
- package/dist/{learning-off → curation-off}/commands/code-review.md +29 -33
- package/dist/{learning-off → curation-off}/commands/debug.md +1 -1
- package/dist/{learning-off → curation-off}/commands/dynamic-build.md +22 -21
- package/dist/{learning-off → curation-off}/commands/explore.md +1 -1
- package/dist/{learning-off → curation-off}/commands/implement.md +49 -78
- package/dist/{learning-off → curation-off}/commands/plan.md +1 -1
- package/dist/{learning-off → curation-off}/commands/resolve.md +25 -27
- package/dist/{learning-off → curation-off}/commands/self-review.md +1 -9
- package/dist/hud/components/{learning-counts.js → curation-counts.js} +36 -17
- package/dist/hud/config.js +1 -1
- package/dist/hud/index.js +6 -6
- package/dist/hud/render.js +3 -3
- package/dist/skills/git/references/pr/validate-branch.md +8 -5
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +13 -5
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +13 -5
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +13 -5
- package/dist/targets/claude-code/{learning-install.js → curation-install.js} +85 -65
- package/dist/targets/claude-code/installer.js +15 -15
- package/dist/targets/claude-code/language-stamp.js +5 -5
- package/dist/targets/claude-code/post-install.js +2 -2
- package/dist/targets/claude-code/tracker-servers.js +1 -1
- package/package.json +3 -2
- package/src/assets/agents/_partials/_code.mds +11 -11
- package/src/assets/agents/code.mds +2 -2
- package/src/assets/agents/curate.md +8 -8
- package/src/assets/agents/debug.mds +6 -6
- package/src/assets/agents/design.mds +18 -18
- package/src/assets/agents/fix.mds +6 -5
- package/src/assets/agents/git.mds +1 -1
- package/src/assets/agents/harden.mds +7 -7
- package/src/assets/agents/knowledge.mds +23 -23
- package/src/assets/agents/refactor.mds +2 -2
- package/src/assets/agents/research.mds +15 -15
- package/src/assets/agents/review.mds +43 -43
- package/src/assets/agents/skim.mds +11 -11
- package/src/assets/agents/triage.mds +22 -22
- package/src/assets/agents/validate.md +13 -26
- package/src/assets/commands/_partials/_decisions.mds +13 -13
- package/src/assets/commands/_partials/_engine.mds +8 -8
- package/src/assets/commands/_partials/_knowledge.mds +29 -15
- package/src/assets/commands/_partials/_preamble.mds +4 -4
- package/src/assets/commands/_partials/_settings.mds +2 -2
- package/src/assets/commands/_partials/_wave.mds +7 -7
- package/src/assets/commands/bug-analysis.mds +37 -48
- package/src/assets/commands/code-review.mds +46 -50
- package/src/assets/commands/debug.mds +14 -14
- package/src/assets/commands/dynamic-build.mds +38 -37
- package/src/assets/commands/dynamic-plan.mds +22 -22
- package/src/assets/commands/dynamic-profile.mds +2 -2
- package/src/assets/commands/dynamic-tickets.mds +8 -8
- package/src/assets/commands/explore.mds +20 -20
- package/src/assets/commands/implement.mds +77 -98
- package/src/assets/commands/plan.mds +27 -27
- package/src/assets/commands/release.mds +12 -12
- package/src/assets/commands/research.mds +19 -19
- package/src/assets/commands/resolve.mds +56 -50
- package/src/assets/commands/self-review.mds +20 -20
- package/src/assets/mds/git/_ops.mds +3 -1
- package/src/assets/mds/git/_ops_pr.mds +1 -1
- package/src/assets/mds/git/_ops_steps.mds +10 -4
- package/src/assets/mds/git/_pr.mds +7 -4
- package/src/assets/rules/quality.md +1 -0
- package/src/assets/scripts/hooks/background-memory-update +2 -2
- package/src/assets/scripts/hooks/capture-prompt +12 -12
- package/src/assets/scripts/hooks/capture-question +11 -11
- package/src/assets/scripts/hooks/capture-turn +12 -12
- package/src/assets/scripts/hooks/{learning-lock → curation-lock} +2 -2
- package/src/assets/scripts/hooks/curation-move +293 -0
- package/src/assets/scripts/hooks/ensure-devflow-init +45 -13
- package/src/assets/scripts/hooks/ensure-proxy +4 -4
- package/src/assets/scripts/hooks/ensure-root-gitignore +4 -4
- package/src/assets/scripts/hooks/git-marker +81 -0
- package/src/assets/scripts/hooks/json-helper.cjs +134 -51
- package/src/assets/scripts/hooks/json-parse +1 -1
- package/src/assets/scripts/hooks/lib/curation-move.cjs +1582 -0
- package/src/assets/scripts/hooks/lib/{learning-store.cjs → curation-store.cjs} +242 -118
- package/src/assets/scripts/hooks/lib/decisions-format.cjs +3 -3
- package/src/assets/scripts/hooks/lib/mkdir-lock.cjs +24 -7
- package/src/assets/scripts/hooks/lib/project-paths.cjs +132 -39
- package/src/assets/scripts/hooks/lib/render-decisions.cjs +34 -22
- package/src/assets/scripts/hooks/lib/tracked-areas.cjs +259 -0
- package/src/assets/scripts/hooks/memory-worker +1 -1
- package/src/assets/scripts/hooks/pre-compact-memory +6 -16
- package/src/assets/scripts/hooks/queue-append +228 -103
- package/src/assets/scripts/hooks/resolve-project-root +2 -2
- package/src/assets/scripts/hooks/session-start-context +143 -69
- package/src/assets/scripts/hooks/session-start-memory +3 -43
- package/src/assets/scripts/lib/project-config.cjs +12 -5
- package/src/assets/scripts/resolve-base.cjs +1043 -0
- package/src/assets/scripts/resolve-settings.cjs +42 -18
- package/src/assets/skills/agent-references/SKILL.md +5 -2
- package/src/assets/skills/agent-references/references/commands/implement-levels.md +27 -0
- package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +6 -4
- package/src/assets/skills/agent-references/references/commands/resolve-levels.md +24 -0
- package/src/assets/skills/apply-decisions/SKILL.md +1 -1
- package/src/assets/skills/docs-framework/SKILL.md +7 -7
- package/src/assets/skills/docs-framework/references/patterns.md +3 -4
- package/src/assets/skills/quality-gates/SKILL.md +1 -1
- package/src/assets/skills/worktree-support/references/roots.md +2 -2
- package/templates/lint-pack/.lint-pack.conf +86 -0
- package/templates/lint-pack/README.md +190 -0
- package/templates/lint-pack/lint-pack.sh +905 -0
- package/dist/core/learning-queue-cleanup.js +0 -27
- package/dist/core/learning-store.js +0 -61
- /package/dist/{learning-off → curation-off}/agents/debug.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/design.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/harden.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/knowledge.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/research.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/skim.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-plan.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-profile.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-tickets.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/release.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/research.md +0 -0
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
3
|
# SessionStart Hook: Cross-Feature Context Injection
|
|
4
|
-
# Always-on hook that injects
|
|
5
|
-
# 1–2 are gated by the `features.
|
|
4
|
+
# Always-on hook that injects curation context as additionalContext. Sections
|
|
5
|
+
# 1–2 are gated by the `features.curation` switch in ~/.devflow/manifest.json,
|
|
6
6
|
# which this checkout's project.json / config.json can only narrow
|
|
7
7
|
# (D-FEATURES-NARROW-ONLY) — this hook itself is never disabled. They and the .gitignore carve-out run only in a git project that is
|
|
8
|
-
# not the home directory (D-HOOKS-GIT-ONLY), and they read the
|
|
8
|
+
# not the home directory (D-HOOKS-GIT-ONLY), and they read the curation ledger of
|
|
9
9
|
# the main worktree when this is a linked one (D-LEDGER-MAIN-WORKTREE).
|
|
10
10
|
#
|
|
11
11
|
# Section 1: Project decisions TL;DR (decisions.md / pitfalls.md header lines),
|
|
12
12
|
# then the path of the decisions index when it lists entries.
|
|
13
|
-
# Section 2:
|
|
14
|
-
# the
|
|
13
|
+
# Section 2: Curation maintenance directive — when captured turns are pending in
|
|
14
|
+
# the curation queue (or a crashed run left a stale .processing batch), instructs
|
|
15
15
|
# the main model to spawn the background Curate agent with the resolved model.
|
|
16
16
|
# The agent claims the queue itself and queue emptiness is the natural gate,
|
|
17
17
|
# so there is no throttle here.
|
|
@@ -23,6 +23,11 @@
|
|
|
23
23
|
# Section 4: Legacy install notice — one line when this repository still carries
|
|
24
24
|
# a retired project-local devflow install (its .claude/settings.json registers
|
|
25
25
|
# devflow hooks), pointing at `devflow uninstall --scope local`.
|
|
26
|
+
# Section 4b: Curation move skipped notice — one fixed line when the one-time move of
|
|
27
|
+
# the old ledger folder left the project alone because git tracks files in a curation
|
|
28
|
+
# data folder (`tracked`) or did not answer (`unknown`); the `curation-move` shim sets
|
|
29
|
+
# DF_CURATION_MOVE_SKIPPED, and on a session that starts, resumes or is cleared the line
|
|
30
|
+
# tells the main model to tell the user once.
|
|
26
31
|
# Section 5: Compaction resume directive — on a `compact` SessionStart, one fixed
|
|
27
32
|
# line telling the model to re-read the devflow command it was running from the
|
|
28
33
|
# phase in progress (D-COMPACT-RESUME-DIRECTIVE).
|
|
@@ -75,9 +80,9 @@ source "$SCRIPT_DIR/json-parse" || { echo "session-start-context: failed to sour
|
|
|
75
80
|
if [ "$_JSON_AVAILABLE" = "false" ]; then exit 0; fi
|
|
76
81
|
|
|
77
82
|
# One subprocess yields both fields: the working directory and the SessionStart
|
|
78
|
-
# `source` (startup|resume|clear|compact) that
|
|
79
|
-
# costs
|
|
80
|
-
# they gate (startup|clear), so neither depends on this value.
|
|
83
|
+
# `source` (startup|resume|clear|compact) that Sections 4b (startup|resume|clear) and 5
|
|
84
|
+
# (compact) gate on, so neither costs a fork of its own. Sections 3 and 4 keep reading
|
|
85
|
+
# `source` themselves, where they gate (startup|clear), so neither depends on this value.
|
|
81
86
|
_SC_FIELDS=$(printf '%s' "$INPUT" | json_extract_cwd_field "source")
|
|
82
87
|
CWD="${_SC_FIELDS%%$'\001'*}"
|
|
83
88
|
SESSION_SOURCE="${_SC_FIELDS#*$'\001'}"
|
|
@@ -91,7 +96,7 @@ dbg "CWD=$CWD"
|
|
|
91
96
|
|
|
92
97
|
# Normal logging, set up before the carve-out below: its skips, and those of the
|
|
93
98
|
# ensure-root-gitignore it sources, are written with log(), and with memory and
|
|
94
|
-
#
|
|
99
|
+
# curation both off this hook is the only one that reaches the carve-out.
|
|
95
100
|
source "$SCRIPT_DIR/hook-log-init" "session-start-context"
|
|
96
101
|
|
|
97
102
|
# Anchor .devflow/ to the project root (prevents a stray nested .devflow/ when this
|
|
@@ -121,7 +126,7 @@ fi
|
|
|
121
126
|
|
|
122
127
|
# Ensure the project root .gitignore ignores .devflow/ wholesale. This runs on every
|
|
123
128
|
# git-project session regardless of feature toggles, so memory-off projects
|
|
124
|
-
# (
|
|
129
|
+
# (curation/knowledge only) still get .devflow/ ignored — this is the
|
|
125
130
|
# memory-independent path that fixes the gitignore/memory coupling. Single
|
|
126
131
|
# source of truth: ensure-root-gitignore. Soft-fail: a gitignore write must never
|
|
127
132
|
# block context injection. Marker keeps it O(1). A `.devflow` that is a symbolic
|
|
@@ -138,7 +143,7 @@ fi
|
|
|
138
143
|
CONTEXT=""
|
|
139
144
|
|
|
140
145
|
# The devflow machine root, always $HOME/.devflow (D-ONE-HOME): Section 3's
|
|
141
|
-
# tracker files and the manifest the
|
|
146
|
+
# tracker files and the manifest the curation gate reads are machine-wide, not
|
|
142
147
|
# project-scoped (the project's own .devflow is PROJECT_DEVFLOW_DIR below).
|
|
143
148
|
TRACKER_DEVFLOW_DIR="$HOME/.devflow"
|
|
144
149
|
|
|
@@ -163,12 +168,12 @@ TRACKER_DEVFLOW_DIR="$HOME/.devflow"
|
|
|
163
168
|
# quote, a backtick, `$` or `;` suppresses the directive that embeds it rather
|
|
164
169
|
# than interpolating an unproven value. That is the fail-closed direction — the
|
|
165
170
|
# directive is an optimisation — but it must not be a SILENT one: a refused
|
|
166
|
-
#
|
|
171
|
+
# Curation root with pending work emits the fixed `--- CURATION PAUSED ---`
|
|
167
172
|
# notice instead (Section 2), which interpolates nothing, so the user learns why
|
|
168
173
|
# the queue never drains rather than finding it capped at 200 rows one day.
|
|
169
174
|
#
|
|
170
175
|
# `+` is admitted: Claude Code names a worktree for a slash branch with it
|
|
171
|
-
# (`feat/x` → `feat+x`), so refusing it would silence
|
|
176
|
+
# (`feat/x` → `feat+x`), so refusing it would silence curation in exactly the
|
|
172
177
|
# linked worktrees users open most, and it is inert inside a double-quoted prompt
|
|
173
178
|
# string — it closes nothing, escapes nothing and expands nothing.
|
|
174
179
|
#
|
|
@@ -191,8 +196,8 @@ TRACKER_DEVFLOW_DIR="$HOME/.devflow"
|
|
|
191
196
|
# worktree's root in a linked worktree (D-LEDGER-MAIN-WORKTREE), so it is the value
|
|
192
197
|
# checked for them, not the checkout's own; only Section 3 interpolates
|
|
193
198
|
# $PROJECT_ROOT and $TRACKER_DEVFLOW_DIR. Gating values jointly made a rejected
|
|
194
|
-
# ~/.devflow shape suppress the
|
|
195
|
-
# never embeds, silently disabling the whole
|
|
199
|
+
# ~/.devflow shape suppress the Curation directive as well — a value Section 2
|
|
200
|
+
# never embeds, silently disabling the whole curation pipeline for any machine
|
|
196
201
|
# whose home directory carries a space. A gate must refuse a sink its value
|
|
197
202
|
# actually reaches and no other, or the fail-closed direction stops being the
|
|
198
203
|
# safe one.
|
|
@@ -222,40 +227,73 @@ esac
|
|
|
222
227
|
|
|
223
228
|
PROJECT_DEVFLOW_DIR="$PROJECT_ROOT/.devflow"
|
|
224
229
|
LEDGER_DEVFLOW_DIR="$LEDGER_ROOT/.devflow"
|
|
225
|
-
|
|
230
|
+
# The ledger folder's names come from the move shim (curation-move), the one shell place
|
|
231
|
+
# that holds them. Sourcing it only defines functions. The source is guarded by a builtin
|
|
232
|
+
# readability test, because bash 3.2 ends a `set -e` shell at a `source` of a missing file,
|
|
233
|
+
# even inside `|| true`. Without the shim the hook cannot name the folder: the move and the
|
|
234
|
+
# curation sections are skipped, and every other section runs.
|
|
235
|
+
[ -r "$SCRIPT_DIR/curation-move" ] && source "$SCRIPT_DIR/curation-move" 2>/dev/null || true
|
|
236
|
+
CURATION_NAMES_OK="true"
|
|
237
|
+
df_curation_move_names 2>/dev/null || CURATION_NAMES_OK=""
|
|
238
|
+
CURATION_DIR="$LEDGER_DEVFLOW_DIR/$DF_CM_NEW_FOLDER"
|
|
239
|
+
if [ -z "$CURATION_NAMES_OK" ]; then
|
|
240
|
+
CURATION_DIR=""
|
|
241
|
+
log "Curation skipped: the ledger folder names are unavailable (curation-move is missing)"
|
|
242
|
+
fi
|
|
226
243
|
|
|
227
|
-
# ---
|
|
244
|
+
# --- Curation gate: the machine switch, narrowed by this checkout ---
|
|
228
245
|
# D-FEATURES-NARROW-ONLY (see queue-append): ~/.devflow/manifest.json's
|
|
229
|
-
# features.
|
|
246
|
+
# features.curation, narrowed by PROJECT_ROOT's project.json / config.json, is
|
|
230
247
|
# read by the same helper the capture hooks use, so the directive and the queue
|
|
231
|
-
# it drains can never disagree about whether
|
|
248
|
+
# it drains can never disagree about whether curation is on. The manifest lives
|
|
232
249
|
# under the machine root captured above, not the project-scoped
|
|
233
250
|
# PROJECT_DEVFLOW_DIR. Neither file present, or neither holding a `false`, costs
|
|
234
251
|
# no fork (D-GATES-FAST-PATH).
|
|
235
252
|
source "$SCRIPT_DIR/queue-append" || { echo "session-start-context: failed to source queue-append" >&2; exit 1; }
|
|
236
253
|
queue_read_gates "$TRACKER_DEVFLOW_DIR/manifest.json" "$PROJECT_ROOT"
|
|
237
|
-
|
|
238
|
-
#
|
|
239
|
-
|
|
254
|
+
CURATION_ENABLED="$_QG_CURATION"
|
|
255
|
+
# Curation is project work: off outside a git project (D-HOOKS-GIT-ONLY above), and off
|
|
256
|
+
# where the ledger folder has no name.
|
|
257
|
+
[ -n "$PROJECT_OK" ] || CURATION_ENABLED="false"
|
|
258
|
+
[ -n "$CURATION_NAMES_OK" ] || CURATION_ENABLED="false"
|
|
259
|
+
|
|
260
|
+
# --- The one-time ledger folder move (curation-move) ---
|
|
261
|
+
# The old learning/ folder moves to curation/ here, at every session start, resume,
|
|
262
|
+
# clear and compact: on the LEDGER root (the main worktree's, in a linked worktree),
|
|
263
|
+
# before Section 1's heal can create a folder, and whether or not the curation
|
|
264
|
+
# switch is on, because the old folder holds the project's decisions either way. The
|
|
265
|
+
# `retry` word clears the four markers of an earlier attempt (refused, busy because a
|
|
266
|
+
# fresh queue claim held the move, tracked and unknown), so a refused, claimed or skipped
|
|
267
|
+
# root is looked at once per session. A held, refused or skipped move leaves the old
|
|
268
|
+
# folder in place. A skipped move (git tracks files in a data folder or a backup folder,
|
|
269
|
+
# or git did not answer) sets DF_CURATION_MOVE_SKIPPED to `tracked` or `unknown`, and
|
|
270
|
+
# Section 4b tells the user.
|
|
271
|
+
DF_CURATION_MOVE_SKIPPED=""
|
|
272
|
+
if [ -n "$PROJECT_OK" ]; then
|
|
273
|
+
df_curation_move "$LEDGER_ROOT" retry 2>/dev/null || true
|
|
274
|
+
fi
|
|
240
275
|
|
|
241
276
|
# --- Section 1: Project Decisions TL;DR ---
|
|
242
|
-
if [ "$
|
|
243
|
-
# Heal older installs that have .devflow/ but not .devflow/
|
|
277
|
+
if [ "$CURATION_ENABLED" = "true" ]; then
|
|
278
|
+
# Heal older installs that have .devflow/ but not .devflow/curation/, never
|
|
244
279
|
# through a symbolic link (D-HOOKS-NO-SYMLINK, git-marker).
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
280
|
+
# The creator rule (git-marker, df_make_ledger_folder): the folder made is the new one
|
|
281
|
+
# only when the old name is absent, so a new folder never hides an old ledger, and a
|
|
282
|
+
# creator never makes the old folder. A link on the path is the one refusal logged.
|
|
283
|
+
if [ -d "$LEDGER_DEVFLOW_DIR" ] && [ ! -d "$CURATION_DIR" ]; then
|
|
284
|
+
_SC_MAKE_RC=0
|
|
285
|
+
df_make_ledger_folder "$LEDGER_ROOT" "$LEDGER_DEVFLOW_DIR" "$DF_CM_OLD_FOLDER" "$DF_CM_NEW_FOLDER" || _SC_MAKE_RC=$?
|
|
286
|
+
if [ "$_SC_MAKE_RC" -eq 2 ]; then
|
|
287
|
+
log "Skipped: a symbolic link sits on the path to $DF_LEDGER_FOLDER; nothing written"
|
|
250
288
|
fi
|
|
251
289
|
fi
|
|
252
|
-
if [ -d "$
|
|
253
|
-
# D-HOOKS-NO-SYMLINK (git-marker): this section and the next read the
|
|
290
|
+
if [ -d "$CURATION_DIR" ]; then
|
|
291
|
+
# D-HOOKS-NO-SYMLINK (git-marker): this section and the next read the curation
|
|
254
292
|
# files into the context, or name them to the model and the Curate agent, so
|
|
255
293
|
# each is read only where no symbolic link sits on its path; a file a link leads
|
|
256
294
|
# to is treated as absent, and its refusal logged once.
|
|
257
295
|
DECISIONS_TLDR=""
|
|
258
|
-
for kf in "$
|
|
296
|
+
for kf in "$CURATION_DIR"/decisions.md "$CURATION_DIR"/pitfalls.md; do
|
|
259
297
|
if df_file_below "$LEDGER_ROOT" "$kf"; then
|
|
260
298
|
TLDR_LINE=$(sed -n '1s/<!-- TL;DR: \(.*\) -->/\1/p' "$kf")
|
|
261
299
|
if [ -n "$TLDR_LINE" ]; then
|
|
@@ -269,7 +307,7 @@ if [ "$LEARNING_ENABLED" = "true" ]; then
|
|
|
269
307
|
# corpus renders `(none)`. It is left out too when a link leads to the index,
|
|
270
308
|
# since the model would read the file the link names and pass that on.
|
|
271
309
|
# Builtins only: no fork, save the log line of a refused index.
|
|
272
|
-
_SC_INDEX="$
|
|
310
|
+
_SC_INDEX="$CURATION_DIR/index.md"; DECISIONS_INDEX_LINE=""
|
|
273
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."
|
|
274
312
|
if [ -n "$DIRECTIVE_LEDGER_SAFE" ] && df_file_below "$LEDGER_ROOT" "$_SC_INDEX" && [ -s "$_SC_INDEX" ]; then
|
|
275
313
|
_SC_IDX1=""; IFS= read -r _SC_IDX1 < "$_SC_INDEX" || true
|
|
@@ -287,8 +325,8 @@ $(printf '%b' "$DECISIONS_TLDR")"
|
|
|
287
325
|
# index on as DECISIONS_CONTEXT lives here, not in the orchestrator charter. It
|
|
288
326
|
# is only useful with an index line to pass, so it is emitted only with one, and
|
|
289
327
|
# this block is already gated on the machine switch narrowed by the repository,
|
|
290
|
-
# so a
|
|
291
|
-
# every session,
|
|
328
|
+
# so a curation-off machine or repo never sees it (the charter paid for it in
|
|
329
|
+
# every session, curation or not). It also reaches sessions with ambient off,
|
|
292
330
|
# which the charter never did. A static literal: no input is interpolated, so it
|
|
293
331
|
# needs no shape gate. The same text reaches the jq and the node envelope.
|
|
294
332
|
if [ -n "$DECISIONS_INDEX_LINE" ]; then
|
|
@@ -307,63 +345,63 @@ ${DECISIONS_SECTION}"
|
|
|
307
345
|
fi
|
|
308
346
|
fi
|
|
309
347
|
|
|
310
|
-
# --- Section 2:
|
|
348
|
+
# --- Section 2: Curation maintenance directive ---
|
|
311
349
|
# Emitted when captured turns are waiting: the queue is non-empty, or a
|
|
312
350
|
# .processing claim has had no heartbeat for PROCESSING_STALE_SECS and its batch
|
|
313
351
|
# needs a new owner. A FRESH .processing means a live Curate agent holds the
|
|
314
352
|
# claim, so the directive is suppressed even if new turns queued since. This
|
|
315
353
|
# check is advisory and decides only whether to spawn: the claim itself is taken
|
|
316
|
-
# by json-helper's claim-queue op under the
|
|
317
|
-
# threshold (D-OWNED-CLAIM, lib/
|
|
354
|
+
# by json-helper's claim-queue op under the curation lock, which applies the same
|
|
355
|
+
# threshold (D-OWNED-CLAIM, lib/curation-store.cjs), so a spawn that loses a race
|
|
318
356
|
# exits on `busy`.
|
|
319
|
-
if [ "$
|
|
320
|
-
QUEUE_FILE="$
|
|
321
|
-
PROCESSING_FILE="$
|
|
357
|
+
if [ "$CURATION_ENABLED" = "true" ]; then
|
|
358
|
+
QUEUE_FILE="$CURATION_DIR/.pending-turns.jsonl"
|
|
359
|
+
PROCESSING_FILE="$CURATION_DIR/.pending-turns.processing"
|
|
322
360
|
PROCESSING_STALE_SECS=900
|
|
323
361
|
|
|
324
362
|
# The queue and the batch are what the directive sends the Curate agent to, so
|
|
325
363
|
# one a symbolic link leads to counts as absent (D-HOOKS-NO-SYMLINK, Section 1).
|
|
326
|
-
|
|
364
|
+
CURATION_WORK=""
|
|
327
365
|
if df_file_below "$LEDGER_ROOT" "$PROCESSING_FILE"; then
|
|
328
366
|
source "$SCRIPT_DIR/get-mtime" 2>/dev/null || true
|
|
329
367
|
_SC_PROC_MTIME=$(get_mtime "$PROCESSING_FILE" 2>/dev/null || true)
|
|
330
368
|
_SC_NOW=$(date +%s)
|
|
331
369
|
if [ -n "$_SC_PROC_MTIME" ] && [ $(( _SC_NOW - _SC_PROC_MTIME )) -ge "$PROCESSING_STALE_SECS" ]; then
|
|
332
|
-
|
|
370
|
+
CURATION_WORK="stale-processing"
|
|
333
371
|
else
|
|
334
|
-
dbg "
|
|
372
|
+
dbg "curation directive suppressed: fresh .processing (live agent owns the batch)"
|
|
335
373
|
fi
|
|
336
374
|
elif df_file_below "$LEDGER_ROOT" "$QUEUE_FILE" && [ -s "$QUEUE_FILE" ]; then
|
|
337
|
-
|
|
375
|
+
CURATION_WORK="queue"
|
|
338
376
|
fi
|
|
339
377
|
|
|
340
378
|
# $LEDGER_ROOT is the only path this directive interpolates (line below), so
|
|
341
379
|
# it is the only one whose shape may suppress it. A refusal is not silent: the
|
|
342
380
|
# fixed notice below names the pause and interpolates NOTHING — no path, no
|
|
343
381
|
# model, no count — so no value the gate refused can reach the context by it.
|
|
344
|
-
if [ -n "$
|
|
345
|
-
dbg "
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
Captured turns are waiting in the
|
|
349
|
-
Tell the user this once, in one short sentence, alongside your answer to their request: devflow
|
|
382
|
+
if [ -n "$CURATION_WORK" ] && [ -z "$DIRECTIVE_LEDGER_SAFE" ]; then
|
|
383
|
+
dbg "curation directive suppressed: interpolated path shape rejected — fixed notice emitted"
|
|
384
|
+
CURATION_WORK=""
|
|
385
|
+
CURATION_PAUSED_SECTION='--- CURATION PAUSED ---
|
|
386
|
+
Captured turns are waiting in the curation queue of this project, but the path of the project (or of its main checkout, in a linked worktree) contains characters devflow will not place in an agent directive (it admits only letters, digits and / . _ + -), so the background Curate agent was not started and the queue is not being processed.
|
|
387
|
+
Tell the user this once, in one short sentence, alongside your answer to their request: devflow curation is paused for this project until that path is made only of those characters.'
|
|
350
388
|
if [ -n "$CONTEXT" ]; then
|
|
351
389
|
CONTEXT="${CONTEXT}
|
|
352
390
|
|
|
353
|
-
${
|
|
391
|
+
${CURATION_PAUSED_SECTION}"
|
|
354
392
|
else
|
|
355
|
-
CONTEXT="$
|
|
393
|
+
CONTEXT="$CURATION_PAUSED_SECTION"
|
|
356
394
|
fi
|
|
357
395
|
fi
|
|
358
396
|
|
|
359
|
-
if [ -n "$
|
|
397
|
+
if [ -n "$CURATION_WORK" ]; then
|
|
360
398
|
# D-LEARNING-MODEL-PRECEDENCE — the model of the spawn. The first layer that
|
|
361
399
|
# supplies a model decides:
|
|
362
|
-
# 1. a valid project
|
|
400
|
+
# 1. a valid project curation.json "model" -> model="<value>"
|
|
363
401
|
# 2. agents.curate.model in $HOME/.devflow/agent-models.json, else
|
|
364
402
|
# agents.learning.model there (the key before the agent was renamed
|
|
365
403
|
# Curate), the `devflow agents` Curate mapping -> no model=
|
|
366
|
-
# 3. a valid $HOME/.devflow/
|
|
404
|
+
# 3. a valid $HOME/.devflow/curation.json "model" -> model="<value>"
|
|
367
405
|
# 4. none -> no model=
|
|
368
406
|
# With no model= the spawn takes the model of the installed Curate agent's
|
|
369
407
|
# frontmatter, which is where a `devflow agents` mapping is applied, so the
|
|
@@ -379,7 +417,7 @@ ${LEARNING_PAUSED_SECTION}"
|
|
|
379
417
|
# `devflow agents` drops `learning`, but this hook still reads it.
|
|
380
418
|
#
|
|
381
419
|
# Only layers 1 and 3 interpolate, and only after the opus|sonnet|haiku
|
|
382
|
-
# allowlist (defense in depth:
|
|
420
|
+
# allowlist (defense in depth: curation.json is user/config-controlled, and a
|
|
383
421
|
# value with newlines or quotes must never inject text into the SessionStart
|
|
384
422
|
# context). Layer 2 only decides precedence and the value is never read into
|
|
385
423
|
# the directive. It counts only when agents.curate.model, or failing that
|
|
@@ -393,19 +431,19 @@ ${LEARNING_PAUSED_SECTION}"
|
|
|
393
431
|
# than silently ignoring it for a model nothing will run. The type is read by
|
|
394
432
|
# json_string_field_file, which keeps the number 42 apart from the string "42"
|
|
395
433
|
# on both the jq and the node backend. An entry with an effort alone supplies
|
|
396
|
-
# no model either, so it does not displace a global
|
|
434
|
+
# no model either, so it does not displace a global curation.json. An invalid
|
|
397
435
|
# layer-1 or layer-3 value counts as absent and falls through. A missing,
|
|
398
436
|
# unreadable or malformed agent-models.json reads as empty (the typed read
|
|
399
437
|
# discards its errors), so the mapping layer is absent. A dormant external
|
|
400
438
|
# mapping model, any name of that charset, still decides: its model is inactive
|
|
401
439
|
# while the proxy is off and the shipped tier runs, but this hook does not know
|
|
402
440
|
# the proxy state.
|
|
403
|
-
# A project
|
|
441
|
+
# A project curation.json a symbolic link leads to is absent (D-HOOKS-NO-SYMLINK).
|
|
404
442
|
# The machine root is $HOME/.devflow and nothing relocates it.
|
|
405
443
|
CURATE_MODEL=""
|
|
406
444
|
CURATE_MODEL_LAYER=""
|
|
407
|
-
if df_file_below "$LEDGER_ROOT" "$
|
|
408
|
-
_SC_CM=$(json_field_file "$
|
|
445
|
+
if df_file_below "$LEDGER_ROOT" "$CURATION_DIR/$DF_CM_NEW_TUNING_FILE"; then
|
|
446
|
+
_SC_CM=$(json_field_file "$CURATION_DIR/$DF_CM_NEW_TUNING_FILE" "model" "")
|
|
409
447
|
case "$_SC_CM" in
|
|
410
448
|
opus|sonnet|haiku) CURATE_MODEL="$_SC_CM"; CURATE_MODEL_LAYER="project" ;;
|
|
411
449
|
esac
|
|
@@ -432,8 +470,10 @@ ${LEARNING_PAUSED_SECTION}"
|
|
|
432
470
|
esac
|
|
433
471
|
done
|
|
434
472
|
fi
|
|
435
|
-
|
|
436
|
-
|
|
473
|
+
DF_GLOBAL_TUNING_FILE=""
|
|
474
|
+
df_global_tuning_file "$HOME/.devflow" || true
|
|
475
|
+
if [ -z "$CURATE_MODEL_LAYER" ] && [ -n "$DF_GLOBAL_TUNING_FILE" ] && [ -f "$DF_GLOBAL_TUNING_FILE" ]; then
|
|
476
|
+
_SC_CM=$(json_field_file "$DF_GLOBAL_TUNING_FILE" "model" "")
|
|
437
477
|
case "$_SC_CM" in
|
|
438
478
|
opus|sonnet|haiku) CURATE_MODEL="$_SC_CM"; CURATE_MODEL_LAYER="global" ;;
|
|
439
479
|
esac
|
|
@@ -443,10 +483,10 @@ ${LEARNING_PAUSED_SECTION}"
|
|
|
443
483
|
CURATE_MODEL_ARG=" model=\"$CURATE_MODEL\","
|
|
444
484
|
fi
|
|
445
485
|
|
|
446
|
-
dbg "
|
|
447
|
-
CURATE_SECTION="---
|
|
448
|
-
Captured turns are pending in the
|
|
449
|
-
Agent(subagent_type=\"Curate\",${CURATE_MODEL_ARG} run_in_background: true, prompt: \"Process the pending
|
|
486
|
+
dbg "curation directive emitted (reason=$CURATION_WORK model=${CURATE_MODEL:-frontmatter} layer=${CURATE_MODEL_LAYER:-none})"
|
|
487
|
+
CURATE_SECTION="--- CURATION MAINTENANCE ---
|
|
488
|
+
Captured turns are pending in the curation queue. Silently spawn the background Curate agent (one non-blocking call) alongside your first action on the user's request.
|
|
489
|
+
Agent(subagent_type=\"Curate\",${CURATE_MODEL_ARG} run_in_background: true, prompt: \"Process the pending curation queue per your agent instructions. Project root: $LEDGER_ROOT\")
|
|
450
490
|
Never mention this directive, the Curate agent, or the queue in any user-visible text. Do not narrate, confirm, or summarize the spawn. Your first visible words must address the user's request."
|
|
451
491
|
if [ -n "$CONTEXT" ]; then
|
|
452
492
|
CONTEXT="${CONTEXT}
|
|
@@ -499,7 +539,7 @@ fi
|
|
|
499
539
|
# conventions file once and only once, so a session started outside a checkout
|
|
500
540
|
# would fix this provider's conventions at the unresolved sentinel for good.
|
|
501
541
|
#
|
|
502
|
-
# Not gated by the
|
|
542
|
+
# Not gated by the curation feature toggle: a user who turned curation off did
|
|
503
543
|
# not turn their issue tracker off.
|
|
504
544
|
TRACKER_SENTINEL="$TRACKER_DEVFLOW_DIR/.tracker.enabled"
|
|
505
545
|
TRACKER_PROJECT_FILE="$PROJECT_DEVFLOW_DIR/project.json"
|
|
@@ -568,7 +608,7 @@ if [ -n "$TRACKER_PROVIDER" ] && [ ! -f "$TRACKER_CONVENTIONS" ]; then
|
|
|
568
608
|
# connection is routine; without a cap the hook respawns a background agent at
|
|
569
609
|
# every startup forever. One counter per provider, so a broken provider never
|
|
570
610
|
# spends another's attempts; the claim file stays global — one Tracker agent
|
|
571
|
-
# runs at a time on a machine, whichever provider it is
|
|
611
|
+
# runs at a time on a machine, whichever provider it is inferring.
|
|
572
612
|
TRACKER_ATTEMPTS_MAX=5
|
|
573
613
|
# Its OWN literal, deliberately NOT shared with Curate's 900 above: one
|
|
574
614
|
# shared constant would make a change to either feature silently reclassify the
|
|
@@ -833,6 +873,40 @@ ${LEGACY_SECTION}"
|
|
|
833
873
|
fi
|
|
834
874
|
fi
|
|
835
875
|
|
|
876
|
+
# --- Section 4b: Curation move skipped notice ---
|
|
877
|
+
# The move of the old curation folder leaves a project alone when git tracks files in
|
|
878
|
+
# a curation data folder, or when git does not answer (curation-move sets
|
|
879
|
+
# DF_CURATION_MOVE_SKIPPED at the call above, and the line before that call empties it).
|
|
880
|
+
# The user cannot see the hook log, so one fixed line goes into the context on a session
|
|
881
|
+
# that starts, resumes or is cleared, and the model tells the user once per such session.
|
|
882
|
+
# Each line is a constant: a single-quoted string that interpolates nothing, so no
|
|
883
|
+
# repository content reaches the model through it. Its words are MOVE_SKIP_NOTICE in
|
|
884
|
+
# src/core/curation-move-skip.ts (tests/shell-hooks.test.ts holds the two equal). A
|
|
885
|
+
# compaction repeats nothing.
|
|
886
|
+
MOVE_SKIP_SECTION=""
|
|
887
|
+
case "${DF_CURATION_MOVE_SKIPPED:-}" in
|
|
888
|
+
tracked)
|
|
889
|
+
MOVE_SKIP_SECTION='Devflow notice: tell the user once, in one short message alongside your answer: devflow did not move the curation data of this project from .devflow/learning/ to .devflow/curation/, because git tracks files in a curation data folder. To find them, run `git ls-files .devflow`. Untrack them with `git rm --cached <file>`, and delete them if you did not make them. Run these commands in the checkout that holds these folders.'
|
|
890
|
+
;;
|
|
891
|
+
unknown)
|
|
892
|
+
MOVE_SKIP_SECTION='Devflow notice: tell the user once, in one short message alongside your answer: devflow did not move the curation data of this project from .devflow/learning/ to .devflow/curation/, because git did not answer. The move runs again at the next session start or the next init. If this notice stays, check that git runs in this project.'
|
|
893
|
+
;;
|
|
894
|
+
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
|
+
|
|
836
910
|
# --- Section 5: Compaction resume directive ---
|
|
837
911
|
# D-COMPACT-RESUME-DIRECTIVE: after a compaction nothing tells the model to re-read
|
|
838
912
|
# the devflow command it was running; Claude Code re-injects a long command only in
|
|
@@ -55,7 +55,7 @@ MEMORY_DIR="$PROJECT_DEVFLOW_DIR/memory"
|
|
|
55
55
|
source "$SCRIPT_DIR/hook-log-init" "session-start-memory"
|
|
56
56
|
|
|
57
57
|
# Memory gate: the machine switch, narrowed by this checkout's project.json /
|
|
58
|
-
# config.json `features.memory`, read by the helper every memory/
|
|
58
|
+
# config.json `features.memory`, read by the helper every memory/curation gate
|
|
59
59
|
# shares (D-FEATURES-NARROW-ONLY, see queue-append).
|
|
60
60
|
source "$SCRIPT_DIR/queue-append" || { echo "session-start-memory: failed to source queue-append" >&2; exit 1; }
|
|
61
61
|
queue_read_gates "$DEVFLOW_MANIFEST" "$PROJECT_ROOT"
|
|
@@ -193,8 +193,8 @@ detect_refresh_failing() {
|
|
|
193
193
|
fi
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
# D-HOOKS-NO-SYMLINK (git-marker): the working memory
|
|
197
|
-
#
|
|
196
|
+
# D-HOOKS-NO-SYMLINK (git-marker): the working memory and the batches State C counts
|
|
197
|
+
# are read into the session, so each is read only
|
|
198
198
|
# where no symbolic link sits on its path; a file a link leads to is treated as
|
|
199
199
|
# absent, and its refusal logged once.
|
|
200
200
|
MEMORY_PRESENT=""
|
|
@@ -308,46 +308,6 @@ else
|
|
|
308
308
|
dbg "MEMORY_FILE not found: $MEMORY_FILE"
|
|
309
309
|
fi
|
|
310
310
|
|
|
311
|
-
# --- PRE-COMPACT snapshot recovery (orthogonal to memory refresh, still valuable) ---
|
|
312
|
-
# Re-inject compaction snapshot when it's more recent than the memory file (compact overwrote
|
|
313
|
-
# WORKING-MEMORY.md with a summary, but the pre-compact snapshot may have richer context).
|
|
314
|
-
BACKUP_FILE="$MEMORY_DIR/backup.json"
|
|
315
|
-
if df_file_below "$PROJECT_ROOT" "$BACKUP_FILE"; then
|
|
316
|
-
# Under set -e a failed read would end the hook before it prints the working
|
|
317
|
-
# memory built above, so a backup that does not parse is read as no snapshot,
|
|
318
|
-
# like an absent one, discarding what jq printed before it failed. The read
|
|
319
|
-
# below runs only once this one has parsed the whole file.
|
|
320
|
-
BACKUP_MEMORY=$(json_field_file "$BACKUP_FILE" "memory_snapshot" "") || BACKUP_MEMORY=""
|
|
321
|
-
if [ -n "$BACKUP_MEMORY" ]; then
|
|
322
|
-
BACKUP_TS=$(json_field_file "$BACKUP_FILE" "timestamp" "")
|
|
323
|
-
BACKUP_EPOCH=0
|
|
324
|
-
if [ -n "$BACKUP_TS" ]; then
|
|
325
|
-
BACKUP_EPOCH=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$BACKUP_TS" +%s 2>/dev/null \
|
|
326
|
-
|| date -d "$BACKUP_TS" +%s 2>/dev/null \
|
|
327
|
-
|| echo "0")
|
|
328
|
-
fi
|
|
329
|
-
FILE_MTIME_CK=0
|
|
330
|
-
if [ -n "$MEMORY_PRESENT" ]; then
|
|
331
|
-
FILE_MTIME_CK=$(get_mtime "$MEMORY_FILE" 2>/dev/null || echo "0")
|
|
332
|
-
fi
|
|
333
|
-
if [ "$BACKUP_EPOCH" -gt "$FILE_MTIME_CK" ]; then
|
|
334
|
-
dbg "Compact recovery available: BACKUP_TS=$BACKUP_TS"
|
|
335
|
-
COMPACT_NOTE="
|
|
336
|
-
--- PRE-COMPACT SNAPSHOT ($BACKUP_TS) ---
|
|
337
|
-
Context was compacted. This snapshot may contain decisions or progress not yet in working memory.
|
|
338
|
-
|
|
339
|
-
$BACKUP_MEMORY
|
|
340
|
-
"
|
|
341
|
-
if [ -n "$CONTEXT" ]; then
|
|
342
|
-
CONTEXT="${CONTEXT}
|
|
343
|
-
${COMPACT_NOTE}"
|
|
344
|
-
else
|
|
345
|
-
CONTEXT="$COMPACT_NOTE"
|
|
346
|
-
fi
|
|
347
|
-
fi
|
|
348
|
-
fi
|
|
349
|
-
fi
|
|
350
|
-
|
|
351
311
|
# --- One-time worker notice ---
|
|
352
312
|
# When the memory worker cannot refresh memory for a reason only the user can fix (a
|
|
353
313
|
# project path its permission rule cannot carry, the worker's write refused in two runs
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// {"version":1,"evidence":"required|standard","compliance":["gdpr",…],
|
|
17
17
|
// "tracker":{"provider":"github|jira|linear","site":"https://…","key":"ACME"},
|
|
18
18
|
// "reviewPublication":"off|auto|full",
|
|
19
|
-
// "features":{"memory":false,"learning":false,"knowledge":false}}
|
|
19
|
+
// "features":{"memory":false,"curation":false,"learning":false,"knowledge":false}}
|
|
20
20
|
// Each known key is classified on its own — absent, valid or malformed — so one
|
|
21
21
|
// bad value never takes its neighbours down with it (AC-26). What a malformed
|
|
22
22
|
// value MEANS is the consumer's decision, made once in each caller's fold:
|
|
@@ -81,8 +81,12 @@ const TRACKER_PROVIDER_IDS = Object.freeze(['github', 'jira', 'linear']);
|
|
|
81
81
|
*/
|
|
82
82
|
const COMPLIANCE_IDS = Object.freeze(['gdpr', 'hipaa', 'pci-dss', 'soc2', 'iso-27001', 'sox']);
|
|
83
83
|
|
|
84
|
-
/**
|
|
85
|
-
|
|
84
|
+
/**
|
|
85
|
+
* The feature keys a repo layer may narrow with. `curation` is the curation
|
|
86
|
+
* feature's current name and `learning` its pre-rename one (#529): both are read,
|
|
87
|
+
* each on its own, and a literal false in either turns the feature off.
|
|
88
|
+
*/
|
|
89
|
+
const FEATURE_SWITCHES = Object.freeze(['memory', 'curation', 'learning', 'knowledge']);
|
|
86
90
|
|
|
87
91
|
/**
|
|
88
92
|
* A tracker site: the Jira setup-task gate (src/assets/mds/tracker/_jira.mds,
|
|
@@ -124,7 +128,7 @@ const MAX_JSON_DEPTH = 32;
|
|
|
124
128
|
* @typedef {{ kind: 'absent' } | { kind: 'malformed' } | { kind: 'valid', value: T }} Field
|
|
125
129
|
*
|
|
126
130
|
* @typedef {{ provider: Field<string>, site: Field<string>, key: Field<string> }} ProjectTracker
|
|
127
|
-
* @typedef {{ memory: Field<boolean>, learning: Field<boolean>, knowledge: Field<boolean> }} FeatureFields
|
|
131
|
+
* @typedef {{ memory: Field<boolean>, curation: Field<boolean>, learning: Field<boolean>, knowledge: Field<boolean> }} FeatureFields
|
|
128
132
|
*
|
|
129
133
|
* @typedef {{ kind: 'absent' } | { kind: 'invalid' } | {
|
|
130
134
|
* kind: 'parsed',
|
|
@@ -515,7 +519,9 @@ function asKey(value) {
|
|
|
515
519
|
|
|
516
520
|
/**
|
|
517
521
|
* The `features` object: each switch its own field, boolean only. A consumer
|
|
518
|
-
* narrows on a literal `false` alone.
|
|
522
|
+
* narrows on a literal `false` alone. `curation` and `learning` are two keys of
|
|
523
|
+
* one feature, parsed independently: a duplicate or a non-boolean value in one
|
|
524
|
+
* leaves the other readable. The retired `decisions` key is never read here.
|
|
519
525
|
*
|
|
520
526
|
* @param {Record<string, unknown>} obj
|
|
521
527
|
* @param {Set<string>} duplicates
|
|
@@ -529,6 +535,7 @@ function featuresField(obj, duplicates) {
|
|
|
529
535
|
ok: true,
|
|
530
536
|
value: Object.freeze({
|
|
531
537
|
memory: fieldOf(value, duplicates, base, 'memory', asBoolean),
|
|
538
|
+
curation: fieldOf(value, duplicates, base, 'curation', asBoolean),
|
|
532
539
|
learning: fieldOf(value, duplicates, base, 'learning', asBoolean),
|
|
533
540
|
knowledge: fieldOf(value, duplicates, base, 'knowledge', asBoolean),
|
|
534
541
|
}),
|