Repository navigation
feat: token diet wave 3 — settings hoist, learning-off variants, knowledge reshape, /code-review gating, session plumbing, Git split (#425–#429) - #440
Merged
Conversation
release.md becomes release.mds: the Phase 1c block that restated the evidence_policy define is now its expansion, and nothing else in the file moves. The compiled dist/commands/release.md is byte-identical to the former hand-authored file (cmp exits 0). With no hand-authored command left, the copy path in build-mds.ts is removed and dist/commands/ joins the orphan-prune scope. The manifest grows to 14 command hosts, dist-host-count rises 13 to 14, and a guard fails on any static .md command in src/assets/commands/.
code, design, diagnose, knowledge, research, review, scrutinize, triage and skim become generator hosts (output-dir: dist/agents) like the Git agent, so their learning arms can be built into a learning-off variant. The body of each is unchanged: eight compiled agents are byte-identical to the former hand-authored files, and diagnose differs only by the blank line the MDS compiler drops at end of file. Every test that read one of these agents by its src path now reads it through resolveAgentSource, which prefers the compiled agent. The generator-host roster grows from one to ten, and the provider-scope allowlist follows the Code agent's paste gate to its .mds source and compiled file.
A command or agent body can carry whole-line learning markers (on, off, end).
The new pure module src/core/learning-variants.ts splits a compiled body into
its learning-on and learning-off variants and refuses nesting, an unopened end,
an unclosed arm, an empty arm and a stray marker, each with its line. The markers
are interim until mdscript ships fence- and indent-aware conditionals.
The build runs the split after the MDS compile, so it costs no extra compile.
The learning-on variant is written to today's path, and the learning-off variant
to dist/learning-off/{commands,agents}/ only for a host that has an arm. The
orphan prune now covers dist/learning-off/ and is keyed on the files written.
A reference module that carries a marker is refused, and the build prints the
number of learning-off variants, which the manifest roster
LEARNING_VARIANT_HOSTS pins at every step.
Guards: no dist Markdown file carries a marker, the learning-off files are the
roster in both directions, and the tarball carries exactly those files.
…cisions Every command host now expands the settings block once, before the earliest of its consumers (14 hosts, release and dynamic-profile included). The publication, compliance and knowledge gates and the decisions partial import nothing from the settings partial; their text refers to the line resolved above and keeps each condition in its own step. Decisions loads sit behind a one-sentence LEARNING=off gate. The authoring decisions step leaves the authoring preamble for its own define, expanded by the four dynamic hosts after their settings block. The index goes only to agents whose contract declares it: pass sites narrow to declared receivers, every Code spawn carries the key, and the Code agent's fallback read and the apply-decisions read-it-yourself clause are removed. Skim takes a LEARNING input and skips its decisions read when it is off. A new seam guard computes the declared set from the agent sources and holds spawn sites and pass sentences to it, reading spawn sites through the shared collector, which now takes an agent type and covers the engine pseudo-form.
Every command and nine agents carry learning-on and learning-off arms, so a learning-off build holds no decisions text: the decisions load, the pass sites, the spawn keys, the Produces/Requires and phase-tree lines, the apply-decisions steps and preload, and Skim's LEARNING input and Step 6. The learning-on build is byte-identical to the one before the arms. Fourteen commands and the nine agents join LEARNING_VARIANT_HOSTS. With learning off, eight hosts keep the settings block (they have a consumer besides learning); for the other six it sits inside the learning-on arm. The runtime LEARNING gate and Skim's skip rule exist only in the learning-on build. New guards read the real arms: each variant against an independent resolution of the compiled host, learning text in every on arm and none in an off arm, no token under the learning-off tree, completeness against the Learning agent exemption, off-agent frontmatter, settings blocks per variant, and the status-line anchors. Code's preload is measured for both variants, and the learning-off tree joins the provider-scope scan.
The machine's learning switch now decides which prompts and which skill an install carries. With learning off, installViaFileCopy installs the learning-off variant of every command and agent that has one (the learning-off directory first in the source order, then the normal order) and leaves the apply-decisions skill out. installViaFileCopy takes a required `learning`; init passes the value it has already settled. convergeLearningVariants makes an already-installed tree match the switch. Its roster is the files under dist/learning-off. It rewrites only files already installed, compares bytes first, writes atomically, isolates each item and only ever warns. The skill is resolved through the installer's own shadow rule, so a shadow stays dormant under learning off and applies again on enable. Init runs it after the file copy; `devflow learning --enable/--disable` runs it after writing the switch, skips it with a "run devflow init" message when the manifest version differs from the running one, and reapplies the saved agent model mapping so `devflow agents` overrides survive. The closure guard keeps reasoning over the learning-on variant and now pins that no prompt a learning-off machine installs references the absent skill.
…ns block The rule that tells the main model to pass the decisions index on as DECISIONS_CONTEXT moves out of the orchestrator charter, which every ambient session pays for, into the PROJECT DECISIONS block of session-start-context, directly after its Index line. That block is already gated on the machine's learning switch and the repository's narrowing, so a learning-off machine no longer carries the rule, and sessions with ambient off now see it too. The rule is only emitted with an Index line, since there is nothing to pass without one. The charter shrinks from 2,709 to 2,514 characters against its 3,072 ceiling, which stays where it is.
…ent hosts Every command is now an MDS host and ten agents are generator hosts, so the static-command wording in the contributor docs and CLAUDE.md goes, and the agent counts and examples follow. The learning-off variants, their install order, the toggle's converge and the apply-decisions skill condition are described once in the file-organization reference and pointed to from the CLI and hooks references. One changelog bullet for the series.
The all-off install runs with learning off, so it no longer installs devflow:apply-decisions: the skill directory and its SKILL.md leave the snapshot. Nothing else moves; the github and jira-hipaa installs (learning on) and the hook matrix are unchanged.
…e-set fence The learning toggle now converges the installed learning variants, so its allowlist admits the variant roster's command and agent files (read from dist/learning-off, the converge's own source) and the apply-decisions skill directory, matched file by file with no commands/agents subtree. Probes reject a write to an agent or command outside the roster, another skill, and a roster name under the wrong kind. The roster surface counts as one declared allowlist entry, because its members come from the build and not from hand edits. The footprint ceiling rises from 11 to 12 by the maintainer's one-time authorisation, and a probe shows a second hand-written entry reaches 13 and fails it.
A KB now leads with a `## Rules` section of one-line bullets under stable
KB-AP-n / KB-INV-n IDs, held to a 30,000-character target and a 40,000
ceiling that are curated to, never truncated to. knowledge_load hands each
agent the one to three most relevant Rules bullets per KB, its path and a
heading index (listed with `command grep -n '^## '`, read with a ranged
Read) instead of the whole file. A KB with no Rules section falls back to
a few Anti-Patterns or Gotchas entries cited by section name.
Review, Triage, Design, Code, Diagnose, Research and Explore take Rules plus
the index; Evaluate and Scrutinize take the Rules alone. /release loads
through the same delivery and passes neither variable to a spawn. The
charter's Feature knowledge bullet passes the same form.
Each Code phase now appends its own `## Phase {N} Implementation Summary`
section (at most 8,192 bytes) to the handoff file, and the next phase reads
only the one before it; the orchestrator no longer writes phase sections.
New guards: feature-knowledge-delivery, kb-shape and handoff-sections, each
with known-bad probes over a non-empty corpus.
…ands The Validation Order table gains a Quiet form column, and one sentence after it makes a verbose re-run of only the failing test the stated exception to the never-re-run rule. The Running commands block is untouched. The TypeScript, Python, Go, Java and Rust rules each gain one quiet-form bullet with their paths frontmatter unchanged.
A fourteenth rule, registered with the core plugin, tells every session to list a large file's headings before reading ranges, to count search matches before printing them, and never to print a large file whole. Its body is about 400 characters (ceiling 450) and names no search binary or count flag. The rule-count statements in the docs now say 14.
…eference scan The probe path named a skill that does not exist, so the scan over test sources read it as a non-canonical skill reference. The path is now joined from parts; the probe asserts the same thing.
Phase 1 classifies each worktree's diff as code, docs-only, tests-only or
lockfile-only (a mixed non-code diff keeps each class it holds). A reduced
class runs a reduced focus set, and language and conditional triggers are
suppressed inside it. The orchestrator writes diff.patch once per worktree
and a diff-{focus}.patch per language focus, by redirect, and Review reads
its file through DIFF_FILE instead of running git. The language gate becomes
one fixed-prefix stamp line the installer rewrites, replacing the run-time
test -f probe.
Principles 1 and 3 name the redirect-only writes and the bounded path listing
as exceptions that print nothing. review.md declares DIFF_FILE and DIFF_RANGE
and verifies findings with ranged reads. /dynamic-build is untouched.
The requires-closure, claude-dir and skill-references tests are retargeted to
the stamp, and a new guard pins the classification, the reduced sets, the
union, the suppression, the patch writes and the exceptions with known-bad
probes.
The language gate is one fixed-prefix line of the installed code-review.md, written from the registry and the effective selection by a pure function (installedLanguageFocuses). installViaFileCopy rewrites it after the command copy and the orphan sweep on every install shape, whether or not code-review's own plugin is in the run, and the selective uninstall phase re-stamps it from the plugins that remain. The learning converge carries the installed stamp into the variant it writes, so a variant switch never loses the list and a steady-state run still finds the installed bytes equal to what it would write. Only code-review.md carries the stamp; /dynamic-build does not. Adds the stamp unit tests, a built-CLI test under a temp HOME for the install, partial install, uninstall and learning-toggle cases, and the installed-copy and stamp-list assertions in the retargeted claude-dir and skill-references tests. The D-PRESENCE-GATED JSDoc now describes the stamp.
…tamp CLAUDE.md names the stamp as the /code-review language gate, docs/commands.md describes the reduced focus sets and the stamped language focuses, docs/reference/init.md records that installs stamp and uninstall --plugin re-stamps, and the changelog gets one #428 entry with the byte figures.
devflow flags gains auto-compact-window, mapped to the Claude Code window-size variable CLAUDE_CODE_AUTO_COMPACT_WINDOW (100000-1000000, integer). It sits right after bash-max-timeout-ms in the valued block, is unset by default (the manifest records null, settings.json carries no key) and is not recommended: init applies a seeded record unseen, and a smaller window compacts sooner, which the new resume directive has not yet been checked against a forced mid-command compaction. It has no upstream default because the env name and range were not confirmed against the binary. devflow does not write or name the percent-based override. A hand-set in-range value is folded into the record by the existing adoption fold, on init and on a flags --set of another flag. Counts in the CLI reference and the file-organization tree move from 30 to 31, and the flag gets its table row.
claude-md-audit.cjs reads the gated CLAUDE.md roots and reports the @path imports that load a large file into every thread: an import over 10,000 bytes, and a root whose import chain totals over 40,000 bytes. The grammar, hop bound (five), bounds (64 paths per root, 64 KiB scanned per file, 1 MiB read per run), display gate (printable ASCII, else "<path not shown>"), finding keys and the stamp format live once, in the script; it writes nothing and opens only regular files (FIFOs, devices and sockets are stat-ed and skipped, and the open is non-blocking and re-checked). The facade src/core/claude-md-audit.ts is the typed seam (loadScript against an explicit surface map): it names the roots, runs the audit, formats the one note init prints, and writes the stamp under an existing machine root only, after an lstat check that refuses a symlink or any non-regular stamp path. The script's header records the comparison with Claude Code's memory documentation (code.claude.com/docs/en/memory, 2026-10-10): the import syntax, relative-to-the- importing-file resolution, ~/ and absolute paths, code span and fence skipping and quoted paths agree; the documented depth is four hops on the current page and five on older snapshots, so the bound stays five; escaped spaces are not followed; a file over 4 MiB is skipped as Claude Code skips it; the external-import approval dialog and HTML-comment stripping are not modelled. A permanent guard keeps the percent-based auto-compact override out of src/.
…tart-context Section 5: on a SessionStart whose source is compact the hook appends one fixed line (249 ASCII characters, interpolating nothing) telling the model that if a devflow command was running it should re-read the command file from the Claude config directory, list headings, read from the current phase, take its input (COMMAND_INPUT or ARGUMENTS) from the summary and resume after the last finished phase, else ignore. The gate is a positive compact case, emitted whatever the project; a new ceiling compact-directive-max-chars (250) pins the emitted text. Section 6: the hook audits the gated CLAUDE.md roots and shows a finding once, in a top-level systemMessage that never enters additionalContext. The stamp at ~/.devflow/.claude-md-audit makes an unchanged start free (builtin comparison of the root set and each recorded path; no fork, no node) and a changed start cost one node process; a machine with no CLAUDE.md anywhere records its stamp with builtins and starts none. A finding is recorded only after the output was emitted; a failure shows nothing and records the roots with an error marker. The stamp path is refused when it is a link or not a regular file, and is written through an owner-only sibling temp file. Sections 5 and 6 add no exit path. json_session_output (and json-helper session-output) takes an optional message: with one argument the envelope is byte-identical to before; with a message and no context it is systemMessage alone. The CWD and source are now read in the same single subprocess that read the CWD alone. The [DR-10] tracker differentials prime the audit stamp before measuring, so they keep measuring Section 3 alone; the HOME-rooted-repository scaffolding checks treat the stamp as machine data beside the logs.
…amp on uninstall devflow init audits the Claude directory's CLAUDE.md and, inside a git repository that is not HOME, the project's CLAUDE.md, .claude/CLAUDE.md and CLAUDE.local.md at the toplevel. It prints what is flagged as one note after the install on both paths (the Recommended summary prints before the install and the Advanced path prints no end-of-wizard summary, so the audit is neither's row), through one pure function, claudeMdAuditStep. Init is a deliberate command, so it states every current finding even when a session already showed it; nothing is printed when nothing is flagged, and a failure is at most one degraded line with the exit code unchanged. Init then records the stamp (the examined paths and the keys it printed, with the keys earlier runs recorded kept), so the first SessionStart afterwards repeats nothing it just printed. The write needs the machine root to exist and refuses a symlinked or non-regular stamp path. Uninstall lists the stamp, and its pid-named temp file as a prefix family, in installArtifactPaths, so the artifacts-only passes remove them; the write-set fence is unchanged (init and the hook write the stamp, no toggle does).
…ew flag's probe hooks.md, init.md and working-memory.md describe Sections 5 and 6, the stamp, the systemMessage and init's after-install note. platform-assumptions.md amends the SessionStart source row (Sections 3 and 4 consume startup and clear, Section 5 consumes compact) and adds the systemMessage row (documentation-grounded; the live display was not observed) and the CLAUDE_CODE_AUTO_COMPACT_WINDOW row. The probe note records the env name found in the Claude Code 2.1.296 executable, what its strings say about precedence and range, and what was not observed, so the flag keeps no upstreamDefault. The CHANGELOG gets one bullet and the file-organization tree the two new files.
…mport audit Regenerated from the built CLI (npm run build, then npm run test:golden:update -- install-snapshot). The three install snapshots gain exactly three lines each: - .devflow/.claude-md-audit, the stamp devflow init writes after the install; - .devflow/scripts/claude-md-audit.cjs, the audit script composeScripts copies verbatim; - "auto-compact-window": null in the flags record, the opt-in flag recorded as unset. The frozen github-status-lines golden is not touched. The hook matrix changes in two cells only, both session-start-context, both as "M home/.devflow/.claude-md-audit" (modified, not added): the stamp already exists in the post-install tree, because init writes it, so a hook run rewrites it only where the gated root set differs from the one init recorded. Init ran in the repository, so the root and subdir cells (same four roots, none present) take the unchanged-start fast path and write nothing; the worktree cell (the worktree's own project roots) and the non-git cell (the global root alone) record a new root set and rewrite the stamp, with builtins only, since no CLAUDE.md exists in the sandbox. The plan expected every session-start-context cell to gain the stamp as an addition; that holds for a machine that has not run init, and the matrix starts from a post-install tree.
…escape text Comments and test titles added in this series cited learning-ledger IDs, which are machine-local; they now state the rule in words. The audit test's bidi-control and accented-letter inputs had reached the file as raw characters (a Unicode escape typed into a tool call is written as the character itself); they are escape text again, so the no-bidi guard and a plain diff see them.
…tral steps out of the Git agent The provider resolution and the tracker input contract leave the always-loaded Git agent for a new ungated reference, tracker/_contract.md, which a spawn reads once, before its first tracker step. The agent keeps a Loading the mechanics section: the PR-mechanics load rule, the merged step order and the one line that names the contract, the tool-call contract and the per-operation path. dist/agents/git.md falls from 43,814 to 35,474 characters. The provider-neutral steps of setup-task, fetch-issues-batch, gather-release-evidence and post-wave-report, and fetch-issue's marker-neutralisation reminder, are authored once in _steps.mds and expanded into the GitHub, Jira and Linear references. They sit in a module of their own because the resolver's compile cost is exponential in a module's define count and _common.mds has no room left (22 defines compiled in 4.9 s against a 1.5 s guard). Steps that other operations share, or that carry the GitHub CLI or GraphQL, stay inline. Git gains effort: medium and the shared denylist, a row in AGENT_CONFIG, and an Output section ending in a report cap that names the fields callers parse. The agent-config exemption set is the Tracker agent alone and the report-cap roster is 15. The budget model bills the contract once per spawn to every tracker row and to no PR-host row. The five git.md-carrying ceilings fall to measured + 80, the reference floors rise 47 to 48, and the status-lines sampler is retargeted with the straddle split so the frozen fixture stays byte-identical.
npm run build && npm run test:golden:update -- install-snapshot. The diff is one line in each of the all-off, github and jira-hipaa goldens: .claude/skills/devflow:git/references/tracker/_contract.md, the new provider-independent reference every install carries. The hook matrix is unchanged.
…mpare The compare derived its expected reference keys from the fanout modules, the gated tool-call contract and the cross-cutting documents; the tracker contract is a fourth, ungated source and the compare counted 95 files against 94.
The current Claude Code memory page documents imports to a maximum of four hops; older snapshots said five. The audit measures what Claude Code loads, so following a fifth hop counted bytes that are never loaded and could raise false findings. MAX_HOPS is now 4, the spec-decision note cites the source and fetch date, and the hop-bound tests prove hop 4 is counted and hop 5 is neither read nor counted.
The CLAUDE.md import audit bounds its walk at MAX_HOPS = 4, the depth the current upstream memory page documents. The hooks reference still said five.
…rning converge A --plugin init right after an upgrade copies no command, so the converge alone rewrote an installed code-review.md that predates the stamp line. With nothing to carry it installed the shipped (none) and dropped every language focus until the next full install. The converge now carries a list when the copy holds one and otherwise stamps the effective selection, as the install does.
The SessionStart hook writes the stamp under umask 077 because it names the user's project paths, but init's first write took the umask default (0644), and an unchanged start never rewrites it, so the stamp stayed world-readable. The atomic writer takes an optional creation mode for its temp file, so the bits are set before the file exists; an existing target's mode still wins.
The converge compared the shipped variant against the installed agent byte for byte. An agent carrying a `devflow agents` or proxy override differs from its shipped model and effort, so every partial init and every learning toggle, a no-op included, rewrote all nine roster agents to their shipped values and left reapplyAgentMapping to restore the overrides. The end state was right, but the "N agent(s) rewritten" line was wrong and the files sat on default models in between. carryAgentOverrides copies the installed copy's model and effort into the variant before the comparison, through the same rewriteAgentFrontmatter the reapply uses, the way the language stamp is carried into code-review. A steady-state converge with overrides now writes nothing, and a real switch writes the target variant with the overrides already on it. reapplyAgentMapping still runs after a converge that rewrote an agent and remains the authority; the carry only agrees with it.
convergeLearningVariants takes a packageRoot for the variants and the command and agent sources, but the apply-decisions skill was resolved through resolveSkillSource, which always read the running package's skillsDir(). An injected root therefore governed everything except the skill. skillsDir and resolveSkillSource take an optional package root, defaulting to the running package, and the converge passes its own through. The converge tests now seed the temp root with its own skill, so a read of the wrong root shows, and one test pins that a root without the skill fails the skill step rather than falling back to the running package.
- assets.ts: commandsDir() describes the end state, one compiled file per .mds command host - partial-count floor raised 16 to 17 (floor, pattern and the pinned literal in build-mds.test.ts together) now that _steps.mds is a partial - byte-budget.test.ts: Jira and Linear rows record the current measured loaded sets, 72,737 and 73,333, 58 ch under their unchanged ceilings
Drop the hard partial counts from the two manifest set-equality titles so they stop going stale, and rewrite the byte-budget headroom notes to state the rule (each ceiling is min(previous, measured + 80) when set, so headroom differs per row) instead of a uniform figure. No ceiling or assertion changes.
The release command is authored as src/assets/commands/release.mds and compiled to dist/commands/release.md. Correct the frontmatter directory entry and the host-command table row, and regenerate the index line from the frontmatter.
The new Step 1.1, 1.2 and 1.3 leaf headings carried no Produces or Requires line, so the phase/step annotation guard failed on them. Each now names the principal upstream state it depends on and the state it produces: the diff class and changed-path list from 1.1, the focus list from 1.2, and the diff files from 1.3. The stamp line is untouched.
…edge index line The feature-knowledge-system index line and its KB frontmatter description now say that knowledge is delivered as Rules plus a heading index. The index line stays a byte-for-byte cache of the frontmatter description (166 characters, line 294 characters).
The charter figure now states the net across the wave (2,709 to 2,683 characters, after the Feature knowledge bullet was rewritten for the knowledge reshape). The /code-review sizes are the measurements at the final head: 35,570 bytes learning on, 32,970 off, against 29,286 on main (main re-built from its tree; the earlier baseline of 29,570 was stale).
pr-create mode loads no mode skill and only opens the pull request, so the decisions index the parallel path's Phase 10 spawn carried was pure cost there. Every other Code spawn keeps the key. The seam guard exempts the pr-create spawn by its OPERATION literal and pins the exemption both ways: a pr-create spawn that carries the key is reported, and a keyless spawn that is not pr-create is reported.
writeMachineFeature rewrote manifest.json, refreshing updatedAt, even when features.<name> already held the requested boolean, so `devflow learning --enable` on an enabled machine (and the memory and knowledge toggles alike) changed the file for nothing. The shared writer now returns early when the manifest already records exactly that value. An absent key, a non-boolean value and a legacy key are not "recorded", so they are still written as the explicit boolean. The learning-variant converge is already byte-compared and is unchanged.
A selective uninstall removed the plugin's files but left its name in manifest.plugins. devflow init seeds its plugin selection from that list, so the next plain init installed the plugin again. The selective phase now takes the selected names off manifest.plugins through a raw read-modify-write that carries every other key verbatim (knownPlugins included, so the removed plugin is not adopted as new) and writes atomically. It writes nothing when the plugin was not listed or there is no usable manifest, goes through the legacy-local symlink guard, and a failed write is a warning. This predates the branch: the selective path on main never wrote the manifest.
List the full selective-uninstall sequence in file-organization.md, including the code-review language-stamp re-stamp and the step that drops the named plugins from manifest.plugins. Add the uninstall manifest fix and the no-op toggle fix to the Unreleased changelog.
Owner
Author
Test Plan Evidence — 7558785Verified 170/174: VERIFIED-CI 144, ATTESTED-LOCAL 26, UNVERIFIED 1, STALE 0, FAILED 3, INDETERMINATE 0
|
This was referenced Oct 13, 2026
Closed
dean0x
added a commit
that referenced
this pull request
Oct 15, 2026
## Summary This PR fixes all seven blocking HIGH findings from the re-review of PRs #436, #440 and #459. Each finding is named as "finding N of #486". - Finding 1 of #486: after a fast-forward merge, the wave undo could stop at an earlier commit. Part of a failed ticket then stayed on the integration branch. - Finding 2 of #486: the live tests ran claude with the skip-permissions flag, in the real repository and the real HOME. - Finding 3 of #486: the closing-line rules conflicted. - Finding 4 of #486: four KB and comment texts described the old behaviour. - Finding 5 of #486: waitForCi was hard to change safely. - Finding 6 of #486: the wave merge arm nested seven levels deep. - Finding 7 of #486: walkRoot was hard to change safely. ## Changes - Finding 1 of #486: the merge uses `--no-ff`. The integration HEAD is recorded as baseSha before the merge. The undo resets to baseSha after read-only checks. - Finding 1 of #486: the workflow halts before Validate on a bad or unchanged SHA. Each case has its own fixed reason. - Finding 6 of #486: merge, Validate and undo sit in one helper. The per-ticket loop nests no deeper than five levels below `while`. - Finding 2 of #486: the live tests use one sandboxed builder. It sets a temp home, a temp repository and a temp install. The spawns use explicit permissions. A static guard covers the spawns under tests/. - Finding 3 of #486: step 4a decides the closing line, and step 4b also publishes the Handoff line. Bare-number and branch-name closing paths are removed for all three providers. - Finding 4 of #486: four KB entries and the platform text match the new code. - Finding 5 of #486: waitForCi uses one settle helper, with a separate clock for each window. - Finding 7 of #486: walkRoot is split. Value pins cover the audit limits. - The wave merges only a local ticket branch. The workflow runs `git rev-parse --verify --quiet refs/heads/<name>` first. The code check refuses a value that is not a string, a name with a leading `#` or `-`, a name with `..`, and the names `HEAD`, `@`, `FETCH_HEAD`, `ORIG_HEAD` and `MERGE_HEAD`. The merge aborts only when it began, which means `MERGE_HEAD` exists. A merge that did not begin returns `merged` false, and the ticket is quarantined. ## Breaking Changes 1. Every wave merge creates a merge commit. Each ticket adds one merge commit to the integration history. 2. An "already up to date" merge halts the wave before Validate. Before this change, the wave continued. The halt text states why the wave stopped and what to check. 3. A malformed or equal SHA halts the wave before Validate. The halt has a fixed reason. The rejected value is never repeated. 4. The undo resets to the recorded pre-merge SHA. It does not reset to the parent of the merge commit. The undo counts as done only when HEAD equals the recorded SHA. 5. A supplied ISSUE_PR_LINK is final for GitHub, Jira and Linear. This includes `(none)`. Step 4b runs only when ISSUE_PR_LINK is absent and no wave block is admitted. It takes only the Handoff Values link line, and it refuses that line in step 4a's words when the line fails the check. It composes no line. 6. PRs from /code-review and /bug-analysis no longer get a closing line from the branch name. Jira and Linear tickets no longer get a reference from the branch name. 7. /implement with an issue number and no link emits `TRACEABILITY: DEGRADED (ambiguous issue reference)`. 8. A run with no issue number and no link writes no Related Issues heading. 9. The live integration tests no longer use the skip flag. They run in a temp home and a temp repository. They skip, and print a reason, when no credential is set. These are test-only changes. 10. A static guard fails any claude spawn under tests/ that lacks env or cwd. It also fails a spawn that uses the skip flag or bypassPermissions. 11. The dynamic-build ceilings are lowered to the new sizes. 12. A wave merges only a local ticket branch. A branch name that fails the ref check, or that is not a local branch, quarantines the ticket, and no merge runs. 13. On Jira and Linear, the PR link line always uses the default form, `Refs {KEY}-{n}`. A custom `## Reference Rendering` no longer shapes that line. It still shapes the Shipped Issues section of a release. Follow-up #511 tracks this change. The maintainer accepted it. ## Deviations 1. A true fast-forward merge is refused. It is not undone. A `--no-ff` merge is the covered case. 2. waitForCi stays at 117 lines and depth 6. Finding 5 of #486 states 115 lines and depth 5. This PR does not improve those values. The fix is one shared settle comparison. 3. init always registers the capture hooks. The spawn-context builder removes them from the temporary settings. Therefore, only the memory-hook assertion is a real check. 4. The afterAll hook of the live-file test disposes its resources without an explicit finally block. createSpawnContext cleans up its own failures. 5. A test title in tests/setup/isolate-env.test.ts is corrected. The static guard also refuses bypassPermissions under tests/integration. 6. To fit the ceiling, which has no spare room, the fence comments in src/assets/commands/dynamic-build.mds were condensed by about 400 bytes. This is in addition to the planned cut. The two dynamic-build ceilings are lowered to 106,923 and 103,536. After the base update, the measured sizes are 106,828 and 103,441. The ceilings are not changed. ## Validation - Head: 3304490. The base update merges main 8875e09 (merge 7eaaf44). - Full Validate at 8bef0ce: build PASS, tsc PASS, integration 51 of 51, and unit 12,630 passed with 17 failed in 9 files. All 9 files pass when run alone. The failures are load timeouts, plus two load assertions in the redact-secrets and model-discovery tests. This PR does not touch those tests. - Targeted tests at the fix commits all pass. - CI at 3304490: build-and-test (22) pass, macos-bash32-hooks pass, security/snyk pass. - CI is the binding full-suite gate. ## QA - 73 checks in total: 63 passed, 2 failed and 8 were skipped. - The two failed checks are not code defects. - The check for the RED proof on the unedited undo fails earlier than planned. The current test first requires the recorded base SHA. The planned RED output is in the #486 issue comment. - The check for planning IDs in the PR text fails only on the test-plan block at the end of this body. The evidence contract requires the IDs in that block. - The eight skipped checks: - Five need a token. - Two check the PR body. They ran before the PR existed. - One checks the full suite. Validate runs that check. ## Not verified: deny-rule binding for subagents and the live proof (no token) - The deny-rule probe did not run. The live run did not run. No token was available. - The platform-assumptions row records the deny-rule binding as UNVERIFIED. - The permission design stays unproven until a run with a token. - Dean decides at merge. live proof not run: no token ## Reviewer Focus Areas - The deny-rule binding for subagents. It is UNVERIFIED. See the section above. - The two halt reasons. Check that the already-up-to-date text says what to do next. - The three halt kinds: `red`, `bad-sha` and `unchanged`. Each kind has fixed text. Check each text against its case. - The closing-line rule. Check the case with no issue number and no link. That case writes no heading. - The real-git test. Check its RED output and its GREEN run. - The byte table. Both dynamic-build variants must stay under their ceilings. - The platform wording, reproduced here for review: > Where a test needs live `claude` sessions, the test is opt-in, runs in a temp HOME and a temp repository, and uses an explicit permission mode and deny list instead of the flag (`tests/integration/subagent-skill-preload.test.ts` is excluded at glob time rather than by a command-line path). ## Disputed findings None. The wording of finding 7 of #486 is corrected: walkRoot is a function of 94 lines with a loop of 55 lines. No count is disputed. ## Follow-ups - #496: token-audit splits, after #492. - #498: closing keywords in pasted fields. - #499: proof-based non-closing reference. - #500: stale header in the frozen golden test. - #506: a throw after a merge leaves the unvalidated merge. - #509: the deny-rule probe, then scope the reads. - #510: the wave merge-conflict doctrine compared with the skeleton. - #511: apply the Reference Rendering gate to the Handoff PR link line (Jira, Linear). - Known gap, not changed here: when a Validate or undo spawn throws after a merge, the ticket is quarantined. The unvalidated merge stays on the integration branch. ## Related Issues Closes #486 <!-- devflow:test-plan --> ## Test Plan - [x] TP-1 (AC-7) Dynamic-build and resolve sizes are recorded before the first fence edit — method:local [files: dist/commands/dynamic-build.md] - [x] TP-2 (AC-1) Merge prompt reads baseSha before a no-ff merge, aborts on conflict, and returns baseSha last — method:ci [files: tests/build-mds.test.ts] - [x] TP-3 (AC-1) A failed abort returns merged true with no mergeSha, and the wave halts — method:ci [files: tests/dynamic/wave-flow.test.ts] - [ ] TP-4 (AC-1) Doctrine step one says Git aborts the merge and reports the conflicting files — method:local [files: src/assets/commands/_partials/_wave.mds] - [x] TP-5 (AC-2) A bad baseSha with merged true halts before Validate or undo, with null SHA fields — method:ci [files: tests/dynamic/wave-flow.test.ts] - [x] TP-6 (AC-2) A rejected SHA value never appears in the halted record, the reason or the report — method:ci [files: tests/dynamic/wave-flow.test.ts] - [x] TP-7 (AC-3) Undo collector pins the symbolic HEAD, branch head, caret and baseSha reset checks — method:ci [files: tests/build-mds.test.ts] - [x] TP-8 (AC-3) Known-bad probes with a caret reset, a hard reset or a push are refused — method:ci [files: tests/build-mds.test.ts] - [x] TP-9 (AC-4) A head equal to baseSha counts as undone, and any other or malformed head halts — method:ci [files: tests/dynamic/wave-flow.test.ts] - [x] TP-10 (AC-5) Base A and ticket commits B1 and B2: HEAD returns to A after a fast-forward merge, with B1 unreachable — method:ci [files: tests/dynamic/undo-real-git.test.ts] - [ ] TP-11 (AC-5) The same test on the unedited build fails with HEAD on B1, and that output is recorded before the fix — method:local [files: tests/dynamic/undo-real-git.test.ts] - [x] TP-12 (AC-5) A detached HEAD and a conflicting local change report not undone, and a dirty file survives — method:ci [files: tests/dynamic/undo-real-git.test.ts] - [ ] TP-13 (AC-5) The real-git test sets its own identity, and the phase 2a fallback is gone after GREEN — method:local [files: tests/dynamic/undo-real-git.test.ts] - [x] TP-14 (AC-6) No per-ticket loop line sits more than five levels below while, and a seeded probe fails — method:ci [files: tests/build-mds.test.ts] - [x] TP-15 (AC-6) Verdict check stays the first if after the engine call, and the merge name and prompt starts stay — method:ci [files: tests/dynamic/wave-flow.test.ts] - [x] TP-16 (AC-6) Frozen anchors stay above the compose step and appear once, and the condensed partial compiles — method:ci [files: tests/guards/retired-wording.test.ts] - [x] TP-17 (AC-7) Lowered ceilings match the json rows and test literals, each pattern occurs once, and no ceiling rises — method:ci [files: tests/guards/command-size.test.ts] - [x] TP-18 (AC-8) Builder home and cwd are realpaths under a symlinked temp parent, and home passes the temp check — method:ci [files: tests/integration-spawn-context.test.ts] - [x] TP-19 (AC-8) Temp repository prints no remote, copies no git or devflow directory, and sits outside the checkout — method:ci [files: tests/integration-spawn-context.test.ts] - [x] TP-20 (AC-9) Environment holds the OAuth token first, the API key only as fallback, and HOME as the temp home — method:ci [files: tests/integration-spawn-context.test.ts] - [x] TP-21 (AC-9) With no credential the builder yields a printed skip reason, not no-session-dir, and shows set or not set only — method:ci [files: tests/integration-spawn-context.test.ts] - [x] TP-22 (AC-10) Both spawn argument lists pin dontAsk, the per-spawn tool, the six-tool deny list and empty strict MCP — method:ci [files: tests/integration-spawn-context.test.ts] - [x] TP-23 (AC-10) The token before the prompt is -p and never a variadic flag value, in both argument lists — method:ci [files: tests/integration-spawn-context.test.ts] - [x] TP-24 (AC-11) Transcript lookup with the builder home and cwd resolves the encoded real project directory — method:ci [files: tests/integration/helpers.test.ts] - [x] TP-25 (AC-12) Init into the temp home installs the Validate and Test agents and no memory or learning hook — method:ci [files: tests/integration-spawn-context.test.ts] - [x] TP-26 (AC-12) Cleanup removes both temp directories after a midway throw, and the beforeAll and init timeouts are explicit — method:ci [files: tests/integration-spawn-context.test.ts] - [x] TP-27 (AC-13) The guard finds both claude spawns with env and cwd, and no skip flag or bypass mode — method:ci [files: tests/guards/integration-spawn.test.ts] - [x] TP-28 (AC-13) Seeded probes lacking env, cwd or the skip flag fail, and the eager memory argv literals do not match — method:ci [files: tests/guards/integration-spawn.test.ts] - [x] TP-29 (AC-13) A seeded init spawn without env or cwd in an integration file fails the guard — method:ci [files: tests/guards/integration-spawn.test.ts] - [ ] TP-30 (AC-14) The probe with Bash allowed and denied in one run leaves probe-marker absent when deny rules bind — method:manual - [ ] TP-31 (AC-14) The probe records whether dontAsk works with print mode and whether a denied tool ends or stalls the turn — method:manual - [ ] TP-32 (AC-14) The probe makes at most two attempts, each with HOME and cwd set after a realpath check — method:manual - [ ] TP-33 (AC-14) Both live files run once under the lock with the opt-in set, and the outcome is pass, fail, or not run with no token — method:manual [files: tests/integration/subagent-skill-preload.test.ts] - [x] TP-34 (AC-15) No skip-flag claim and no developer-config claim remain in the four stale texts — method:local [files: docs/reference/platform-assumptions.md] - [x] TP-35 (AC-15) Doc reader tests stay green after the platform text edit — method:ci [files: tests/agent-name-guards.test.ts] - [x] TP-36 (AC-15) Review confirms the hook line holds no allow or deny judgment — method:manual [files: docs/reference/platform-assumptions.md] - [x] TP-37 (AC-16) A supplied none value is final with no reference, and the words or an absent block are gone from step 4a — method:ci [files: tests/seams/pr-link-handoff.test.ts] - [x] TP-38 (AC-17) Step 4b requires an absent Handoff block and no admitted wave block, with one writer per path — method:ci [files: tests/seams/pr-link-handoff.test.ts] - [x] TP-39 (AC-17) With ISSUE_NUMBER and ISSUE_PR_LINK both none, the built text writes no Related Issues heading — method:ci [files: tests/seams/pr-link-handoff.test.ts] - [x] TP-40 (AC-18) No provider step 4b composes a closing line from a bare number or a branch name, and seeds are refused — method:ci [files: tests/seams/pr-link-handoff.test.ts] - [x] TP-41 (AC-18) Issue keys stay out of every Code spawn, and the agent input seam tests stay green — method:ci [files: tests/seams/command-agent-input.test.ts] - [x] TP-42 (AC-19) The degraded line appears when ISSUE_NUMBER is set and no line is accepted, never for none or empty — method:ci [files: tests/seams/pr-link-handoff.test.ts] - [x] TP-43 (AC-20) Every fence passing ISSUE_NUMBER also passes ISSUE_PR_LINK, and at least one fence is found — method:ci [files: tests/seams/pr-link-handoff.test.ts] - [ ] TP-44 (AC-20) The same fence check passes on the unedited base, so it is a characterization, not RED — method:local [files: tests/seams/pr-link-handoff.test.ts] - [ ] TP-45 (AC-21) Byte budget rows show zero net growth, the PR-open floor holds, and references stay within baselines — method:ci [files: tests/tracker/byte-budget.test.ts] - [x] TP-46 (AC-22) Five settle cases pass on the unedited base, including the 90000 ms boundary and 89999 ms — method:local [files: tests/evidence/ci-wait.test.ts] - [x] TP-47 (AC-23) Characterization and window tests pass unedited after the helper refactor — method:ci [files: tests/evidence/ci-wait.test.ts] - [x] TP-48 (AC-23) Exports, constants, source scan pins and callers stay unchanged — method:ci [files: tests/evidence/merge-readiness.test.ts] - [x] TP-49 (AC-24) Value pins for the five audit limits pass on the unedited base, and the byte self-comparison is gone — method:local [files: tests/core/claude-md-audit.test.ts] - [x] TP-50 (AC-25) Audit cases, truncation branches and hop order give identical output after the split — method:ci [files: tests/core/claude-md-audit.test.ts] - [x] TP-51 (AC-25) walkRoot is at most sixty lines, only it writes the context, and no members destructure remains — method:ci [files: src/assets/scripts/claude-md-audit.cjs] - [ ] TP-52 (AC-26) The PR body and issue comment show function 94 lines and loop 55 lines — method:manual - [ ] TP-53 (AC-27) The compliance KB names the install-time stamp gate and has no run-time probe wording — method:local [files: .devflow/features/compliance-feature/KNOWLEDGE.md] - [ ] TP-54 (AC-28) The waves KB has no caret one rule, and names the baseSha rule and the verify-evidence publish step — method:local [files: .devflow/features/dynamic-workflow-engine-waves/KNOWLEDGE.md] - [x] TP-55 (AC-29) The tracker-guards KB is at or below 39925 characters and has no Phase 10b text — method:local [files: .devflow/features/test-harness-tracker-guards/KNOWLEDGE.md] - [x] TP-56 (AC-30) The prompt-install exception row no longer needs the flag and names the hook as a pointer only — method:local [files: .devflow/features/test-harness-prompt-install/KNOWLEDGE.md] - [ ] TP-57 (AC-31) Rule IDs equal the base, updated is set, and each edited KB is at or below 40000 characters — method:local [files: .devflow/features/*/KNOWLEDGE.md] - [x] TP-58 (AC-32) The git agent golden is byte-equal, the install snapshot stays green, and the frozen status file is unchanged — method:ci [files: tests/install-snapshot.test.ts] - [ ] TP-59 (AC-33) Validate runs both full suites under the heavy tests lock, and both are green — method:local [files: tests/**/*.test.ts] - [ ] TP-60 (AC-34) The PR text shows the behaviour changes, the follow-ups, the platform wording and no planning IDs — method:manual - [x] TP-61 (AC-35) The ref_preflight_branch define, its export and its shared rules row are removed, and no tracker text calls it — method:ci [files: tests/provider-literals.test.ts] - [x] TP-62 (AC-36) The settle comparison appears once in the helper, and the helper is called once per window — method:ci [files: src/assets/scripts/ci-wait.cjs] - [x] TP-63 (AC-37) Without the opt-in both live files are excluded, and no live spawn or init runs — method:ci [files: vitest.integration.config.ts] - [x] TP-64 (AC-37) With the opt-in set to 1, true or yes, both live files are collected — method:ci [files: vitest.integration.config.ts] - [x] TP-65 (AC-38) Frozen anchor lines are recorded before the first prompt edit, and each prompt keeps their order — method:local [files: src/assets/mds/git/_ops_steps.mds] - [ ] TP-66 (AC-39) No planning ID appears in committed text or commit messages, and repository rule IDs are unchanged — method:local - [ ] TP-67 (AC-40) Each Code brief names only its phase's test files, and only Validate runs the full suite — method:manual - [ ] TP-68 (AC-40) Every targeted command in the briefs names its test files and removes both credential variables — method:local - [ ] TP-69 (AC-41) The PR body has a Disputed findings section that reads none unless disputed with evidence — method:manual - [x] TP-70 (AC-42) The already-up-to-date halt shows a fixed reason that says what to do next and echoes no SHA — method:ci [files: tests/dynamic/wave-flow.test.ts] - [x] TP-71 (AC-43) Each new platform fact row carries the date 2026-10-14 and a drift symptom — method:local [files: docs/reference/platform-assumptions.md] - [ ] TP-72 (AC-44) The probe and live results reach the resume file and one issue comment, with no new issue filed — method:manual - [x] TP-73 (AC-45) Init runs in a temp directory that is not a repository, and checkout status is equal before and after — method:ci [files: tests/integration-spawn-context.test.ts] <!-- /devflow:test-plan -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Token Diet wave 3, delivered as one change (#425 to #429) in integration order. A learning-off machine carries 19,286 B less across the 14 compiled commands, the always-loaded Git agent is 8,432 B smaller, and knowledge, diffs and session plumbing reach agents in narrower forms. Learning-on commands are larger than main (+26,550 B): the learning arms and the
/code-reviewrules cost more than the prompts shed. The byte table below states both sides.#426: settings hoist and learning-off variants. The settings line is resolved once per command: main had 12 settings invocations across 8 commands, HEAD has exactly one in each of the 14. Every decisions read is gated on it. The build writes a learning-off variant of every command and agent that carries decisions text (23 files under
dist/learning-off/),devflow initinstalls the variant that matches the machine's learning switch, anddevflow learning --enable/--disableconverges what is already installed, atomically and keepingdevflow agentsoverrides. A learning-off machine gets no decisions index, noDECISIONS_CONTEXTpass and noapply-decisionspreload or skill. Passes now reach only agents whose contract declares them, and because Code no longer reads the index itself, commands pass it to every Code spawn except pr-create, which gets none; moving PR creation to the Git agent is tracked in #441.releaseand nine agents (code, design, diagnose, knowledge, research, review, scrutinize, skim, triage) are now MDS sources, and the decisions pass rule moved from the orchestrator charter to one line in the session-start decisions block.#427: knowledge reshape. A KB leads with a
## Rulessection of one-line bullets under stableKB-AP-n/KB-INV-nIDs, curated to a 30,000-character target and a 40,000-character ceiling.knowledge_loadhands each agent one to three relevant bullets per KB plus its path and a heading index, instead of the whole file; a KB with no Rules section falls back to a few Anti-Patterns or Gotchas entries./releaseloads through the same delivery. A new core rule,context-economy, tells every session to list a large file's headings before reading ranges and to count matches before printing them. Each Code phase appends its own handoff section (at most 8,192 bytes) and the next phase reads only the one before it. Validate lists the quiet form of each build, typecheck and test command, with a verbose re-run of only the failing test.#427 measurements:
context-economyrule length: the file is 428 characters (432 B). Its body, counted astests/rules.test.tscounts it (after the frontmatter, trimmed), is 409 characters against the 400 to 450 bound.devflow init --recommendedunder a fresh temporary HOME installs~/.claude/rules/devflow/context-economy.mdbyte-identical tosrc/assets/rules/context-economy.md, with frontmatterpaths: [], alongside the four other core rules.docs(knowledge)commits on this branch (9a79bd8 refreshed the KBs; c586d00 states Rules-plus-index delivery in the index line and the KB's description, 288 characters on the line against the 300 cap).apply-feature-knowledgeis 1,584 B against its 2,200 B cap (tests/guards/code-preload.test.ts), 616 B of headroom.#428:
/code-reviewgating. Phase 1 classifies each worktree's diff as code, docs-only, tests-only or lockfile-only. Reduced classes run reduced focus sets: docs-only runs documentation, consistency and security; tests-only runs testing, reliability, consistency and security; lockfile-only runs dependencies and security; a mixed non-code diff runs the union. The orchestrator writesdiff.patchonce per worktree and adiff-{focus}.patchper language focus, and each Review agent reads its file throughDIFF_FILEinstead of running its own git. The language gate is no longer a run-timetest -fprobe: the installer stamps the installed language focuses intocode-review.mdon every install and re-stamps onuninstall --plugin./dynamic-buildis unchanged.#429: session plumbing. A
compactSessionStart adds one fixed 249-character resume directive (re-read the running command's file, resume after the last finished phase).devflow flagsgains an opt-inauto-compact-windowflag (CLAUDE_CODE_AUTO_COMPACT_WINDOW, 100000 to 1000000, unset by default). The context hook anddevflow initaudit the@pathimports of the Claude directory'sCLAUDE.mdand, in a git project, of itsCLAUDE.md,.claude/CLAUDE.mdandCLAUDE.local.md. An import over 10,000 bytes, or a chain over 40,000 bytes, is named once in a user-visible message that never enters the model's context. A stamp keeps an unchanged start to shell builtins, anddevflow uninstallremoves it. The audit follows four hops (see the decision below).#425: Git agent split. The provider resolution and the tracker input contract moved out of the always-loaded Git agent into a generated reference,
references/tracker/_contract.md, which a spawn that runs a tracker operation reads once. The provider-neutral steps ofsetup-task,fetch-issues-batch,gather-release-evidenceandpost-wave-reportmoved into the three providers' references, each authored once.git.mdfalls from 44,131 B (43,814 characters) to 35,699 B (35,474 characters); a PR-host spawn carries 8,340 fewer characters and the worst tracker spawn on each provider about 540 fewer. The agent also gainseffort: medium, the shared tool denylist and an## Outputsection whose report cap names the fields callers parse.#426: pr-create spawn.
/implement's pr-create Code spawn no longer receives the decisions index; moving PR creation to the Git agent is tracked in #441.Uninstall fix.
devflow uninstall --pluginnow removes the plugin frommanifest.plugins, so a laterdevflow initno longer reinstalls it. This was a pre-existing bug on main.#426: no-op toggles. A
devflow learning,memoryorknowledgetoggle to the value already set no longer rewrites the manifest.Feature KB curation. Feature KBs are curated to the 40,000-character ceiling with a leading Rules section: the 11 KBs (9 over the ceiling) are now 41 focused KBs, none over it, and every index line is at most 300 characters.
Related Issues
Closes #425
Closes #426
Closes #427
Closes #428
Closes #429
Refs #417
Maintainer decisions taken during the build
Proofs
release.mdsconversion iscmp-identical to the hand-authoredrelease.mdat the conversion commit (recorded as TP-9).cmp-identical.diagnosediffers only by one dropped trailing blank line, which MDS trims; accepted._engine,_waveordynamic-build. The only commits on this branch that do are the two Settings hoist and learning-off decisions gate #426 commits.Byte table
Measured at head 7558785 against main 8a49b23. Both trees came from
git archivewith an identical lockfile andnpm run build:mdsin each. "on" isdist/commandsanddist/agents; "off" isdist/learning-off, falling back to the on file where no variant exists. A hand-authored agent is read fromsrc/assets/agents.Commands (bytes)
code-reviewandreleaseare larger than main even with learning off.Changed agents (bytes)
Unchanged: learning, simplify, synthesize, test, tracker.
Skills (SKILL.md bytes)
Fixed budgets
The preload is git, testing, test-driven-development, worktree-support, apply-feature-knowledge and apply-decisions, summed as in
tests/guards/code-preload.test.ts.Decisions index no longer passed per run. The count of removed pass sites was 13 as counted in the #426 build (plan 3, dynamic-plan 2, dynamic-build 1, dynamic-tickets 1, research 1, the four dynamic hosts' authoring-step receiver lists 4, release 1): 13 x 19,452 B = 252,876 B gross. The sites are prose, receiver lists and prompt lines, so no single rule recounts them; the figure is cited as counted. A line-level recount of spawn-prompt carry lines in the compiled learning-on commands finds 24 on main and 31 on HEAD: dynamic-plan loses 2, and the commands now pass the index to Code's spawns other than pr-create (dynamic-build +5, implement +3, resolve +1). So 252,876 B is the gross figure, and a run that reaches those spawns pays the index again there. On a learning-off machine no command carries the index: every off variant has 0 carry lines.
Ratchets moved
From
git diff main...HEAD -- tests/fixtures/numeric-floors.json:OQ9: upstream import-rule findings
CLAUDE.local.mdis in scope.Accepted deviations
git.mdis about 35.5K, not the ticket's ~32K: about 1.5K stays inline for guards, and the PR-only spawn saves 8,340 characters, not the ticket's ~11.8K._steps.mdswas added to keep compile time down.M, because init writes the stamp./code-reviewdiff classes.Post-merge checks from real usage
/code-reviewper-class spawn counts.MDS migration
Tracked in dean0x/mdscript#452 and #439.
Test Plan
Verified 170/174: VERIFIED-CI 144, ATTESTED-LOCAL 26, UNVERIFIED 1, STALE 0, FAILED 3, INDETERMINATE 0 (counts only: the TP lines exceed the PR body limit)