devflow-kit 3.0.1 → 3.1.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 +13 -0
- package/dist/agents/git.md +2 -2
- package/dist/cli/agents-view/index.js +1 -1
- package/dist/cli/agents-view/render.js +2 -2
- package/dist/cli/agents-view/state.js +2 -2
- package/dist/cli/agents-view/terminal.js +5 -5
- package/dist/cli/commands/agents.js +7 -6
- package/dist/cli/commands/ambient.js +1 -1
- package/dist/cli/commands/attribution-prompts.js +8 -8
- package/dist/cli/commands/capture.js +1 -1
- package/dist/cli/commands/compliance-prompts.js +8 -8
- package/dist/cli/commands/compliance.js +8 -7
- package/dist/cli/commands/flags.js +33 -31
- package/dist/cli/commands/hud.js +1 -1
- package/dist/cli/commands/init-seed.js +9 -9
- package/dist/cli/commands/init.js +34 -32
- package/dist/cli/commands/install-report.js +10 -10
- package/dist/cli/commands/learning.js +267 -129
- package/dist/cli/commands/memory.js +1 -1
- package/dist/cli/commands/proxy.js +23 -23
- package/dist/cli/commands/rules.js +6 -5
- package/dist/cli/commands/tracker-prompts.js +6 -6
- package/dist/cli/commands/tracker.js +9 -9
- package/dist/cli/commands/uninstall.js +20 -20
- package/dist/cli/flags-view/render.js +5 -5
- package/dist/cli/flags-view/state.js +9 -9
- package/dist/cli/flags-view/terminal.js +4 -4
- package/dist/cli/tui/cells.js +1 -1
- package/dist/cli/tui/terminal.js +6 -6
- package/dist/commands/dynamic-build.md +18 -4
- package/dist/commands/dynamic-plan.md +19 -5
- package/dist/commands/dynamic-profile.md +17 -3
- package/dist/commands/dynamic-tickets.md +18 -4
- package/dist/commands/release.md +15 -1
- package/dist/commands/research.md +1 -1
- package/dist/commands/resolve.md +8 -9
- package/dist/core/agent-frontmatter.js +3 -3
- package/dist/core/agent-models.js +6 -6
- package/dist/core/agent-state.js +2 -2
- package/dist/core/ansi.js +2 -2
- package/dist/core/cache.js +7 -8
- package/dist/core/codex-auth-inspect.js +4 -4
- package/dist/core/compliance-compose.js +3 -3
- package/dist/core/compliance.js +3 -4
- package/dist/core/evidence-policy.js +14 -13
- package/dist/core/external-models.js +1 -1
- package/dist/core/feature-config.js +3 -3
- package/dist/core/feature-switch.js +3 -3
- package/dist/core/flags.js +25 -25
- package/dist/core/fs-atomic.js +6 -7
- package/dist/core/learning-queue-cleanup.js +16 -80
- package/dist/core/learning-store.js +61 -0
- package/dist/core/manifest.js +5 -5
- package/dist/core/mds-variants.js +13 -13
- package/dist/core/model-discovery.js +8 -8
- package/dist/core/observations.js +17 -101
- package/dist/core/orphan-sweep.js +4 -4
- package/dist/core/plugins.js +4 -5
- package/dist/core/project-paths.js +9 -13
- package/dist/core/proxy-log.js +8 -8
- package/dist/core/proxy-state.js +3 -3
- package/dist/core/reference-sweep.js +6 -6
- package/dist/core/teammate-mode-cleanup.js +1 -1
- package/dist/core/tracker.js +14 -14
- package/dist/hud/colors.js +2 -2
- package/dist/hud/components/learning-counts.js +2 -16
- package/dist/hud/components/version-badge.js +1 -1
- package/dist/skills/git/references/pr/resolve-review-threads.md +2 -2
- package/dist/targets/claude-code/compliance-install.js +17 -15
- package/dist/targets/claude-code/hooks.js +2 -2
- package/dist/targets/claude-code/installer.js +24 -24
- package/dist/targets/claude-code/legacy.js +1 -1
- package/dist/targets/claude-code/post-install.js +7 -7
- package/dist/targets/claude-code/tracker-install.js +2 -2
- package/package.json +1 -1
- package/src/assets/agents/code.md +1 -4
- package/src/assets/agents/design.md +2 -2
- package/src/assets/agents/diagnose.md +1 -1
- package/src/assets/agents/git.mds +2 -2
- package/src/assets/agents/knowledge.md +3 -3
- package/src/assets/agents/learning.md +281 -196
- package/src/assets/agents/research.md +1 -1
- package/src/assets/agents/review.md +3 -3
- package/src/assets/agents/scrutinize.md +1 -1
- package/src/assets/agents/skim.md +1 -1
- package/src/assets/agents/triage.md +9 -9
- package/src/assets/commands/_partials/_decisions.mds +8 -3
- package/src/assets/commands/_partials/_docs_root.mds +3 -3
- package/src/assets/commands/_partials/_engine.mds +1 -1
- package/src/assets/commands/_partials/_preamble.mds +6 -2
- package/src/assets/commands/_partials/_settings.mds +2 -2
- package/src/assets/commands/dynamic-build.mds +1 -1
- package/src/assets/commands/dynamic-plan.mds +3 -3
- package/src/assets/commands/dynamic-profile.mds +1 -1
- package/src/assets/commands/dynamic-tickets.mds +2 -2
- package/src/assets/commands/release.md +15 -1
- package/src/assets/commands/research.mds +1 -1
- package/src/assets/commands/resolve.mds +8 -9
- package/src/assets/mds/git/_pr.mds +3 -3
- package/src/assets/mds/tracker/_common.mds +1 -1
- package/src/assets/mds/tracker/_github.mds +1 -1
- package/src/assets/mds/tracker/_jira.mds +1 -1
- package/src/assets/mds/tracker/_linear.mds +1 -1
- package/src/assets/mds/tracker/_mcp.mds +6 -5
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +1 -0
- package/src/assets/scripts/hooks/background-memory-update +28 -22
- package/src/assets/scripts/hooks/capture-turn +1 -17
- package/src/assets/scripts/hooks/ensure-devflow-init +1 -1
- package/src/assets/scripts/hooks/ensure-proxy +5 -6
- package/src/assets/scripts/hooks/ensure-root-gitignore +1 -1
- package/src/assets/scripts/hooks/is-hex-sha +1 -1
- package/src/assets/scripts/hooks/json-helper.cjs +348 -814
- package/src/assets/scripts/hooks/json-parse +3 -2
- package/src/assets/scripts/hooks/lib/decisions-format.cjs +205 -156
- package/src/assets/scripts/hooks/lib/learning-store.cjs +3102 -0
- package/src/assets/scripts/hooks/lib/mkdir-lock.cjs +7 -5
- package/src/assets/scripts/hooks/lib/project-paths.cjs +13 -19
- package/src/assets/scripts/hooks/lib/render-decisions.cjs +253 -226
- package/src/assets/scripts/hooks/queue-append +2 -2
- package/src/assets/scripts/hooks/resolve-project-root +3 -4
- package/src/assets/scripts/hooks/session-start-context +40 -18
- package/src/assets/scripts/lib/project-config.cjs +2 -2
- package/src/assets/scripts/pr-evidence.cjs +3 -3
- package/src/assets/scripts/redact-secrets.cjs +20 -20
- package/src/assets/scripts/release-trace.cjs +1 -1
- package/src/assets/scripts/resolve-evidence-policy.cjs +3 -3
- package/src/assets/scripts/resolve-settings.cjs +3 -3
- package/src/assets/scripts/verify-evidence.cjs +2 -2
- package/src/assets/skills/apply-decisions/SKILL.md +37 -17
- package/src/assets/skills/docs-framework/SKILL.md +2 -2
- package/src/assets/skills/feature-knowledge/SKILL.md +6 -5
- package/dist/core/observation-io.js +0 -50
- package/src/assets/scripts/hooks/decisions-usage-scan.cjs +0 -131
|
@@ -65,7 +65,21 @@ The `budget` global governs depth. Scale Review agent roster and verification vo
|
|
|
65
65
|
|
|
66
66
|
### DECISIONS_CONTEXT — obtain BEFORE authoring
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
The decisions ledger belongs to the repository, not to one checkout: in a linked worktree it lives in the main worktree, and a session started in a subdirectory reads the copy at the repository root. Locate it with ONE git call, run from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git -C "{start}" rev-parse --path-format=absolute --show-toplevel --git-common-dir
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Line 1 is the checkout's toplevel, line 2 the repository's common git directory. A git older than 2.31 echoes `--path-format=absolute` back as a line of its own first. `{ledger}` is the first of these that applies:
|
|
75
|
+
|
|
76
|
+
1. **The main worktree** — line 2 without its trailing `/.git`, when the output is exactly two lines each beginning with `/`, line 2 ends in `/.git`, and the directory left once it is removed is not your home directory and contains a `.devflow/` directory.
|
|
77
|
+
2. **The toplevel** — line 1, or on an older git the line after the echoed flag.
|
|
78
|
+
3. **The start directory itself** — when the command failed or printed no absolute toplevel (outside a git repository).
|
|
79
|
+
|
|
80
|
+
This is the rule the learning hooks apply (D-LEDGER-MAIN-WORKTREE, D-PROMPT-ROOT), so you read the index the Learning agent writes.
|
|
81
|
+
|
|
82
|
+
Before you author the workflow script, read `{ledger}/.devflow/learning/index.md`. If the file is absent or empty, set `DECISIONS_CONTEXT` to `(none)`; otherwise use the file content as `DECISIONS_CONTEXT`.
|
|
69
83
|
|
|
70
84
|
The script body cannot perform this read — you (the main model) do it before authoring. Then inject the relevant DECISIONS_CONTEXT into agent prompts using the `devflow:apply-decisions` consumption algorithm (scan index → Read relevant entries → cite verbatim IDs in agent prompts). Only agents that need architectural context (Code agent, Evaluate agent, Review agent, Scrutinize agent) need DECISIONS_CONTEXT injected; lightweight agents (Validate agent, Simplify agent) do not.
|
|
71
85
|
|
|
@@ -73,7 +87,7 @@ The script body cannot perform this read — you (the main model) do it before a
|
|
|
73
87
|
|
|
74
88
|
When a ticket requires multiple sequential Code agent phases, each Code agent writes `{toplevel}/.devflow/docs/handoff-{branch_slug}.md` (branch-scoped to prevent concurrent session clobber), `{toplevel}` being `git rev-parse --show-toplevel` in the checkout the ticket's branch is in — never a subdirectory. The next Code agent reads it via HANDOFF_FILE input. PRIOR_PHASE_SUMMARY is the compact in-context form; the handoff file is the durable form that survives context compaction. Always read the handoff file directly — code is authoritative, summaries are supplementary.
|
|
75
89
|
|
|
76
|
-
### IRON RULE (
|
|
90
|
+
### IRON RULE (LLM-vs-plumbing)
|
|
77
91
|
|
|
78
92
|
**Author ZERO deterministic feature code.** No parsers, no schedulers, no topological-sort, no dependency-graph helpers, no confidence formulas. ALL issue reading, dependency reasoning, and scheduling decisions are LLM judgment at runtime, performed by the workflow's agents. The recipe is instructions. The workflow script Claude authors IS the runtime logic — keep it free of hand-coded feature algorithms.
|
|
79
93
|
|
|
@@ -680,7 +694,7 @@ Code(agentType:"Code", prompt: full task + plan + DECISIONS_CONTEXT + handoff if
|
|
|
680
694
|
→ gate2_acceptance() ← Gate 2 runs HERE — before the review pass, not after
|
|
681
695
|
```
|
|
682
696
|
|
|
683
|
-
The Code agent prompt must include: task description, implementation plan (if one exists), relevant DECISIONS_CONTEXT (
|
|
697
|
+
The Code agent prompt must include: task description, implementation plan (if one exists), relevant DECISIONS_CONTEXT (the index you loaded before authoring), the compliance lens (`COMPLIANCE_FRAMEWORKS` — every Code prompt carries it, fix prompts included), and any PRIOR_PHASE_SUMMARY / HANDOFF_FILE for sequential multi-phase tickets.
|
|
684
698
|
|
|
685
699
|
Gate 2 runs at implementation acceptance — this matches devflow's deliberate placement: "evaluation is part of implementation acceptance, not post-review" (§6.1).
|
|
686
700
|
|
|
@@ -1240,4 +1254,4 @@ Update the wave PR's test-plan block and post its evidence comment."
|
|
|
1240
1254
|
|
|
1241
1255
|
### Maintenance note
|
|
1242
1256
|
|
|
1243
|
-
This recipe encodes the current `/implement` + `/code-review` + `/resolve` orchestration shape as of the authoring date (2026-06-12). When those base commands change their orchestration, update this recipe to match. No tooling detects drift — by design (
|
|
1257
|
+
This recipe encodes the current `/implement` + `/code-review` + `/resolve` orchestration shape as of the authoring date (2026-06-12). When those base commands change their orchestration, update this recipe to match. No tooling detects drift — by design (the LLM-vs-plumbing Iron Rule). The reminder lives in the design doc §16.
|
|
@@ -65,7 +65,21 @@ The `budget` global governs depth. Scale Review agent roster and verification vo
|
|
|
65
65
|
|
|
66
66
|
### DECISIONS_CONTEXT — obtain BEFORE authoring
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
The decisions ledger belongs to the repository, not to one checkout: in a linked worktree it lives in the main worktree, and a session started in a subdirectory reads the copy at the repository root. Locate it with ONE git call, run from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git -C "{start}" rev-parse --path-format=absolute --show-toplevel --git-common-dir
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Line 1 is the checkout's toplevel, line 2 the repository's common git directory. A git older than 2.31 echoes `--path-format=absolute` back as a line of its own first. `{ledger}` is the first of these that applies:
|
|
75
|
+
|
|
76
|
+
1. **The main worktree** — line 2 without its trailing `/.git`, when the output is exactly two lines each beginning with `/`, line 2 ends in `/.git`, and the directory left once it is removed is not your home directory and contains a `.devflow/` directory.
|
|
77
|
+
2. **The toplevel** — line 1, or on an older git the line after the echoed flag.
|
|
78
|
+
3. **The start directory itself** — when the command failed or printed no absolute toplevel (outside a git repository).
|
|
79
|
+
|
|
80
|
+
This is the rule the learning hooks apply (D-LEDGER-MAIN-WORKTREE, D-PROMPT-ROOT), so you read the index the Learning agent writes.
|
|
81
|
+
|
|
82
|
+
Before you author the workflow script, read `{ledger}/.devflow/learning/index.md`. If the file is absent or empty, set `DECISIONS_CONTEXT` to `(none)`; otherwise use the file content as `DECISIONS_CONTEXT`.
|
|
69
83
|
|
|
70
84
|
The script body cannot perform this read — you (the main model) do it before authoring. Then inject the relevant DECISIONS_CONTEXT into agent prompts using the `devflow:apply-decisions` consumption algorithm (scan index → Read relevant entries → cite verbatim IDs in agent prompts). Only agents that need architectural context (Code agent, Evaluate agent, Review agent, Scrutinize agent) need DECISIONS_CONTEXT injected; lightweight agents (Validate agent, Simplify agent) do not.
|
|
71
85
|
|
|
@@ -73,7 +87,7 @@ The script body cannot perform this read — you (the main model) do it before a
|
|
|
73
87
|
|
|
74
88
|
When a ticket requires multiple sequential Code agent phases, each Code agent writes `{toplevel}/.devflow/docs/handoff-{branch_slug}.md` (branch-scoped to prevent concurrent session clobber), `{toplevel}` being `git rev-parse --show-toplevel` in the checkout the ticket's branch is in — never a subdirectory. The next Code agent reads it via HANDOFF_FILE input. PRIOR_PHASE_SUMMARY is the compact in-context form; the handoff file is the durable form that survives context compaction. Always read the handoff file directly — code is authoritative, summaries are supplementary.
|
|
75
89
|
|
|
76
|
-
### IRON RULE (
|
|
90
|
+
### IRON RULE (LLM-vs-plumbing)
|
|
77
91
|
|
|
78
92
|
**Author ZERO deterministic feature code.** No parsers, no schedulers, no topological-sort, no dependency-graph helpers, no confidence formulas. ALL issue reading, dependency reasoning, and scheduling decisions are LLM judgment at runtime, performed by the workflow's agents. The recipe is instructions. The workflow script Claude authors IS the runtime logic — keep it free of hand-coded feature algorithms.
|
|
79
93
|
|
|
@@ -233,7 +247,7 @@ const plans = await phase("plan-parallel", () =>
|
|
|
233
247
|
parallel((tickets || []).map(ticket => () =>
|
|
234
248
|
agent(`Write an implementation plan for this ticket.
|
|
235
249
|
Ticket: ${JSON.stringify(ticket)}
|
|
236
|
-
Decisions context (apply devflow:apply-decisions;
|
|
250
|
+
Decisions context (apply devflow:apply-decisions; a plan file can be posted to the tracker, so state each decision in words, never by ID): ${DECISIONS_CONTEXT}
|
|
237
251
|
The plan must cover: approach overview, affected files and modules, key design decisions, implementation sequence (what to build first), risks and mitigations, and any open questions you cannot resolve from the ticket alone.
|
|
238
252
|
Write a thorough but tight plan — every section must earn its place for a Code agent who has no other context.
|
|
239
253
|
Return: { ticketTitle, planMarkdown, openDecisions (array of genuine unknowns requiring user input) }.`, { agentType: "Design" })
|
|
@@ -324,7 +338,7 @@ For each ticket, write ${OUTDIR}/{ticket-slug}-plan.md containing:
|
|
|
324
338
|
- ## Acceptance Criteria (numbered, positive + negative)
|
|
325
339
|
- ## Test Plan — the challenger's testPlan lines, verbatim, one per line, and nothing else: no prose, no blank line between them, no setup or outcome
|
|
326
340
|
- ## Test Scenarios — one line per TP, in TP order: TP-n: its setup, then its expected outcome, from testScenarios
|
|
327
|
-
- ## Auto-Resolved Decisions (if any — list each as: decision → resolution → source)
|
|
341
|
+
- ## Auto-Resolved Decisions (if any — list each as: decision → resolution → source, naming a recorded decision in words, never by ID)
|
|
328
342
|
|
|
329
343
|
Then write ${OUTDIR}/DECISIONS-NEEDED.md:
|
|
330
344
|
- ## Auto-Resolved Decisions — list each silently-resolved decision as: decision → resolution → source (preference profile / ADR-NNN), so auto-resolution is auditable and reversible. If none, write "None."
|
|
@@ -430,4 +444,4 @@ After the workflow returns: check each plan's test plan (step 1 of the F4 list a
|
|
|
430
444
|
|
|
431
445
|
### Maintenance note
|
|
432
446
|
|
|
433
|
-
This recipe encodes the planning pipeline as of the authoring date (2026-06-12). The plan-challenge verbatim intent (§5.1) is load-bearing — do not paraphrase it when authoring the challenger agent prompt. The "Acceptance criteria + test plan contract" section above is the shared shape with `/devflow:dynamic-build` Gate 2; any change must be kept in sync. No tooling detects drift — by design (
|
|
447
|
+
This recipe encodes the planning pipeline as of the authoring date (2026-06-12). The plan-challenge verbatim intent (§5.1) is load-bearing — do not paraphrase it when authoring the challenger agent prompt. The "Acceptance criteria + test plan contract" section above is the shared shape with `/devflow:dynamic-build` Gate 2; any change must be kept in sync. No tooling detects drift — by design (the LLM-vs-plumbing Iron Rule).
|
|
@@ -65,7 +65,21 @@ The `budget` global governs depth. Scale Review agent roster and verification vo
|
|
|
65
65
|
|
|
66
66
|
### DECISIONS_CONTEXT — obtain BEFORE authoring
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
The decisions ledger belongs to the repository, not to one checkout: in a linked worktree it lives in the main worktree, and a session started in a subdirectory reads the copy at the repository root. Locate it with ONE git call, run from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git -C "{start}" rev-parse --path-format=absolute --show-toplevel --git-common-dir
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Line 1 is the checkout's toplevel, line 2 the repository's common git directory. A git older than 2.31 echoes `--path-format=absolute` back as a line of its own first. `{ledger}` is the first of these that applies:
|
|
75
|
+
|
|
76
|
+
1. **The main worktree** — line 2 without its trailing `/.git`, when the output is exactly two lines each beginning with `/`, line 2 ends in `/.git`, and the directory left once it is removed is not your home directory and contains a `.devflow/` directory.
|
|
77
|
+
2. **The toplevel** — line 1, or on an older git the line after the echoed flag.
|
|
78
|
+
3. **The start directory itself** — when the command failed or printed no absolute toplevel (outside a git repository).
|
|
79
|
+
|
|
80
|
+
This is the rule the learning hooks apply (D-LEDGER-MAIN-WORKTREE, D-PROMPT-ROOT), so you read the index the Learning agent writes.
|
|
81
|
+
|
|
82
|
+
Before you author the workflow script, read `{ledger}/.devflow/learning/index.md`. If the file is absent or empty, set `DECISIONS_CONTEXT` to `(none)`; otherwise use the file content as `DECISIONS_CONTEXT`.
|
|
69
83
|
|
|
70
84
|
The script body cannot perform this read — you (the main model) do it before authoring. Then inject the relevant DECISIONS_CONTEXT into agent prompts using the `devflow:apply-decisions` consumption algorithm (scan index → Read relevant entries → cite verbatim IDs in agent prompts). Only agents that need architectural context (Code agent, Evaluate agent, Review agent, Scrutinize agent) need DECISIONS_CONTEXT injected; lightweight agents (Validate agent, Simplify agent) do not.
|
|
71
85
|
|
|
@@ -73,7 +87,7 @@ The script body cannot perform this read — you (the main model) do it before a
|
|
|
73
87
|
|
|
74
88
|
When a ticket requires multiple sequential Code agent phases, each Code agent writes `{toplevel}/.devflow/docs/handoff-{branch_slug}.md` (branch-scoped to prevent concurrent session clobber), `{toplevel}` being `git rev-parse --show-toplevel` in the checkout the ticket's branch is in — never a subdirectory. The next Code agent reads it via HANDOFF_FILE input. PRIOR_PHASE_SUMMARY is the compact in-context form; the handoff file is the durable form that survives context compaction. Always read the handoff file directly — code is authoritative, summaries are supplementary.
|
|
75
89
|
|
|
76
|
-
### IRON RULE (
|
|
90
|
+
### IRON RULE (LLM-vs-plumbing)
|
|
77
91
|
|
|
78
92
|
**Author ZERO deterministic feature code.** No parsers, no schedulers, no topological-sort, no dependency-graph helpers, no confidence formulas. ALL issue reading, dependency reasoning, and scheduling decisions are LLM judgment at runtime, performed by the workflow's agents. The recipe is instructions. The workflow script Claude authors IS the runtime logic — keep it free of hand-coded feature algorithms.
|
|
79
93
|
|
|
@@ -217,4 +231,4 @@ Next steps:
|
|
|
217
231
|
|
|
218
232
|
### Maintenance note
|
|
219
233
|
|
|
220
|
-
This command mines ALL projects' history on this machine. The bounded-reading discipline (grep/rg + sample — never full-read) is mandatory and must be preserved in every revision. The agent writes prose; no extraction or clustering algorithm is authored here. Per
|
|
234
|
+
This command mines ALL projects' history on this machine. The bounded-reading discipline (grep/rg + sample — never full-read) is mandatory and must be preserved in every revision. The agent writes prose; no extraction or clustering algorithm is authored here. Per the LLM-vs-plumbing Iron Rule: the agent does the reading and summarizing — not a script we maintain.
|
|
@@ -65,7 +65,21 @@ The `budget` global governs depth. Scale Review agent roster and verification vo
|
|
|
65
65
|
|
|
66
66
|
### DECISIONS_CONTEXT — obtain BEFORE authoring
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
The decisions ledger belongs to the repository, not to one checkout: in a linked worktree it lives in the main worktree, and a session started in a subdirectory reads the copy at the repository root. Locate it with ONE git call, run from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git -C "{start}" rev-parse --path-format=absolute --show-toplevel --git-common-dir
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Line 1 is the checkout's toplevel, line 2 the repository's common git directory. A git older than 2.31 echoes `--path-format=absolute` back as a line of its own first. `{ledger}` is the first of these that applies:
|
|
75
|
+
|
|
76
|
+
1. **The main worktree** — line 2 without its trailing `/.git`, when the output is exactly two lines each beginning with `/`, line 2 ends in `/.git`, and the directory left once it is removed is not your home directory and contains a `.devflow/` directory.
|
|
77
|
+
2. **The toplevel** — line 1, or on an older git the line after the echoed flag.
|
|
78
|
+
3. **The start directory itself** — when the command failed or printed no absolute toplevel (outside a git repository).
|
|
79
|
+
|
|
80
|
+
This is the rule the learning hooks apply (D-LEDGER-MAIN-WORKTREE, D-PROMPT-ROOT), so you read the index the Learning agent writes.
|
|
81
|
+
|
|
82
|
+
Before you author the workflow script, read `{ledger}/.devflow/learning/index.md`. If the file is absent or empty, set `DECISIONS_CONTEXT` to `(none)`; otherwise use the file content as `DECISIONS_CONTEXT`.
|
|
69
83
|
|
|
70
84
|
The script body cannot perform this read — you (the main model) do it before authoring. Then inject the relevant DECISIONS_CONTEXT into agent prompts using the `devflow:apply-decisions` consumption algorithm (scan index → Read relevant entries → cite verbatim IDs in agent prompts). Only agents that need architectural context (Code agent, Evaluate agent, Review agent, Scrutinize agent) need DECISIONS_CONTEXT injected; lightweight agents (Validate agent, Simplify agent) do not.
|
|
71
85
|
|
|
@@ -73,7 +87,7 @@ The script body cannot perform this read — you (the main model) do it before a
|
|
|
73
87
|
|
|
74
88
|
When a ticket requires multiple sequential Code agent phases, each Code agent writes `{toplevel}/.devflow/docs/handoff-{branch_slug}.md` (branch-scoped to prevent concurrent session clobber), `{toplevel}` being `git rev-parse --show-toplevel` in the checkout the ticket's branch is in — never a subdirectory. The next Code agent reads it via HANDOFF_FILE input. PRIOR_PHASE_SUMMARY is the compact in-context form; the handoff file is the durable form that survives context compaction. Always read the handoff file directly — code is authoritative, summaries are supplementary.
|
|
75
89
|
|
|
76
|
-
### IRON RULE (
|
|
90
|
+
### IRON RULE (LLM-vs-plumbing)
|
|
77
91
|
|
|
78
92
|
**Author ZERO deterministic feature code.** No parsers, no schedulers, no topological-sort, no dependency-graph helpers, no confidence formulas. ALL issue reading, dependency reasoning, and scheduling decisions are LLM judgment at runtime, performed by the workflow's agents. The recipe is instructions. The workflow script Claude authors IS the runtime logic — keep it free of hand-coded feature algorithms.
|
|
79
93
|
|
|
@@ -210,7 +224,7 @@ const drafts = await phase("draft", () =>
|
|
|
210
224
|
agent(`Draft ticket for initiative: "${initiative}"
|
|
211
225
|
Ticket: ${JSON.stringify(c)}
|
|
212
226
|
Constraints: ${constraints}
|
|
213
|
-
Decisions context (apply devflow:apply-decisions;
|
|
227
|
+
Decisions context (apply devflow:apply-decisions; the ticket is filed to the tracker, so state each decision in words, never by ID): ${DECISIONS_CONTEXT}
|
|
214
228
|
Write the ticket body following the ticket_body_template structure (Wave/Depends-on header, Summary, Scope with In/Out + anti-features, Invariants, numbered Acceptance Criteria with at least one negative criterion, Open Questions).
|
|
215
229
|
Return a JSON object with: title (string), summary (string), wave (number), dependsOn (array), bodyMarkdown (string), openQuestions (array).`, { agentType: "Design" })
|
|
216
230
|
))
|
|
@@ -641,4 +655,4 @@ The tracking-issue path and any open questions are the primary handoff to `/devf
|
|
|
641
655
|
|
|
642
656
|
### Maintenance note
|
|
643
657
|
|
|
644
|
-
This recipe encodes the ticket-factory shape as of the authoring date (2026-06-12). The pipeline structure (`draft → [2-lens review] → revise → whole-set critic → amend → tracking-issue`) is the load-bearing invariant. Per
|
|
658
|
+
This recipe encodes the ticket-factory shape as of the authoring date (2026-06-12). The pipeline structure (`draft → [2-lens review] → revise → whole-set critic → amend → tracking-issue`) is the load-bearing invariant. Per the LLM-vs-plumbing Iron Rule, no deterministic ticket-parsing logic is added — ticket slates are proposed by the model and confirmed by the user. When the devflow agent roster changes, update the `agentType` values above. No tooling detects drift — by design, under the same rule.
|
package/dist/commands/release.md
CHANGED
|
@@ -48,7 +48,21 @@ Read `.release/RELEASE-FLOW.md`:
|
|
|
48
48
|
|
|
49
49
|
**Produces:** DECISIONS_CONTEXT, FEATURE_KNOWLEDGE
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
The decisions ledger belongs to the repository, not to one checkout: in a linked worktree it lives in the main worktree, and a session started in a subdirectory reads the copy at the repository root. Locate it with ONE git call, run from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`):
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
git -C "{start}" rev-parse --path-format=absolute --show-toplevel --git-common-dir
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Line 1 is the checkout's toplevel, line 2 the repository's common git directory. A git older than 2.31 echoes `--path-format=absolute` back as a line of its own first. `{ledger}` is the first of these that applies:
|
|
58
|
+
|
|
59
|
+
1. **The main worktree** — line 2 without its trailing `/.git`, when the output is exactly two lines each beginning with `/`, line 2 ends in `/.git`, and the directory left once it is removed is not your home directory and contains a `.devflow/` directory.
|
|
60
|
+
2. **The toplevel** — line 1, or on an older git the line after the echoed flag.
|
|
61
|
+
3. **The start directory itself** — when the command failed or printed no absolute toplevel (outside a git repository).
|
|
62
|
+
|
|
63
|
+
This is the rule the learning hooks apply (D-LEDGER-MAIN-WORKTREE, D-PROMPT-ROOT), so you read the index the Learning agent writes.
|
|
64
|
+
|
|
65
|
+
Read `{ledger}/.devflow/learning/index.md`. If the file is absent or empty, set `DECISIONS_CONTEXT` to `(none)`; otherwise use the file content as `DECISIONS_CONTEXT`.
|
|
52
66
|
|
|
53
67
|
Load feature knowledge: Attempt to read `.devflow/features/index.md` (the regenerable cache). If absent or empty, glob `.devflow/features/*/KNOWLEDGE.md` and read each file's YAML frontmatter (`name`, `description`, `directories`) as the relevance surface. Pick release-relevant KBs by matching their documented area against the release context. For each selected KB, read the full `KNOWLEDGE.md` — trust current code over KB content on any mismatch. Concatenate under slug headers and set `FEATURE_KNOWLEDGE` (or `(none)` if no KBs exist or none are relevant). No `index.json`, no subprocess, no `.cjs` script.
|
|
54
68
|
|
|
@@ -64,7 +64,7 @@ The index is one direct file read, written at render time by `render-decisions.c
|
|
|
64
64
|
|
|
65
65
|
When `DECISIONS_CONTEXT` is not `(none)`, follow `devflow:apply-decisions` to scan the index, identify plausibly-relevant entries, Read full entry bodies on demand, and cite verbatim IDs in downstream agent prompts and reasoning.
|
|
66
66
|
|
|
67
|
-
Use `DECISIONS_CONTEXT` locally when framing research — prior decisions and pitfalls suggest areas to investigate. Follow `devflow:apply-decisions` to Read full entry bodies on demand. Pass `DECISIONS_CONTEXT` to each Research agent in Phase 4 so
|
|
67
|
+
Use `DECISIONS_CONTEXT` locally when framing research — prior decisions and pitfalls suggest areas to investigate. Follow `devflow:apply-decisions` to Read full entry bodies on demand. Pass `DECISIONS_CONTEXT` to each Research agent in Phase 4 so their findings can account for relevant decisions.
|
|
68
68
|
|
|
69
69
|
### Load Feature Knowledge
|
|
70
70
|
|
package/dist/commands/resolve.md
CHANGED
|
@@ -266,14 +266,14 @@ Wait for Triage agent to complete before proceeding. Parse verdict ledger from T
|
|
|
266
266
|
- **ESCALATED**: Security issues requiring human escalation
|
|
267
267
|
- **FIX_NOW**: Valid issues assigned to Code agents (with risk tier: Standard | Careful)
|
|
268
268
|
- **FALSE_POSITIVE**: Issues the Review agent got wrong (with cited evidence)
|
|
269
|
-
- **BY_DESIGN**: Intentional code (with
|
|
269
|
+
- **BY_DESIGN**: Intentional code (with a recorded decision, stated in words, or a code doc citation)
|
|
270
270
|
- **FIX_SEPARATE**: Valid but out of blast-radius scope (must become manage-debt ticket)
|
|
271
271
|
- **TECH_DEBT**: Architectural overhaul only — LAST RESORT
|
|
272
272
|
- **DUPLICATE**: Collapsed duplicate issue — carries `duplicate_of: <primary-id>` referencing the non-DUPLICATE primary; inherits the primary's outcome
|
|
273
273
|
|
|
274
|
-
Collect
|
|
274
|
+
Collect every decision and pitfall the Triage agent's Reasoning columns state, in its words — the resolution summary is posted, so it never carries an ADR/PF ID.
|
|
275
275
|
|
|
276
|
-
**Triage agent completeness assertion
|
|
276
|
+
**Triage agent completeness assertion:** Verify the parsed ledger against ISSUES before proceeding:
|
|
277
277
|
1. Every issue `id` from ISSUES must appear in exactly one verdict bucket — none may vanish, none may appear in multiple buckets. DUPLICATE is a valid bucket; a valid DUPLICATE entry must name its `duplicate_of` primary (the `Duplicate Of` column of the ledger's DUPLICATE table) and that primary must be a non-DUPLICATE issue id. A missing `duplicate_of` or one that chains to another DUPLICATE is a **Triage agent failure** (retry-then-abort as below).
|
|
278
278
|
2. If the Triage agent output is empty, contains a skill re-entrancy guard string (e.g., contains `already running`), or is missing any issue IDs from ISSUES: treat as a **Triage agent failure**:
|
|
279
279
|
- Retry the Triage agent once with the same inputs.
|
|
@@ -487,7 +487,7 @@ Run this step only when `EVIDENCE_POLICY` is `required` and THREAD_MAP is non-em
|
|
|
487
487
|
|
|
488
488
|
Prepare THREAD_MAP with verdicts from triage/code agent results:
|
|
489
489
|
- For each `ext-{N}`: match to an issue verdict (FIXED, FALSE_POSITIVE, BY_DESIGN, ESCALATED) by `file:line` correlation
|
|
490
|
-
- If the matched issue has verdict DUPLICATE, use the **primary's** verdict and verification status for the reply — do not expose DUPLICATE to the thread author (
|
|
490
|
+
- If the matched issue has verdict DUPLICATE, use the **primary's** verdict and verification status for the reply — do not expose DUPLICATE to the thread author (caller-side mapping: the Git agent's verdict set has no DUPLICATE, so its contract stays unchanged)
|
|
491
491
|
- Include `commit_sha` from Code agent results for FIXED verdicts
|
|
492
492
|
- If `fork_no_push` is set: drop FIXED entries from THREAD_MAP — their commits never reached the PR — and record each as `DEGRADED` in `## Third-Party Threads`
|
|
493
493
|
- Unmatched threads: ESCALATED (human review)
|
|
@@ -793,8 +793,7 @@ Written in Phase 5 (Collect Results) to `{TARGET_DIR}/resolution-summary.md`:
|
|
|
793
793
|
|
|
794
794
|
## Decisions Citations
|
|
795
795
|
|
|
796
|
-
-
|
|
797
|
-
- avoids PF-{NNN} — {batch-id}, {issue-id}
|
|
796
|
+
- {decision applied or pitfall avoided, stated in words — never its ID} — {batch-id}, {issue-id}
|
|
798
797
|
|
|
799
798
|
(Omit section if no citations were made)
|
|
800
799
|
|
|
@@ -832,9 +831,9 @@ Final gate: PASS | FAILED after {n} attempts
|
|
|
832
831
|
| {description} | {file}:{line} | {why} |
|
|
833
832
|
|
|
834
833
|
## By Design
|
|
835
|
-
| Issue | File:Line | Rationale (
|
|
836
|
-
|
|
837
|
-
| {description} | {file}:{line} | {
|
|
834
|
+
| Issue | File:Line | Rationale (decision/doc) |
|
|
835
|
+
|-------|-----------|--------------------------|
|
|
836
|
+
| {description} | {file}:{line} | {the decision, in words, or code comment} |
|
|
838
837
|
|
|
839
838
|
## Fix Separately
|
|
840
839
|
| Issue | File:Line | Reason | Tracked |
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Pure module — zero I/O. All functions take content strings and return
|
|
5
5
|
* new content strings; callers own file reads and writes.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Pure core-layer module, no Claude Code adapter concerns.
|
|
8
|
+
* No process.exit(); all fallible paths return Result.
|
|
9
9
|
*
|
|
10
10
|
* Regex scoping guarantee: ALL operations are confined to the FIRST `---…---`
|
|
11
11
|
* block. Model/effort lines in the document body are never touched.
|
|
@@ -156,7 +156,7 @@ export function rewriteAgentFrontmatter(content, opts) {
|
|
|
156
156
|
if (currentEffort !== opts.effort) {
|
|
157
157
|
// Use a replacement function — NOT a string — so that $&, $`, $',
|
|
158
158
|
// and $1 in opts.effort are written verbatim rather than expanded as
|
|
159
|
-
// replacement patterns
|
|
159
|
+
// replacement patterns.
|
|
160
160
|
newBody = newBody.replace(EFFORT_RE, () => `effort: ${opts.effort}`);
|
|
161
161
|
}
|
|
162
162
|
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Agent model mapping engine — schema, persistence, and convergence for the
|
|
3
3
|
* per-agent model configuration feature.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Pure core-layer module, no Claude Code adapter concerns.
|
|
6
|
+
* All fallible operations return Result, no process.exit().
|
|
7
7
|
*
|
|
8
8
|
* Mapping file: ~/.devflow/agent-models.json
|
|
9
9
|
* { version: 1, agents: { [name]: { model?, effort? } } }
|
|
@@ -48,7 +48,7 @@ export const EFFORT_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
|
48
48
|
/**
|
|
49
49
|
* Membership set typed as ReadonlySet<string> so .has() accepts a plain
|
|
50
50
|
* string argument without a cast — TypeScript's Set<T>.has() requires T, and
|
|
51
|
-
* EFFORT_LEVELS[number] is a literal union, not string
|
|
51
|
+
* EFFORT_LEVELS[number] is a literal union, not string.
|
|
52
52
|
*/
|
|
53
53
|
const EFFORT_LEVELS_SET = new Set(EFFORT_LEVELS);
|
|
54
54
|
// ---------------------------------------------------------------------------
|
|
@@ -288,7 +288,7 @@ export async function readAgentMapping(devflowDir, opts) {
|
|
|
288
288
|
* A missing directory returns an empty set rather than throwing (ENOENT is
|
|
289
289
|
* not an error — the install dir may not exist on a fresh machine before
|
|
290
290
|
* `devflow init` runs). Any other OS error also returns an empty set
|
|
291
|
-
* (degrade-not-throw
|
|
291
|
+
* (degrade-not-throw) — a transient or misconfigured path must
|
|
292
292
|
* not prevent the TUI or --list from starting.
|
|
293
293
|
*
|
|
294
294
|
* @param installDir - Path to ~/.claude/agents/devflow (or equivalent).
|
|
@@ -411,7 +411,7 @@ async function readDirDefaults(dir) {
|
|
|
411
411
|
* keep rendering (`devflow agents --list`), so it warns instead. Staying silent
|
|
412
412
|
* is what makes the gap dangerous: resolveEffective returns an undefined model,
|
|
413
413
|
* reapplyAgentMapping buckets the agent 'unchanged', and disabling the proxy
|
|
414
|
-
* leaves an externally-pinned agent unreverted with nothing said
|
|
414
|
+
* leaves an externally-pinned agent unreverted with nothing said.
|
|
415
415
|
*
|
|
416
416
|
* @param dirs - Agent directories, most-preferred first. Injectable so tests can
|
|
417
417
|
* prove the precedence against a temp tree; all real callers use the default.
|
|
@@ -480,7 +480,7 @@ export async function reapplyAgentMapping(opts) {
|
|
|
480
480
|
// Guard: reject mapping keys that would read/write outside the install directory.
|
|
481
481
|
// A corrupted or adversarial agent-models.json could contain path-traversal keys
|
|
482
482
|
// such as '../../etc/passwd'; this check prevents any filesystem access beyond
|
|
483
|
-
// opts.installDir.
|
|
483
|
+
// opts.installDir. Degrade-not-throw: this emits a warning and skips gracefully.
|
|
484
484
|
if (!isContainedIn(opts.installDir, mdFileName(agentName))) {
|
|
485
485
|
localWarn(`reapplyAgentMapping: agent name "${agentName}" resolves outside the install ` +
|
|
486
486
|
`directory — skipped (containment guard)`);
|
package/dist/core/agent-state.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Agent installation-state classification.
|
|
3
3
|
*
|
|
4
4
|
* Single source of truth for the STATE column shared by `--list` and the TUI.
|
|
5
|
-
* Centralised here (core layer)
|
|
5
|
+
* Centralised here (core layer) so neither cli/commands nor
|
|
6
6
|
* cli/agents-view owns the vocabulary.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* Pure core-layer module, no CLI-adapter concerns.
|
|
9
9
|
*/
|
|
10
10
|
import { isDormantExternalModel } from './external-models.js';
|
|
11
11
|
// ---------------------------------------------------------------------------
|
package/dist/core/ansi.js
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* feature module (src/hud/). Re-exported verbatim from src/hud/colors.ts
|
|
7
7
|
* so all existing HUD call sites remain untouched.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* src/core/ = agent-neutral logic; ANSI primitives have no
|
|
10
10
|
* feature coupling and belong here, not in a feature module.
|
|
11
|
-
*
|
|
11
|
+
* One shared definition over per-consumer copies.
|
|
12
12
|
*/
|
|
13
13
|
const ESC = '\x1b[';
|
|
14
14
|
const RESET = `${ESC}0m`;
|
package/dist/core/cache.js
CHANGED
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
* path.join()'s normalization of ".." components. safeEntryPath() enforces
|
|
16
16
|
* containment and returns null on violation; all callers treat null as a miss.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* Core-layer module, no Claude Code adapter concerns.
|
|
19
|
+
* Entries are written via tmp→rename (writeFileAtomicExclusive), so a reader
|
|
20
|
+
* sees the old entry or the new one, never a missing or partial file.
|
|
20
21
|
*/
|
|
21
22
|
import * as fs from 'node:fs';
|
|
22
23
|
import { promises as fsAsync } from 'node:fs';
|
|
@@ -38,9 +39,9 @@ export const MAX_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
|
38
39
|
// All callers that write or read the model-discovery catalog AND the uninstall
|
|
39
40
|
// removal target derive their directory paths from these functions. Keeping
|
|
40
41
|
// write-site and removal-site in the same module prevents silent orphaning of
|
|
41
|
-
// cache data on future relocations
|
|
42
|
+
// cache data on future relocations.
|
|
42
43
|
//
|
|
43
|
-
//
|
|
44
|
+
// Path layout owned by the core module, not scattered across
|
|
44
45
|
// callers in src/cli/ or src/hud/.
|
|
45
46
|
/**
|
|
46
47
|
* Returns the model-discovery cache directory for the given devflowDir.
|
|
@@ -137,8 +138,6 @@ export function readCache(cacheDir, key, validate) {
|
|
|
137
138
|
* The single canonical envelope parser — used by readCacheEntry (which adds
|
|
138
139
|
* the expiry check on top) and exported for model-discovery.ts (stale-fallback
|
|
139
140
|
* and prune sorters) so all callers share one parser and cannot drift.
|
|
140
|
-
*
|
|
141
|
-
* applies ADR-003: eliminated private parseEnvelope duplicate; one parser, one truth.
|
|
142
141
|
*/
|
|
143
142
|
export function parseRawEnvelope(raw) {
|
|
144
143
|
let parsed;
|
|
@@ -166,7 +165,7 @@ export function parseRawEnvelope(raw) {
|
|
|
166
165
|
* Write a value to cache with a TTL in milliseconds.
|
|
167
166
|
*
|
|
168
167
|
* - Creates cacheDir at mode 0700 if absent (owner-only access).
|
|
169
|
-
* - Writes the entry via atomic tmp→rename (
|
|
168
|
+
* - Writes the entry via atomic tmp→rename (no delete-then-write window).
|
|
170
169
|
* - Hardens the entry to 0600 after the write (owner-only read/write for cache data
|
|
171
170
|
* that will feed agent frontmatter in later phases).
|
|
172
171
|
* - TTL is clamped to MAX_TTL_MS before storage.
|
|
@@ -196,7 +195,7 @@ export async function writeCache(cacheDir, key, data, ttlMs) {
|
|
|
196
195
|
}
|
|
197
196
|
// Harden entry to 0600 after the atomic write. writeFileAtomicExclusive
|
|
198
197
|
// preserves the existing mode on re-writes; this chmod bootstraps 0600 on
|
|
199
|
-
// the first write to a fresh entry. Best-effort, non-fatal
|
|
198
|
+
// the first write to a fresh entry. Best-effort, non-fatal.
|
|
200
199
|
try {
|
|
201
200
|
await fsAsync.chmod(filePath, 0o600);
|
|
202
201
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Codex credential-file inspection for `devflow proxy --status`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Pure core-layer module — no Claude Code adapter concerns and
|
|
5
|
+
* no presentation (colour/format lives with the other CLI formatters).
|
|
6
6
|
*
|
|
7
7
|
* WHY THIS EXISTS RATHER THAN IMPORTING THE ROUTING RUNTIME
|
|
8
8
|
* The routing runtime validates this same file and exposes an equivalent
|
|
@@ -62,8 +62,8 @@ function jwtAccountId(token) {
|
|
|
62
62
|
/**
|
|
63
63
|
* Classify an I/O error from reading ~/.codex/auth.json into a CodexAuthState.
|
|
64
64
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
65
|
+
* Pure classification logic belongs in src/core/, not in the
|
|
66
|
+
* CLI presentation layer that calls it.
|
|
67
67
|
*
|
|
68
68
|
* ENOENT is the ordinary "not signed in" case: the file has never been written
|
|
69
69
|
* by `codex login`. Every other error (EACCES, EISDIR, EMFILE, …) is a real
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* fragment files, so installed artifacts differ by selection rather than being
|
|
6
6
|
* static all-six blobs.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Pure helpers in src/core/, no I/O.
|
|
9
|
+
* Warn-not-throw for per-item failures.
|
|
10
10
|
*/
|
|
11
11
|
import { COMPLIANCE_FRAMEWORKS, stampComplianceRule } from './compliance.js';
|
|
12
12
|
// ── Constants ──────────────────────────────────────────────────────────────────
|
|
@@ -278,7 +278,7 @@ function buildReferences(activeFrameworks, fragments) {
|
|
|
278
278
|
* Strict boundary parser: validates all required sections (## Mapping / ##
|
|
279
279
|
* Reference / ## Checklist / ## Rule) and their structural constraints.
|
|
280
280
|
* CRLF-tolerant: Windows line endings are normalised before parsing.
|
|
281
|
-
* Never throws
|
|
281
|
+
* Never throws: all errors return { ok: false, error }.
|
|
282
282
|
*/
|
|
283
283
|
export function parseComplianceFragment(id, raw) {
|
|
284
284
|
// CRLF tolerance
|
package/dist/core/compliance.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Core compliance framework registry and utilities.
|
|
3
3
|
*
|
|
4
4
|
* Pure module — no I/O, no side effects.
|
|
5
|
-
*
|
|
5
|
+
* Pure helpers in src/core/, I/O orchestration in src/targets/.
|
|
6
6
|
*/
|
|
7
7
|
/**
|
|
8
8
|
* Canonical compliance framework registry.
|
|
@@ -26,7 +26,7 @@ export const COMPLIANCE_RULE_PLACEHOLDER = '${DEVFLOW_COMPLIANCE_FRAMEWORKS}';
|
|
|
26
26
|
* detection.md — generic detection heuristics
|
|
27
27
|
* sources.md — authoritative source index
|
|
28
28
|
*
|
|
29
|
-
* Exported (
|
|
29
|
+
* Exported (pure constant in src/core/) so both compliance-install.ts
|
|
30
30
|
* (install) and compliance.ts CLI (status/drift detection) share a single
|
|
31
31
|
* definition. Adding a third always-present ref requires only one change here.
|
|
32
32
|
*/
|
|
@@ -60,7 +60,7 @@ function normalizeId(s) {
|
|
|
60
60
|
* This is the trust boundary for framework IDs that did not come through
|
|
61
61
|
* `parseFrameworkList` — most importantly `manifest.features.compliance.frameworks`,
|
|
62
62
|
* which `normalizeComplianceFeature` only type-checks (it cannot reject unknown IDs
|
|
63
|
-
* without violating the
|
|
63
|
+
* without violating the self-heal contract). Every framework ID that is about
|
|
64
64
|
* to become an fs path segment or be written into an installed artifact must pass
|
|
65
65
|
* through here first (AC-35, AC-36).
|
|
66
66
|
*
|
|
@@ -111,7 +111,6 @@ export function parseFrameworkList(input) {
|
|
|
111
111
|
*
|
|
112
112
|
* Absent, null, malformed, or partially-valid → {enabled:false, frameworks:[]}.
|
|
113
113
|
* Preserves valid {enabled: boolean, frameworks: string[]}.
|
|
114
|
-
* (Applies ADR-014 self-heal idiom)
|
|
115
114
|
*/
|
|
116
115
|
export function normalizeComplianceFeature(raw) {
|
|
117
116
|
const DEFAULT = { enabled: false, frameworks: [] };
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* `resolve-evidence-policy.cjs`, never a second implementation of it.
|
|
4
4
|
*
|
|
5
5
|
* D-POLICY-CJS-SEAM: the resolver is plain CommonJS under src/assets/scripts/,
|
|
6
|
-
* outside every tsconfig
|
|
6
|
+
* outside every tsconfig, so the interfaces below are
|
|
7
7
|
* TRANSCRIBED from its JSDoc typedefs and are the only shape authority on this
|
|
8
8
|
* side — open those typedefs before changing anything here. The module is loaded
|
|
9
9
|
* with `require()` from `scriptsDir()`, which resolves under the package root both
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* `lib/project-config.cjs` (loadProjectConfigLib), so the CLI judges a config
|
|
21
21
|
* file's bytes exactly as the resolvers do.
|
|
22
22
|
*
|
|
23
|
-
* D-POLICY-NO-WRITE
|
|
23
|
+
* D-POLICY-NO-WRITE: `.devflow/project.json` is team-owned, and
|
|
24
24
|
* devflow never writes or replaces a shared file it cannot prove it wrote. This
|
|
25
25
|
* module therefore imports no fs API; the CLI only PRINTS the bytes a team may
|
|
26
26
|
* choose to commit (`evidencePolicySuggestion`, and the migration lines of
|
|
@@ -101,23 +101,24 @@ function surfaceMismatches(value, surface) {
|
|
|
101
101
|
* require() one package script and shape-check it against `surface`. Never
|
|
102
102
|
* throws: a missing file is `not-found`; a module that throws on load or lacks a
|
|
103
103
|
* surface key is `unusable`. The caller's type parameter is justified by the
|
|
104
|
-
* surface check, which `satisfies` ties to the interface's keys.
|
|
104
|
+
* surface check, which `satisfies` ties to the interface's keys. Exported for
|
|
105
|
+
* src/core/learning-store.ts, which loads the learning store the same way.
|
|
105
106
|
*/
|
|
106
|
-
function loadScript(
|
|
107
|
+
export function loadScript(scriptPath, surface) {
|
|
107
108
|
let loaded;
|
|
108
109
|
try {
|
|
109
|
-
loaded = createRequire(import.meta.url)(
|
|
110
|
+
loaded = createRequire(import.meta.url)(scriptPath);
|
|
110
111
|
}
|
|
111
112
|
catch (err) {
|
|
112
113
|
const code = err.code;
|
|
113
114
|
if (code === 'MODULE_NOT_FOUND')
|
|
114
|
-
return { ok: false, error: { kind: 'not-found', path:
|
|
115
|
+
return { ok: false, error: { kind: 'not-found', path: scriptPath } };
|
|
115
116
|
const detail = err instanceof Error ? err.message : String(err);
|
|
116
|
-
return { ok: false, error: { kind: 'unusable', path:
|
|
117
|
+
return { ok: false, error: { kind: 'unusable', path: scriptPath, detail } };
|
|
117
118
|
}
|
|
118
119
|
const mismatches = surfaceMismatches(loaded, surface);
|
|
119
120
|
if (mismatches.length > 0) {
|
|
120
|
-
return { ok: false, error: { kind: 'unusable', path:
|
|
121
|
+
return { ok: false, error: { kind: 'unusable', path: scriptPath, detail: `missing or mistyped: ${mismatches.join(', ')}` } };
|
|
121
122
|
}
|
|
122
123
|
return { ok: true, value: loaded };
|
|
123
124
|
}
|
|
@@ -167,10 +168,10 @@ export function formatEvidencePolicyUnavailable(error) {
|
|
|
167
168
|
/**
|
|
168
169
|
* The `compliance --status` line: the resolved policy for `opts.dir`, or the
|
|
169
170
|
* unavailable line when the loader failed — that line is the whole handling
|
|
170
|
-
* (
|
|
171
|
-
* manifest is never read twice. `resolve()` makes
|
|
172
|
-
* bounds every subprocess with a timeout, so an
|
|
173
|
-
* flagged result rather than a hang.
|
|
171
|
+
* (only a damaged package fails the load). The caller passes the compliance
|
|
172
|
+
* state it already read, so the manifest is never read twice. `resolve()` makes
|
|
173
|
+
* at most three `gh` calls and bounds every subprocess with a timeout, so an
|
|
174
|
+
* offline machine degrades to a flagged result rather than a hang.
|
|
174
175
|
*/
|
|
175
176
|
export function evidencePolicyStatusLine(loaded, opts) {
|
|
176
177
|
if (!loaded.ok)
|
|
@@ -198,7 +199,7 @@ function suggestedFrameworks(complianceState) {
|
|
|
198
199
|
* count); `null` otherwise. The bytes come
|
|
199
200
|
* from the settings resolver's `serializeProjectSuggestion`, which returns them
|
|
200
201
|
* only when they read back through the shared parser as exactly what was asked.
|
|
201
|
-
* Nothing is written (D-POLICY-NO-WRITE
|
|
202
|
+
* Nothing is written (D-POLICY-NO-WRITE).
|
|
202
203
|
*/
|
|
203
204
|
export function evidencePolicySuggestion(complianceState, policy, settings) {
|
|
204
205
|
if (policy.complianceDefault(complianceState) !== 'required')
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* in src/core/model-discovery.ts. The TUI picker and --set validation use the
|
|
10
10
|
* ExternalModelCatalog returned by those functions.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* Pure core-layer module, no Claude Code adapter concerns.
|
|
13
13
|
*
|
|
14
14
|
* NOTE: the internal routing runtime package name must NEVER appear in
|
|
15
15
|
* user-visible strings, CLI output, or error messages. User-facing vocabulary:
|