devflow-kit 3.6.0 → 3.7.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 +80 -1
- package/README.md +6 -2
- package/dist/agents/code.md +7 -11
- package/dist/agents/fix.md +5 -6
- package/dist/agents/refactor.md +5 -6
- package/dist/agents/review.md +2 -0
- package/dist/cli/commands/gates.js +276 -0
- package/dist/cli/commands/init-seed.js +9 -1
- package/dist/cli/commands/init.js +31 -8
- package/dist/cli/commands/memory.js +18 -0
- package/dist/cli.js +2 -0
- package/dist/commands/bug-analysis.md +2 -2
- package/dist/commands/code-review.md +52 -44
- package/dist/commands/debug.md +2 -2
- package/dist/commands/dynamic-build.md +53 -61
- package/dist/commands/dynamic-plan.md +2 -2
- package/dist/commands/dynamic-profile.md +2 -2
- package/dist/commands/dynamic-tickets.md +2 -2
- package/dist/commands/explore.md +2 -2
- package/dist/commands/implement.md +26 -31
- package/dist/commands/plan.md +15 -25
- package/dist/commands/release.md +2 -2
- package/dist/commands/research.md +2 -2
- package/dist/commands/resolve.md +2 -2
- package/dist/commands/self-review.md +2 -2
- package/dist/core/echo-value.js +42 -0
- package/dist/core/evidence-policy.js +17 -5
- package/dist/core/gate-level.js +213 -0
- package/dist/core/manifest.js +26 -10
- package/dist/core/model-discovery.js +7 -1
- package/dist/core/tracker.js +5 -33
- package/dist/learning-off/agents/code.md +7 -11
- package/dist/learning-off/agents/fix.md +5 -6
- package/dist/learning-off/agents/refactor.md +5 -6
- package/dist/learning-off/agents/review.md +2 -0
- package/dist/learning-off/commands/code-review.md +52 -44
- package/dist/learning-off/commands/debug.md +2 -2
- package/dist/learning-off/commands/dynamic-build.md +53 -61
- package/dist/learning-off/commands/explore.md +2 -2
- package/dist/learning-off/commands/implement.md +26 -31
- package/dist/learning-off/commands/plan.md +15 -25
- package/dist/learning-off/commands/resolve.md +2 -2
- package/dist/learning-off/commands/self-review.md +2 -2
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +7 -9
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +7 -15
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +7 -15
- package/dist/targets/claude-code/learning-install.js +15 -3
- package/package.json +1 -1
- package/src/assets/agents/_partials/_code.mds +5 -6
- package/src/assets/agents/code.mds +2 -5
- package/src/assets/agents/curate.md +4 -6
- package/src/assets/agents/review.mds +2 -0
- package/src/assets/agents/synthesize.md +2 -2
- package/src/assets/commands/_partials/_settings.mds +2 -2
- package/src/assets/commands/_partials/_wave.mds +10 -17
- package/src/assets/commands/code-review.mds +50 -42
- package/src/assets/commands/dynamic-build.mds +41 -42
- package/src/assets/commands/implement.mds +24 -29
- package/src/assets/commands/plan.mds +14 -24
- package/src/assets/mds/git/_ops_steps.mds +3 -3
- package/src/assets/mds/tracker/_common.mds +23 -12
- package/src/assets/mds/tracker/_github.mds +2 -9
- package/src/assets/mds/tracker/_jira.mds +2 -15
- package/src/assets/mds/tracker/_linear.mds +2 -15
- package/src/assets/scripts/ci-wait.cjs +22 -4
- package/src/assets/scripts/claude-md-audit.cjs +66 -51
- package/src/assets/scripts/hooks/background-memory-update +404 -95
- package/src/assets/scripts/hooks/json-helper.cjs +11 -0
- package/src/assets/scripts/hooks/json-parse +18 -3
- package/src/assets/scripts/hooks/memory-permission +358 -0
- package/src/assets/scripts/hooks/session-start-context +2 -2
- package/src/assets/scripts/hooks/session-start-memory +26 -5
- package/src/assets/scripts/lib/project-config.cjs +19 -4
- package/src/assets/scripts/resolve-settings.cjs +200 -32
- package/src/assets/skills/agent-references/SKILL.md +6 -2
- package/src/assets/skills/agent-references/references/commands/code-review-levels.md +19 -0
- package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +2 -2
- package/src/assets/skills/agent-references/references/commands/plan-levels.md +40 -0
- package/src/assets/skills/agent-references/references/synthesize/planning.md +9 -22
- package/src/assets/skills/agent-references/references/synthesize/review.md +11 -11
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,84 @@ All notable changes to Devflow will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.7.0] - 2026-10-16
|
|
9
|
+
|
|
10
|
+
### Upgrade notes — Gate level
|
|
11
|
+
|
|
12
|
+
The settings line has a new last field for the gate level ([#472](https://github.com/dean0x/devflow/issues/472)).
|
|
13
|
+
Do the step below after you update.
|
|
14
|
+
|
|
15
|
+
**What you must do:**
|
|
16
|
+
- Run `devflow init` after you update. A full init installs the commands that accept the new settings line.
|
|
17
|
+
- After `devflow init`, a user with no level gets `standard` in `/code-review`: no TypeScript focus, and no Dependencies focus on a code diff that changes no dependency manifest. Run `devflow gates --set full` to keep today's set.
|
|
18
|
+
- A user with no level also gets the lean `/plan`: fewer agents, while the security gap analysis and the design review still run. Run `devflow gates --set full` to keep the full `/plan`.
|
|
19
|
+
|
|
20
|
+
**Stale-install window:**
|
|
21
|
+
- The settings line now ends with `GATE_LEVEL=<full|standard|lean>`. An installed command from an
|
|
22
|
+
older version accepts only the old fields, so it does not accept the longer line.
|
|
23
|
+
- `devflow init --hud-only` and a plugin-only update refresh the resolver script. They do not
|
|
24
|
+
refresh the installed prompts.
|
|
25
|
+
- Until you run a full `devflow init`, every installed command uses its fail-closed settings value.
|
|
26
|
+
- Until you run `devflow init`, the installed `/code-review` and `/plan` ignore the level.
|
|
27
|
+
|
|
28
|
+
**Known limits:**
|
|
29
|
+
- A corrupt manifest, or one over 1 MiB, reads as the `standard` level. `devflow gates --status`
|
|
30
|
+
warns that `devflow init` will replace the file.
|
|
31
|
+
- `devflow init` still overwrites a corrupt or rejected manifest. The stored level is lost with it.
|
|
32
|
+
- Two commands that write the manifest at the same moment can lose one update.
|
|
33
|
+
- There is no team floor. A repository that declares compliance can run a lower personal level.
|
|
34
|
+
`/code-review` and `/plan` read the level, so a personal `lean` level narrows the review and the
|
|
35
|
+
planning run even where compliance is declared. The Compliance focus still runs on its trigger,
|
|
36
|
+
and in `/plan` the security gap focus and the design review run at every level.
|
|
37
|
+
Issue [#488](https://github.com/dean0x/devflow/issues/488) (the compliance lens as a floor on
|
|
38
|
+
the level) is open.
|
|
39
|
+
|
|
40
|
+
### Upgrade notes — Working memory
|
|
41
|
+
|
|
42
|
+
The memory worker's write is limited to its staged file. Read the items below after you update.
|
|
43
|
+
|
|
44
|
+
**What you must do:**
|
|
45
|
+
- Update Claude Code to 2.1.269 or newer. On an older version, working memory does not update and Claude Code shows no message. The worker log has one line that says why.
|
|
46
|
+
- If a worker `env` value lives only in your project or local Claude Code settings, move it to your user settings file. The worker loads only the user settings.
|
|
47
|
+
|
|
48
|
+
**Your own settings:**
|
|
49
|
+
- Allow rules in your own Claude Code settings also apply to the worker's run. A user-level Edit allow rule that covers other paths covers the worker too.
|
|
50
|
+
|
|
51
|
+
**Known limits:**
|
|
52
|
+
- A project path that contains any of `* ? [ ] \ " { }` or a control character gets no working-memory updates. The permission rule carries the path without quoting. The worker skips the project, logs the reason and shows one message at session start. Parentheses and spaces in the path are carried as they are.
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
|
|
56
|
+
- **A gate level on the settings line, and the `devflow gates` command** ([#472](https://github.com/dean0x/devflow/issues/472)). The settings line ends with `GATE_LEVEL=<full|standard|lean>`. The level comes from a personal `gateLevel` key in `.devflow/config.json`, then from the machine default `features.gateLevel` in `~/.devflow/manifest.json`, then from `standard`. The personal key may raise or lower the level. It is not a narrow-only switch, and a later change can make it narrow-only. A `gateLevel` in `project.json` is ignored, and no command writes the personal key. A malformed value reads as `full`. `devflow gates --set <level>` writes the machine default and nothing else. `devflow gates --status` shows the level in effect, the layer that decided it and the machine default. `/code-review` reads the level ([#474](https://github.com/dean0x/devflow/issues/474)) and so does `/plan` ([#473](https://github.com/dean0x/devflow/issues/473)); [#480](https://github.com/dean0x/devflow/issues/480) adds more readers.
|
|
57
|
+
- **`devflow init --hud-only` prints a line when the installed prompts come from another devflow version** ([#472](https://github.com/dean0x/devflow/issues/472)). It tells you to run `devflow init`.
|
|
58
|
+
- **A message at session start when working memory stops for a cause you can fix.** If the worker cannot write its staged file at the project path (for example, a path with a character the permission rule cannot carry), or Claude Code refuses the worker's write in two runs with no successful update between them, the next session start shows one message that points to the log. Each cause is shown once.
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- **`/code-review` picks its focus set by gate level** ([#474](https://github.com/dean0x/devflow/issues/474)). `full` runs every focus, as before. `standard`, the default, drops TypeScript, and drops Dependencies on a code diff that changes no dependency manifest (a lockfile alone does not count). `lean` runs Security, Testing and Reliability on a code diff. The Compliance focus keeps its own trigger at every level. A docs-only, tests-only or lockfile-only diff keeps its reduced set, and at `lean` a tests-only diff drops Consistency. A mixed non-code diff runs the union of its classes' sets. Security runs at every level and in every class. `/code-review` reads a new level reference only at `standard` or `lean`. If it cannot read the reference, it says so and runs the full sets. The review summary now shows the gate level and the focuses, and a review with no security report gives CHANGES REQUESTED.
|
|
63
|
+
- **`/plan` runs a lean shape at `standard` and `lean`** ([#473](https://github.com/dean0x/devflow/issues/473)). `full` runs the phases as before. `standard`, the default, and `lean` run one Explore round of two agents (three for several issues), or none when a `/research` report from the same conversation covers the scope; no Synthesize step; two gap-analysis agents, one for security and one for completeness, architecture and performance (plus consistency and dependencies for several issues); one Plan agent; and the design review. The security gap agent and the design review run at every level, the Compliance agent keeps its own trigger, and Gates 0, 1 and 2 run at every level. Every agent spawn in `/plan` names its agent type; the planning spawns use Claude Code's built-in `Plan` agent. If `/plan` cannot read its level reference, it says so and runs the full shape. The run report names the gate level. Run `devflow gates --set full` to keep the full shape.
|
|
64
|
+
- **`/implement` splits long plans into sequential Code phases; a run with no plan stays single** ([#452](https://github.com/dean0x/devflow/issues/452)).
|
|
65
|
+
- **Code agents read large files once, by range, and cap printed output** ([#452](https://github.com/dean0x/devflow/issues/452)).
|
|
66
|
+
- **Polish, Harden, Evaluate and Test get the full branch diff as `FILES_CHANGED`** ([#452](https://github.com/dean0x/devflow/issues/452)).
|
|
67
|
+
- **`/implement` stops when the base branch name has a character outside ASCII letters, digits and `._/+@,#-`, or is longer than 200** ([#452](https://github.com/dean0x/devflow/issues/452)). The check also covers the re-validation path. Git accepts some refused names, such as `café`. For them, `/implement` stops and tells the user which characters are allowed.
|
|
68
|
+
- **The token audit counts files written through Bash** ([#442](https://github.com/dean0x/devflow/issues/442)).
|
|
69
|
+
- **The memory worker's write is limited to its staged file.** The `claude -p` run loads only your own Claude Code settings plus two permission rules, an allow rule for `.devflow/memory/WORKING-MEMORY.md.new` and a deny rule for every path under it, and a permission mode that refuses every call that no allow rule covers. Allow rules in your own Claude Code settings also apply to the run, so a user-level Edit allow rule that covers other paths covers the worker too. A folder or a link at the staged path is removed by one guarded delete of exactly that path; when it cannot be removed, the run fails and removes nothing. The worker ignores a project's or a local Claude Code settings file.
|
|
70
|
+
- **Working memory needs Claude Code 2.1.269 or newer.** `devflow init` states the version at its working-memory step. On an older Claude Code the worker does not run, keeps the queue and writes one log line that says why, so working memory does not update until you update Claude Code.
|
|
71
|
+
- **The memory log gets one `Run:` line for each worker run.** `.background-memory-update.log` under `~/.devflow/logs/`, in the folder for the session that ran the worker (`<session-directory-slug>`), ends every run with `Run: cli=<version|unknown> argv=<lean|legacy|none> result=<updated|conflict|failed|skip|below-floor> denials=<count|unknown|n/a> removed=<none|dir|link>`. `cli=unknown` means the worker read no version, `denials=unknown` means the result reported no count, and `denials=n/a` means no run started. A below-floor line adds `floor=<version>`. The line holds keywords and digits only, never turn or prompt text.
|
|
72
|
+
- **A project inside a `.git` folder gets no working-memory update.** The worker skips it, logs the reason and shows the path message once at session start. A checkout under `.claude/worktrees` is updated like any other checkout.
|
|
73
|
+
- **PR links and `/dynamic-build` ticket branches follow stricter rules** ([#486](https://github.com/dean0x/devflow/issues/486)). `/implement` prints `TRACEABILITY: DEGRADED (ambiguous issue reference)` when it has an issue number and no link line. `/code-review` and `/bug-analysis` PRs no longer get a closing line from the branch name. A PR body has no `## Related Issues` heading when neither an issue number nor a link line is given. In a `/dynamic-build` wave, the ticket branch must be a local branch name that passes the allowlist. A name with a leading `#` or `-`, a revision name such as `HEAD`, a tag and a SHA are all refused, and the ticket is quarantined. The wave aborts the merge only when the merge began. On Jira and Linear, the PR link line always uses the default reference form (`Refs {KEY}-{n}`). A custom `## Reference Rendering` section no longer shapes that line. It still shapes the release Shipped Issues section. Follow-up [#511](https://github.com/dean0x/devflow/issues/511) tracks applying it to the PR link line.
|
|
74
|
+
|
|
75
|
+
### Fixed
|
|
76
|
+
|
|
77
|
+
- **Prompts resolve the Claude config directory with the same rule as the installer** ([#517](https://github.com/dean0x/devflow/pull/517)). `CLAUDE_CONFIG_DIR` applies when it is an absolute path, else `~/.claude`.
|
|
78
|
+
- **A tracked `.devflow/config.json` no longer applies as personal config** ([#489](https://github.com/dean0x/devflow/pull/489)). devflow ignores it and shows a warning.
|
|
79
|
+
- **`/dynamic-build` wave merges are safer, and a supplied PR link line is used as given** ([#486](https://github.com/dean0x/devflow/issues/486)). A wave merge uses `git merge --no-ff` and records the pre-merge SHA. The ticket branch name is checked before the merge, and a refused name quarantines that ticket. The wave halts when the merge or base SHA is not valid. A ticket whose branch adds no commit stops the wave before the post-merge build, and no wave PR opens. A merge whose post-merge build fails is undone by a reset to the recorded SHA. The run summary names why the wave halted. A PR gets no closing reference from a bare number or the branch name.
|
|
80
|
+
- **Model discovery waits up to 6 s for the routing runtime** ([#519](https://github.com/dean0x/devflow/pull/519)). The wait was 2 s. On a loaded machine, the first start of node with the routing runtime can take more than 2 s. The agents TUI then reported an installed runtime as unknown. `devflow proxy --enable` then did not refresh the model cache. A runtime that does not answer within 6 s still falls back to the stale cache.
|
|
81
|
+
- **The memory worker keeps the queue when it finds neither jq nor node** ([#523](https://github.com/dean0x/devflow/issues/523)). The hooks that start the worker already stop without a parser. A worker that ran with a different PATH deleted the queued turns. Now the worker stops before it reads the queue, and the queue stays for the next run. A parser that exits with an error also leaves the queue and the batch in place, and a queue that cannot be added to a leftover batch stays in its file.
|
|
82
|
+
- **A malformed queue line no longer stops the worker from reading the other lines** ([#528](https://github.com/dean0x/devflow/issues/528)). With jq, the worker read the queue as one stream, so one malformed line stopped the parse. The valid turns after it were lost, or the whole batch was deleted. Now the worker reads each line alone, skips only the malformed line, and logs how many lines in the read window it skipped. A row cut off before its line break runs into the next row, so those two rows are one malformed line. Both parsers read a line as a turn only when its role is user, assistant or qa, so jq and node log the same skip count. A batch with no readable line is still removed.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
8
86
|
## [3.6.0] - 2026-10-14
|
|
9
87
|
|
|
10
88
|
### Upgrade notes — Agent rename (6 agents)
|
|
@@ -1673,7 +1751,8 @@ devflow init
|
|
|
1673
1751
|
|
|
1674
1752
|
---
|
|
1675
1753
|
|
|
1676
|
-
[Unreleased]: https://github.com/dean0x/devflow/compare/
|
|
1754
|
+
[Unreleased]: https://github.com/dean0x/devflow/compare/v3.7.0...HEAD
|
|
1755
|
+
[3.7.0]: https://github.com/dean0x/devflow/compare/v3.6.0...v3.7.0
|
|
1677
1756
|
[3.6.0]: https://github.com/dean0x/devflow/compare/v3.5.0...v3.6.0
|
|
1678
1757
|
[3.5.0]: https://github.com/dean0x/devflow/compare/v3.4.0...v3.5.0
|
|
1679
1758
|
[3.4.0]: https://github.com/dean0x/devflow/compare/v3.3.0...v3.4.0
|
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ This is the **orchestrated flow** — you stay in the loop between every step. W
|
|
|
56
56
|
|
|
57
57
|
**A staffed agent roster.** 20 specialized agents with explicit model assignments — Opus for analysis, Sonnet for execution, Haiku for I/O. Reassign any agent's model with `devflow agents`, including GPT models through external model routing (`devflow proxy`).
|
|
58
58
|
|
|
59
|
-
**Up to 20 parallel Review agents.** Security, architecture, performance, complexity, consistency, regression, testing, and more. Each produces findings with severity, confidence scoring, and concrete fixes. Conditional Review agents activate when relevant (TypeScript for `.ts` files, database for schema changes, compliance when regulated surface detected in the diff). Every finding gets validated and resolved automatically.
|
|
59
|
+
**Up to 20 parallel Review agents.** Security, architecture, performance, complexity, consistency, regression, testing, and more. Each produces findings with severity, confidence scoring, and concrete fixes. Conditional Review agents activate when relevant (language focuses such as TypeScript for `.ts` files, database for schema changes, compliance when regulated surface detected in the diff). The gate level narrows these sets: the default `standard` level drops the TypeScript focus, and `lean` runs fewer focuses still. Every finding gets validated and resolved automatically.
|
|
60
60
|
|
|
61
61
|
**Memory that persists.** Session context survives restarts, `/clear`, and context compaction. Your agent picks up exactly where it left off.
|
|
62
62
|
|
|
@@ -200,7 +200,9 @@ A repository can commit `.devflow/project.json` to settle team-wide choices. Eve
|
|
|
200
200
|
|
|
201
201
|
Each key is checked on its own, so one bad value never disables the rest: a bad `evidence` resolves to `required`, a bad `reviewPublication` to `off`, a bad `compliance` list to the generic lens. A `project.json` or `config.json` that exists but is not a JSON object is never read as absent: the settings fail closed (publication off, knowledge write-back off, the tracker reported as invalid). The compliance lens is the exception, because it only adds scrutiny: it keeps every framework a readable layer declares, and an unreadable `project.json` counts as the generic lens. Memory and learning capture fail open instead: the hooks read an unreadable file as narrowing nothing, so capture runs as your machine's switch says.
|
|
202
202
|
|
|
203
|
-
`config.json` is personal, so it must not be committed. A `config.json` that git tracks is ignored as if it were absent — otherwise a branch could commit `"reviewPublication":"full"` for whoever reviews it — and `devflow tracker|memory|learning|knowledge --status` say so, with the fix: `git rm --cached .
|
|
203
|
+
`config.json` is personal, so it must not be committed. A `config.json` that git tracks is ignored as if it were absent — otherwise a branch could commit `"reviewPublication":"full"` for whoever reviews it — and `devflow tracker|memory|learning|knowledge|gates --status` say so, with the fix: untrack the tracked entry with `git rm --cached`, then check or delete the file, because untracking leaves the committed bytes on disk as your personal file. Commands never read the file themselves — one local resolver folds it with your `config.json` and the machine settings, without touching the network.
|
|
204
|
+
|
|
205
|
+
**Gate level.** The gate level (`full`, `standard` or `lean`) is not a narrow-only switch: your personal `.devflow/config.json` may carry a top-level `"gateLevel"` key that raises or lowers the machine default, `devflow gates --set <level>` sets the machine default in `~/.devflow/manifest.json`, and `standard` applies when neither is set. A `gateLevel` in `project.json` is ignored, and no command writes the personal key. A malformed value reads as `full`. `/code-review` and `/plan` read the level: `standard` and `lean` run fewer review focuses and fewer planning agents. The security checks and, in `/plan`, the design review run at every level. The compliance gap agent runs on its own trigger at every level. Run `devflow gates --set full` to keep the full `/plan` shape; issue #480 adds more readers. After updating, run `devflow init`. See the [CLI reference](https://github.com/dean0x/devflow/blob/main/docs/cli-reference.md#gate-level).
|
|
204
206
|
|
|
205
207
|
## Evidence policy
|
|
206
208
|
|
|
@@ -286,6 +288,8 @@ npx devflow-kit compliance --status # Show compliance state and this repo's
|
|
|
286
288
|
npx devflow-kit tracker --set jira # Pick the machine's default tracker (github | jira | linear)
|
|
287
289
|
npx devflow-kit tracker --status # Show provider, this repo's effective one, learned conventions, mechanics
|
|
288
290
|
npx devflow-kit tracker --servers default # Tracker servers the agents may call (comma-separated names, or default)
|
|
291
|
+
npx devflow-kit gates --set lean # Set the machine's default gate level (full | standard | lean)
|
|
292
|
+
npx devflow-kit gates --status # Show the gate level in effect here and which layer decided it
|
|
289
293
|
npx devflow-kit rules --status # Show installed rules
|
|
290
294
|
npx devflow-kit security --status # Show / manage the security deny list
|
|
291
295
|
npx devflow-kit safe-delete --enable # Install rm -> trash safe-delete
|
package/dist/agents/code.md
CHANGED
|
@@ -67,7 +67,7 @@ You receive from orchestrator:
|
|
|
67
67
|
- Read those relevant files to understand interfaces, types, naming conventions, error handling, and testing patterns established by prior work
|
|
68
68
|
- If PRIOR_PHASE_SUMMARY is provided, use it to validate your understanding — actual code is authoritative, summaries are supplementary
|
|
69
69
|
- If `DECISIONS_CONTEXT` is provided, follow `devflow:apply-decisions` on it. An absent key or `(none)` means no decisions context. State every decision or pitfall you apply in words in code, comments, tests and commit messages, never by its ID.
|
|
70
|
-
- If `HANDOFF_FILE` is provided, read only the `## Phase {N} Implementation Summary` section of the phase immediately before yours — list its `##` headings with `command grep -n '^## ' "$HANDOFF_FILE"`, then Read that range with `offset` and `limit` — never the whole file.
|
|
70
|
+
- If `HANDOFF_FILE` is provided, read only the `## Phase {N} Implementation Summary` section of the phase immediately before yours — list its `##` headings with `command grep -n '^## ' "$HANDOFF_FILE"`, then Read that range with `offset` and `limit` — never the whole file.
|
|
71
71
|
|
|
72
72
|
2. **Implement the plan**: Work through execution steps systematically, creating and modifying files. Follow existing patterns. Type everything. Use Result types if codebase uses them.
|
|
73
73
|
|
|
@@ -93,7 +93,6 @@ When `OPERATION: issue-fix`, you are fixing pre-classified issues assigned FIX_N
|
|
|
93
93
|
- **Standard scope**: Fix directly following existing patterns
|
|
94
94
|
- **Careful scope**: systematic protocol — understand (50+ lines context, callers/consumers) → plan → write failing regression test → implement → verify tests pass → commit
|
|
95
95
|
3. **Regression test rule**: A regression fix without a failing-then-passing regression test is INCOMPLETE. Report BLOCKED rather than commit an unverified fix.
|
|
96
|
-
4. **Self-verification scope**: Run compile + the fix's regression test only. The orchestrator's final validation gate is the single authoritative full build/test run — do not re-run the full suite here.
|
|
97
96
|
|
|
98
97
|
**Return report** (a Return block in the spawn replaces this shape):
|
|
99
98
|
- Status: COMPLETE | PARTIAL | BLOCKED
|
|
@@ -120,9 +119,7 @@ When `OPERATION: issue-fix`, you are fixing pre-classified issues assigned FIX_N
|
|
|
120
119
|
- {Decision with rationale}
|
|
121
120
|
|
|
122
121
|
### Integration Points for Next Phase
|
|
123
|
-
-
|
|
124
|
-
- {Functions to call}
|
|
125
|
-
- {Types to import}
|
|
122
|
+
- `path:line` of each interface, function or type the next phase builds on
|
|
126
123
|
```
|
|
127
124
|
|
|
128
125
|
## Running commands
|
|
@@ -140,11 +137,11 @@ Run builds, typechecks, lints and tests in the foreground, each with an explicit
|
|
|
140
137
|
|
|
141
138
|
## Principles
|
|
142
139
|
|
|
143
|
-
1. **
|
|
144
|
-
2. **
|
|
145
|
-
3. **
|
|
146
|
-
|
|
147
|
-
|
|
140
|
+
1. **Read once** - Never re-read an unchanged file. For a file over 400 lines, or a second look, `grep -n` first, then Read only that range (`offset`, `limit`)
|
|
141
|
+
2. **Small, focused changes** - Don't scope creep beyond the plan
|
|
142
|
+
3. **Fail honestly** - If blocked, report clearly with what was completed
|
|
143
|
+
|
|
144
|
+
- Never print a large file whole; page it with offset and limit, and cap any output you print. Before printing search matches, list or count them.
|
|
148
145
|
|
|
149
146
|
## Output
|
|
150
147
|
|
|
@@ -184,4 +181,3 @@ Report cap: final message at most about 1,500 tokens; longer material goes to a
|
|
|
184
181
|
- Switch branches during implementation
|
|
185
182
|
- Push to branches other than your feature branch
|
|
186
183
|
- Merge PRs (orchestrator handles this)
|
|
187
|
-
- Trust handoff summaries without reading actual code
|
package/dist/agents/fix.md
CHANGED
|
@@ -118,11 +118,11 @@ Run builds, typechecks, lints and tests in the foreground, each with an explicit
|
|
|
118
118
|
|
|
119
119
|
## Principles
|
|
120
120
|
|
|
121
|
-
1. **
|
|
122
|
-
2. **
|
|
123
|
-
3. **
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
1. **Read once** - Never re-read an unchanged file. For a file over 400 lines, or a second look, `grep -n` first, then Read only that range (`offset`, `limit`)
|
|
122
|
+
2. **Small, focused changes** - Don't scope creep beyond the plan
|
|
123
|
+
3. **Fail honestly** - If blocked, report clearly with what was completed
|
|
124
|
+
|
|
125
|
+
- Never print a large file whole; page it with offset and limit, and cap any output you print. Before printing search matches, list or count them.
|
|
126
126
|
|
|
127
127
|
## Output
|
|
128
128
|
|
|
@@ -162,4 +162,3 @@ Report cap: final message at most about 1,500 tokens; longer material goes to a
|
|
|
162
162
|
- Switch branches during implementation
|
|
163
163
|
- Push to branches other than your feature branch
|
|
164
164
|
- Merge PRs (orchestrator handles this)
|
|
165
|
-
- Trust handoff summaries without reading actual code
|
package/dist/agents/refactor.md
CHANGED
|
@@ -86,11 +86,11 @@ Run builds, typechecks, lints and tests in the foreground, each with an explicit
|
|
|
86
86
|
|
|
87
87
|
## Principles
|
|
88
88
|
|
|
89
|
-
1. **
|
|
90
|
-
2. **
|
|
91
|
-
3. **
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
1. **Read once** - Never re-read an unchanged file. For a file over 400 lines, or a second look, `grep -n` first, then Read only that range (`offset`, `limit`)
|
|
90
|
+
2. **Small, focused changes** - Don't scope creep beyond the plan
|
|
91
|
+
3. **Fail honestly** - If blocked, report clearly with what was completed
|
|
92
|
+
|
|
93
|
+
- Never print a large file whole; page it with offset and limit, and cap any output you print. Before printing search matches, list or count them.
|
|
94
94
|
|
|
95
95
|
## Output
|
|
96
96
|
|
|
@@ -130,4 +130,3 @@ Report cap: final message at most about 1,500 tokens; longer material goes to a
|
|
|
130
130
|
- Switch branches during implementation
|
|
131
131
|
- Push to branches other than your feature branch
|
|
132
132
|
- Merge PRs (orchestrator handles this)
|
|
133
|
-
- Trust handoff summaries without reading actual code
|
package/dist/agents/review.md
CHANGED
|
@@ -229,3 +229,5 @@ use the same prefix so values are not double-masked.
|
|
|
229
229
|
| java | If .java files changed |
|
|
230
230
|
| python | If .py files changed |
|
|
231
231
|
| rust | If .rs files changed |
|
|
232
|
+
|
|
233
|
+
The gate level (`GATE_LEVEL`, `/code-review` Step 1.2b) removes focuses from this table at `standard` and `lean`.
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* devflow gates — Show or set the machine's default gate level.
|
|
3
|
+
*
|
|
4
|
+
* The gate level is one of `full`, `standard` or `lean`. `--set` writes the MACHINE
|
|
5
|
+
* default, `features.gateLevel` in `~/.devflow/manifest.json`, and nothing else: not
|
|
6
|
+
* the repository's `project.json`, not the personal `.devflow/config.json` (no
|
|
7
|
+
* command writes that key), and no sentinel, re-arm or install step. `--status` shows
|
|
8
|
+
* the level that applies in the current directory, which layer decided it and what
|
|
9
|
+
* the machine records; it writes nothing.
|
|
10
|
+
*
|
|
11
|
+
* Three parts, so no business logic throws or prints:
|
|
12
|
+
* gatherGateStatus resolves the settings ONCE and reads the manifest raw, and
|
|
13
|
+
* returns a {@link GateStatus} that is a discriminated union
|
|
14
|
+
* renderGateStatus pure: one {@link GateStatus} to lines and warnings
|
|
15
|
+
* gatesCommand the thin shell: parse, call, print, set the exit code
|
|
16
|
+
*
|
|
17
|
+
* No code here chooses a level from a task or a diff: the level comes from the
|
|
18
|
+
* resolver (personal key, machine key, default) or from the word typed after `--set`.
|
|
19
|
+
*/
|
|
20
|
+
import { Command } from 'commander';
|
|
21
|
+
import * as path from 'path';
|
|
22
|
+
import * as p from '@clack/prompts';
|
|
23
|
+
import color from 'picocolors';
|
|
24
|
+
import { DEFAULT_GATE_LEVEL, GATE_LEVELS, machineGateLevel, parseGateLevel, readGateManifest, writeMachineGateLevel, } from '../../core/gate-level.js';
|
|
25
|
+
import { describeEchoedValue } from '../../core/echo-value.js';
|
|
26
|
+
import { loadSettingsModule, settingsSourceFile, trackedConfigWarning, } from '../../core/evidence-policy.js';
|
|
27
|
+
import { getDevFlowDirectory } from '../../targets/claude-code/claude-paths.js';
|
|
28
|
+
// ── Wording ────────────────────────────────────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* What each level means, in one line. Each line says what the word stands for and
|
|
31
|
+
* not what a command does with it.
|
|
32
|
+
*/
|
|
33
|
+
export const GATE_LEVEL_MEANINGS = Object.freeze({
|
|
34
|
+
full: 'the most checking; any value that is not a level also reads as full',
|
|
35
|
+
standard: 'the default when no level is set',
|
|
36
|
+
lean: 'the least checking',
|
|
37
|
+
});
|
|
38
|
+
/** What reads the level today; each later reader updates this text. */
|
|
39
|
+
export const GATE_LEVEL_EFFECT = '/code-review and /plan read the level: standard and lean run fewer review focuses and fewer planning agents. Other commands read it once issue #480 lands.';
|
|
40
|
+
const MACHINE_MANIFEST_NAME = 'manifest.json';
|
|
41
|
+
const MACHINE_INVALID_LINE = `invalid (${MACHINE_MANIFEST_NAME}; reads as full)`;
|
|
42
|
+
const VALID_LEVELS = GATE_LEVELS.join(', ');
|
|
43
|
+
const VALID_LEVELS_OR = `${GATE_LEVELS.slice(0, -1).join(', ')}, or ${GATE_LEVELS[GATE_LEVELS.length - 1]}`;
|
|
44
|
+
/** The resolved settings, or null when the resolver is unusable or throws despite its contract. */
|
|
45
|
+
function resolveOnce(module, dir) {
|
|
46
|
+
try {
|
|
47
|
+
return module.resolveSettings({ dir });
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function manifestState(read) {
|
|
54
|
+
switch (read.kind) {
|
|
55
|
+
case 'absent': return 'absent';
|
|
56
|
+
case 'object': return 'readable';
|
|
57
|
+
case 'corrupt':
|
|
58
|
+
case 'rejected': return 'unreadable';
|
|
59
|
+
default: {
|
|
60
|
+
const exhaustive = read;
|
|
61
|
+
return exhaustive;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Why a fail-closed result failed, or null when the resolver names no reason. */
|
|
66
|
+
function failClosedReason(settings) {
|
|
67
|
+
return settings.unreadable === null ? null : `${settingsSourceFile(settings.unreadable)} is unreadable`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Gather one {@link GateStatus}. Never throws, never writes.
|
|
71
|
+
*
|
|
72
|
+
* The settings resolve ONCE, and that one result feeds the level, the machine value
|
|
73
|
+
* and the tracked-config warning. The manifest is read with the gate reader, never
|
|
74
|
+
* with `readManifest()`, which heal-writes and returns null for a manifest it
|
|
75
|
+
* rejects: a read-only status must do neither. The manifest is read twice, once by
|
|
76
|
+
* the gate reader here and once by the resolver; a write between the two reads can
|
|
77
|
+
* mix two versions, which is acceptable because the result is display only and no
|
|
78
|
+
* action follows it.
|
|
79
|
+
*/
|
|
80
|
+
export async function gatherGateStatus(deps) {
|
|
81
|
+
const manifestPath = path.join(deps.devflowDir, MACHINE_MANIFEST_NAME);
|
|
82
|
+
const read = await deps.readManifest(deps.devflowDir);
|
|
83
|
+
const base = { manifest: manifestState(read), manifestPath };
|
|
84
|
+
// The TypeScript reader's machine value, for a result that carries none of its own.
|
|
85
|
+
const stored = read.kind === 'object' || read.kind === 'rejected' ? machineGateLevel(read.raw) : null;
|
|
86
|
+
const loaded = deps.loadSettings();
|
|
87
|
+
const settings = loaded.ok ? resolveOnce(loaded.value, deps.dir) : null;
|
|
88
|
+
if (settings === null) {
|
|
89
|
+
return { ...base, kind: 'unloadable', machine: stored };
|
|
90
|
+
}
|
|
91
|
+
const trackedWarning = trackedConfigWarning(settings);
|
|
92
|
+
if (!settings.ok || settings.gateLevelSource === null) {
|
|
93
|
+
// Only a result that names an unreadable file carries the manifest's value; a
|
|
94
|
+
// fail-closed result without one (git did not answer) never read the manifest.
|
|
95
|
+
const machine = settings.unreadable !== null ? settings.gateLevelMachine : stored;
|
|
96
|
+
return { ...base, kind: 'fail-closed', reason: failClosedReason(settings), machine, trackedWarning };
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
...base,
|
|
100
|
+
kind: 'resolved',
|
|
101
|
+
level: settings.gateLevel,
|
|
102
|
+
source: settings.gateLevelSource,
|
|
103
|
+
invalid: settings.gateLevelInvalid,
|
|
104
|
+
machine: settings.gateLevelMachine,
|
|
105
|
+
trackedWarning,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/** The machine line: `none` when the manifest records no level or cannot be read (its own warning says which). */
|
|
109
|
+
function machineLine(machine) {
|
|
110
|
+
if (machine === 'invalid')
|
|
111
|
+
return `Machine: ${MACHINE_INVALID_LINE}`;
|
|
112
|
+
if (machine === null)
|
|
113
|
+
return 'Machine: none';
|
|
114
|
+
return `Machine: ${machine}`;
|
|
115
|
+
}
|
|
116
|
+
function manifestWarning(status) {
|
|
117
|
+
if (status.manifest !== 'unreadable')
|
|
118
|
+
return null;
|
|
119
|
+
return `Manifest at ${status.manifestPath} cannot be read as a manifest; devflow init will replace this file.`;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Render a {@link GateStatus}. Pure and total over every `kind`.
|
|
123
|
+
*
|
|
124
|
+
* A malformed level is reported by the layer that holds it and its state, never by
|
|
125
|
+
* its value: a raw config value never reaches a terminal.
|
|
126
|
+
*/
|
|
127
|
+
export function renderGateStatus(status) {
|
|
128
|
+
const warnings = [];
|
|
129
|
+
const lines = [];
|
|
130
|
+
switch (status.kind) {
|
|
131
|
+
case 'resolved': {
|
|
132
|
+
lines.push(`Effective: ${status.level} (${status.source}${status.invalid ? ', invalid' : ''})`);
|
|
133
|
+
lines.push(machineLine(status.machine));
|
|
134
|
+
if (status.invalid && status.source === 'personal') {
|
|
135
|
+
warnings.push(`gateLevel in ${settingsSourceFile('personal')} is not one of ${VALID_LEVELS}, so ${status.level} is in effect. ` +
|
|
136
|
+
'The value is not shown.');
|
|
137
|
+
}
|
|
138
|
+
if (status.trackedWarning !== null)
|
|
139
|
+
warnings.push(status.trackedWarning);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case 'fail-closed': {
|
|
143
|
+
lines.push(`Effective: full (fail-closed: ${status.reason ?? 'reason unknown'})`);
|
|
144
|
+
lines.push(machineLine(status.machine));
|
|
145
|
+
if (status.trackedWarning !== null)
|
|
146
|
+
warnings.push(status.trackedWarning);
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
case 'unloadable': {
|
|
150
|
+
lines.push('Effective: level unknown');
|
|
151
|
+
lines.push(machineLine(status.machine));
|
|
152
|
+
warnings.push('The settings resolver could not be loaded, so the level that applies is unknown.');
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
default: {
|
|
156
|
+
const exhaustive = status;
|
|
157
|
+
return exhaustive;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const fromManifest = manifestWarning(status);
|
|
161
|
+
if (fromManifest !== null)
|
|
162
|
+
warnings.push(fromManifest);
|
|
163
|
+
return { lines, warnings };
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Map the writer's result to a message and an exit code. Pure and total. Every
|
|
167
|
+
* refusal exits 1 and says nothing was written, except a failed write, which names
|
|
168
|
+
* the path and the error code.
|
|
169
|
+
*/
|
|
170
|
+
export function describeGateWrite(result, level, manifestPath) {
|
|
171
|
+
switch (result.kind) {
|
|
172
|
+
case 'written':
|
|
173
|
+
return { kind: 'success', message: `Gate level: ${level}`, exitCode: 0 };
|
|
174
|
+
case 'unchanged':
|
|
175
|
+
return { kind: 'info', message: `Gate level: ${level} (unchanged)`, exitCode: 0 };
|
|
176
|
+
case 'not-installed':
|
|
177
|
+
return { kind: 'error', message: `No manifest found at ${manifestPath}. Run devflow init first.`, exitCode: 1 };
|
|
178
|
+
case 'unreadable':
|
|
179
|
+
return {
|
|
180
|
+
kind: 'error',
|
|
181
|
+
message: `Manifest at ${result.path} exists but cannot be read; nothing changed. Move it aside, then run devflow init.`,
|
|
182
|
+
exitCode: 1,
|
|
183
|
+
};
|
|
184
|
+
case 'write-failed':
|
|
185
|
+
return {
|
|
186
|
+
kind: 'error',
|
|
187
|
+
message: `Could not write the manifest at ${result.path} (${result.reason}); nothing changed.`,
|
|
188
|
+
exitCode: 1,
|
|
189
|
+
};
|
|
190
|
+
case 'invalid-level':
|
|
191
|
+
return {
|
|
192
|
+
kind: 'error',
|
|
193
|
+
message: `Unknown gate level: "${describeEchoedValue(level)}"; nothing changed. Valid levels: ${VALID_LEVELS}`,
|
|
194
|
+
exitCode: 1,
|
|
195
|
+
};
|
|
196
|
+
default: {
|
|
197
|
+
const exhaustive = result;
|
|
198
|
+
return exhaustive;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Write the machine default and describe the result. Never throws. `now` is injected
|
|
204
|
+
* so a test fixes the `updatedAt` stamp.
|
|
205
|
+
*/
|
|
206
|
+
export async function runGatesSet(devflowDir, level, now = () => new Date().toISOString()) {
|
|
207
|
+
const result = await writeMachineGateLevel(devflowDir, level, now);
|
|
208
|
+
return describeGateWrite(result, level, path.join(devflowDir, MACHINE_MANIFEST_NAME));
|
|
209
|
+
}
|
|
210
|
+
function printOutcome(outcome) {
|
|
211
|
+
switch (outcome.kind) {
|
|
212
|
+
case 'success':
|
|
213
|
+
p.log.success(outcome.message);
|
|
214
|
+
break;
|
|
215
|
+
case 'info':
|
|
216
|
+
p.log.info(outcome.message);
|
|
217
|
+
break;
|
|
218
|
+
case 'error':
|
|
219
|
+
p.log.error(outcome.message);
|
|
220
|
+
break;
|
|
221
|
+
default: {
|
|
222
|
+
const exhaustive = outcome.kind;
|
|
223
|
+
return exhaustive;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function levelsHelp() {
|
|
228
|
+
const width = Math.max(...GATE_LEVELS.map(level => level.length));
|
|
229
|
+
return GATE_LEVELS.map(level => ` ${level.padEnd(width)} ${GATE_LEVEL_MEANINGS[level]}`).join('\n');
|
|
230
|
+
}
|
|
231
|
+
export const gatesCommand = new Command('gates')
|
|
232
|
+
.description('Show or set the machine\'s default gate level')
|
|
233
|
+
.option('--status', 'Show the gate level in effect here, which layer decided it, and the machine default')
|
|
234
|
+
.option('--set <level>', `Set the machine's default gate level: ${VALID_LEVELS_OR}`)
|
|
235
|
+
.addHelpText('after', `\nGate levels:\n${levelsHelp()}\n\n` +
|
|
236
|
+
`The default is ${DEFAULT_GATE_LEVEL}. --set writes only the machine default. A personal .devflow/config.json ` +
|
|
237
|
+
'"gateLevel" overrides it in one repository; no command writes that key.\n' +
|
|
238
|
+
GATE_LEVEL_EFFECT)
|
|
239
|
+
.action(async (options) => {
|
|
240
|
+
const devflowDir = getDevFlowDirectory();
|
|
241
|
+
if (!options.status && options.set === undefined) {
|
|
242
|
+
p.intro(color.bgCyan(color.white(' Gates ')));
|
|
243
|
+
p.note(`${color.cyan('devflow gates --status')} Show the gate level in effect here and the machine default\n` +
|
|
244
|
+
`${color.cyan('devflow gates --set <level>')} Set the machine default\n\n` +
|
|
245
|
+
`Valid levels:\n${levelsHelp()}`, 'Usage');
|
|
246
|
+
p.outro(color.dim(GATE_LEVEL_EFFECT));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
// Validate --set before any I/O, even beside --status: a bad word is rejected,
|
|
250
|
+
// never repaired, and exits 1 as it does in `devflow tracker` and `devflow compliance`.
|
|
251
|
+
const parsed = options.set === undefined ? undefined : parseGateLevel(options.set);
|
|
252
|
+
if (parsed !== undefined && !parsed.ok) {
|
|
253
|
+
p.log.error(parsed.error);
|
|
254
|
+
process.exitCode = 1;
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
// --status wins over a valid --set and writes nothing, mirroring `devflow tracker`.
|
|
258
|
+
// With neither flag the usage printed above, so no --set here means --status alone.
|
|
259
|
+
if (options.status || parsed === undefined) {
|
|
260
|
+
const status = await gatherGateStatus({
|
|
261
|
+
devflowDir,
|
|
262
|
+
dir: process.cwd(),
|
|
263
|
+
loadSettings: loadSettingsModule,
|
|
264
|
+
readManifest: readGateManifest,
|
|
265
|
+
});
|
|
266
|
+
const rendered = renderGateStatus(status);
|
|
267
|
+
p.note(rendered.lines.join('\n'), 'Gate Level Status');
|
|
268
|
+
for (const warning of rendered.warnings)
|
|
269
|
+
p.log.warn(warning);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const outcome = await runGatesSet(devflowDir, parsed.value);
|
|
273
|
+
printOutcome(outcome);
|
|
274
|
+
process.exitCode = outcome.exitCode;
|
|
275
|
+
});
|
|
276
|
+
//# sourceMappingURL=gates.js.map
|
|
@@ -27,6 +27,7 @@ export const FEATURE_DEFAULTS = {
|
|
|
27
27
|
behavior: false,
|
|
28
28
|
compliance: { enabled: false, frameworks: [] },
|
|
29
29
|
tracker: { provider: DEFAULT_TRACKER_PROVIDER },
|
|
30
|
+
gateLevel: undefined,
|
|
30
31
|
};
|
|
31
32
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
32
33
|
/**
|
|
@@ -60,7 +61,11 @@ export function resolveSeedFeatures(manifest) {
|
|
|
60
61
|
const memory = manifest?.features.memory ?? FEATURE_DEFAULTS.memory;
|
|
61
62
|
const knowledge = manifest?.features.knowledge ?? FEATURE_DEFAULTS.knowledge;
|
|
62
63
|
const learning = manifest?.features.learning ?? FEATURE_DEFAULTS.learning;
|
|
63
|
-
|
|
64
|
+
// The raw stored level, with no default operator: `??` would turn a stored `null`
|
|
65
|
+
// into the default, and init would then write the default over a value the
|
|
66
|
+
// fold reads as the strictest level.
|
|
67
|
+
const gateLevel = manifest?.features.gateLevel;
|
|
68
|
+
return { ambient, memory, hud, knowledge, learning, rules, proxy, behavior, compliance, tracker, gateLevel };
|
|
64
69
|
}
|
|
65
70
|
/**
|
|
66
71
|
* Resolve the flag record for the init seed.
|
|
@@ -318,6 +323,9 @@ export function applyCliToggles(base, toggles) {
|
|
|
318
323
|
behavior: toggles.behavior ?? base.behavior,
|
|
319
324
|
compliance: toggles.compliance ?? base.compliance,
|
|
320
325
|
tracker: toggles.tracker ?? base.tracker,
|
|
326
|
+
// No command-line flag names the gate level, so the seed's raw value passes
|
|
327
|
+
// through. Only `undefined` means "not specified": `null` is a value, not a gap.
|
|
328
|
+
gateLevel: toggles.gateLevel !== undefined ? toggles.gateLevel : base.gateLevel,
|
|
321
329
|
};
|
|
322
330
|
}
|
|
323
331
|
//# sourceMappingURL=init-seed.js.map
|