Skip to content

feat: token diet wave 3 — settings hoist, learning-off variants, knowledge reshape, /code-review gating, session plumbing, Git split (#425–#429) - #440

Merged
dean0x merged 53 commits into
mainfrom
feat/token-diet-wave-3
Oct 10, 2026
Merged

dean0x merged 53 commits into
mainfrom
feat/token-diet-wave-3

Conversation

@dean0x

@dean0x dean0x commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

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-review rules 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 init installs the variant that matches the machine's learning switch, and devflow learning --enable/--disable converges what is already installed, atomically and keeping devflow agents overrides. A learning-off machine gets no decisions index, no DECISIONS_CONTEXT pass and no apply-decisions preload 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. release and 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 ## Rules section of one-line bullets under stable KB-AP-n / KB-INV-n IDs, curated to a 30,000-character target and a 40,000-character ceiling. knowledge_load hands 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. /release loads 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-economy rule length: the file is 428 characters (432 B). Its body, counted as tests/rules.test.ts counts it (after the frontmatter, trimmed), is 409 characters against the 400 to 450 bound.
  • Temp-HOME install: devflow init --recommended under a fresh temporary HOME installs ~/.claude/rules/devflow/context-economy.md byte-identical to src/assets/rules/context-economy.md, with frontmatter paths: [], alongside the four other core rules.
  • Final KB re-read: done. The feature-knowledge-system KB and its index line were refreshed in 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-knowledge is 1,584 B against its 2,200 B cap (tests/guards/code-preload.test.ts), 616 B of headroom.

#428: /code-review gating. 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 writes diff.patch once per worktree and a diff-{focus}.patch per language focus, and each Review agent reads its file through DIFF_FILE instead of running its own git. The language gate is no longer a run-time test -f probe: the installer stamps the installed language focuses into code-review.md on every install and re-stamps on uninstall --plugin. /dynamic-build is unchanged.

#429: session plumbing. A compact SessionStart adds one fixed 249-character resume directive (re-read the running command's file, resume after the last finished phase). devflow flags gains an opt-in auto-compact-window flag (CLAUDE_CODE_AUTO_COMPACT_WINDOW, 100000 to 1000000, unset by default). The context hook and devflow init audit the @path imports of the Claude directory's CLAUDE.md and, in a git project, of its CLAUDE.md, .claude/CLAUDE.md and CLAUDE.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, and devflow uninstall removes 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 of setup-task, fetch-issues-batch, gather-release-evidence and post-wave-report moved into the three providers' references, each authored once. git.md falls 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 gains effort: medium, the shared tool denylist and an ## Output section 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 --plugin now removes the plugin from manifest.plugins, so a later devflow init no longer reinstalls it. This was a pre-existing bug on main.

#426: no-op toggles. A devflow learning, memory or knowledge toggle 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

  • Write-set fence. The learning-variant roster counts as one allowlist entry, so the fence ceiling goes 11 to 12. Authorised 2026-10-10. It is the only ceiling raised in this PR.
  • Import audit depth. The CLAUDE.md import audit follows four hops, per the current upstream memory docs (https://code.claude.com/docs/en/memory), not the ticket's five.
  • AC-214 re-scoped. The plan routes every series' KB corrections through the Knowledge agent, so this branch refreshes ten KBs. AC-214 now covers only the feature-knowledge-system delivery passage and its index line, which describe Rules-plus-index delivery; the other KB refreshes are the plan's per-series corrections. The criterion's legend row and the TP-65 scenario in the test plan below carry the new wording.

Proofs

  • The release.mds conversion is cmp-identical to the hand-authored release.md at the conversion commit (recorded as TP-9).
  • Eight of the nine agent conversions are cmp-identical. diagnose differs only by one dropped trailing blank line, which MDS trims; accepted.
  • The frozen github-status-lines golden fixture is unchanged, so no re-capture was needed and no residual lines remained. Only the sampler in its test was retargeted.
  • The engine-isolation decision holds: /code-review diff-class gating and diff files #428 never touches _engine, _wave or dynamic-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 archive with an identical lockfile and npm run build:mds in each. "on" is dist/commands and dist/agents; "off" is dist/learning-off, falling back to the on file where no variant exists. A hand-authored agent is read from src/assets/agents.

Commands (bytes)

command main HEAD on HEAD off
bug-analysis 21,863 24,796 20,908
code-review 29,286 35,570 32,970
debug 18,860 19,211 16,432
dynamic-build 106,577 105,869 102,726
dynamic-plan 34,475 36,023 31,841
dynamic-profile 14,432 16,139 12,591
dynamic-tickets 38,843 40,538 36,499
explore 13,181 13,572 10,769
implement 57,330 58,174 55,415
plan 36,329 38,034 35,131
release 17,960 22,554 19,506
research 11,282 14,258 10,224
resolve 53,420 53,873 50,750
self-review 15,082 16,859 13,872
total 468,920 495,470 (+26,550) 449,634 (-19,286)

code-review and release are larger than main even with learning off.

Changed agents (bytes)

agent main HEAD on HEAD off
code 23,937 24,463 24,032
design 5,634 5,680 4,927
diagnose 11,075 11,151 10,631
evaluate 4,356 4,407 4,407
git 44,131 35,699 35,699
knowledge 7,048 7,925 7,459
research 5,941 6,038 5,532
review 12,132 12,444 11,645
scrutinize 5,233 5,304 4,680
skim 7,290 7,544 6,423
triage 9,814 9,893 9,109
validate 5,388 5,867 5,867
total 141,979 136,415 (-5,564) 130,411 (-11,568)

Unchanged: learning, simplify, synthesize, test, tracker.

Skills (SKILL.md bytes)

skill main HEAD
apply-decisions 2,427 2,307 (-120)
apply-feature-knowledge 1,528 1,584 (+56)
feature-knowledge 14,340 17,282 (+2,942)
quality-gates 4,353 4,444 (+91)

Fixed budgets

measure main HEAD limit
orchestrator charter 2,709 chars / 2,731 B 2,683 chars / 2,699 B 3,072 chars
Code preload, six SKILL.md files 25,578 B 25,514 B (-64) 25,646 B (132 B headroom)
git.md 44,131 B / 43,814 chars 35,699 B / 35,474 chars 35,554 chars (budget-git-md)

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:

  • Floors up: dist-host-count 13 to 14; partial-count 16 to 17; the four reference floors 47 to 48 (generated-reference-manifest-size, packed-reference-manifest-size, installed-reference-count-github, installed-reference-count-provider); report-cap-roster-count 14 to 15.
  • Ceilings down (the five budgets): budget-git-md 43,912 to 35,554; budget-loaded-set 62,973 to 62,433; budget-loaded-set-jira 73,335 to 72,795; budget-loaded-set-linear 73,931 to 73,391; budget-loaded-set-pr-host 56,301 to 47,961.
  • New: compact-directive-max-chars 250.
  • The one authorised raise: write-fence-allowlist-size 11 to 12.

OQ9: upstream import-rule findings

  • Imports follow four hops, per https://code.claude.com/docs/en/memory. The ticket assumed five.
  • CLAUDE.local.md is in scope.
  • Code spans and fences are skipped.
  • Files over 4 MiB are skipped.
  • Escaped-space paths are not followed.

Accepted deviations

  • git.md is 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.mds was added to keep compile time down.
  • Two hook-matrix cells are M, because init writes the stamp.
  • The compact directive is 249 characters.
  • Manifests and build files classify as code in the /code-review diff classes.

Post-merge checks from real usage

  • AC-525: Git PR-only and tracker spawns.
  • /code-review per-class spawn counts.
  • Re-measure around 2026-10-24.

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)

dean0x added 30 commits October 10, 2026 01:52
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.
dean0x added 18 commits October 10, 2026 10:57
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.
@dean0x

dean0x commented Oct 10, 2026

Copy link
Copy Markdown
Owner Author

Test Plan Evidence — 7558785

Verified 170/174: VERIFIED-CI 144, ATTESTED-LOCAL 26, UNVERIFIED 1, STALE 0, FAILED 3, INDETERMINATE 0

  • TP-1 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:8066fcf92995
  • TP-2 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:bfe898c6bb84
  • TP-3 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:22015ebb872d
  • TP-4 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:a86576cc0d99
  • TP-5 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:35621f381b83
  • TP-6 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:402280c864b8
  • TP-7 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:f777fd612145
  • TP-8 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:a56f85ac2d0f
  • TP-9 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:c71098ee242f
  • TP-10 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:1a37c9015c4b
  • TP-11 FAILED sha:7558785d9f25571ae66d1879faa74121481185eb out:FAIL exit:1 h:e35bb4ba39df
  • TP-12 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:240ac4983fa4
  • TP-13 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:1d0e9e01251f
  • TP-14 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:e70c3ff71fef
  • TP-15 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:9f799d80d94c
  • TP-16 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:65f5d842d9ea
  • TP-17 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:ee0ce28284f4
  • TP-18 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:252047acbec8
  • TP-19 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:47dd4956609e
  • TP-20 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:1055e1e44f4c
  • TP-21 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:6279eb13d872
  • TP-22 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:b276d29dfcac
  • TP-23 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:0b84ef8b42f4
  • TP-24 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:c3e130550809
  • TP-25 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:78ec564ddac0
  • TP-26 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:18e65650ada9
  • TP-27 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:39d6900577e2
  • TP-28 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:467cc17b69d2
  • TP-29 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:e601621ddcde
  • TP-30 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:6c4acbc4cea8
  • TP-31 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:823b1d045071
  • TP-32 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:eccb7d63340f
  • TP-33 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:8548227b4b3f
  • TP-34 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:379e554c96b2
  • TP-35 FAILED sha:7558785d9f25571ae66d1879faa74121481185eb out:FAIL exit:1 h:b91417458e07
  • TP-36 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:3a8bf69101a1
  • TP-37 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:7cfa7568ceb2
  • TP-38 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:92ad8e411e00
  • TP-39 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:0fc3713c07a5
  • TP-40 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:c69c540338ec
  • TP-41 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:5fa14a265575
  • TP-42 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:60d381d04ae3
  • TP-43 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:303ee3966c16
  • TP-44 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:ba025e00da3d
  • TP-45 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:07af24a88038
  • TP-46 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:77d95fed63cb
  • TP-47 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:0b2230ced83f
  • TP-48 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:e803482a1127
  • TP-49 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:6095b571489f
  • TP-50 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:44608d2d9404
  • TP-51 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:b0957632e273
  • TP-52 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:de5cc48c7ee4
  • TP-53 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:c3f0bfe648b0
  • TP-54 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:7992a9db107a
  • TP-55 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:2f2c47e2eeb3
  • TP-56 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:794ee3be5311
  • TP-57 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:ca5cc0c6f58c
  • TP-58 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:6d312f9803c6
  • TP-59 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:293d148c7081
  • TP-60 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:0d50779dc8f1
  • TP-61 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:154df152a396
  • TP-62 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:3e0943edad95
  • TP-63 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:7d4a0c2c6f4d
  • TP-64 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:22c900efba5c
  • TP-65 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:55578afc69f9
  • TP-66 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:a5316c118b61
  • TP-67 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:2f003c573c16
  • TP-68 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:2215a2138a09
  • TP-69 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:9cbc92437d8f
  • TP-70 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:2107a7ee175b
  • TP-71 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:6783c6e0e0f6
  • TP-72 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:00992432ad43
  • TP-73 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:a5d5650d1aca
  • TP-74 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:3bffe06e08a5
  • TP-75 FAILED sha:7558785d9f25571ae66d1879faa74121481185eb out:FAIL exit:1 h:d3f33e5473a0
  • TP-76 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:b69b443de472
  • TP-77 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:df39ddf99c94
  • TP-78 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:1dec418b590b
  • TP-79 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:08aef9bafcf8
  • TP-80 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:260d92d90ac8
  • TP-81 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:0cb6f5cefb78
  • TP-82 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:9840ccaedbf1
  • TP-83 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:36b50efd3912
  • TP-84 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:e8867b70956d
  • TP-85 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:5be2d54f0a61
  • TP-86 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:7c5345ef870e
  • TP-87 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:1bf7c8adddc9
  • TP-88 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:2ef3832de37b
  • TP-89 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:c2f3d47fc8f0
  • TP-90 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:27d48056de2c
  • TP-91 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:4e82e959e5b5
  • TP-92 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:769cde49091e
  • TP-93 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:5ffe5a789456
  • TP-94 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:6b07e3f32bf6
  • TP-95 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:196ddb148c54
  • TP-96 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:1beeb1889a08
  • TP-97 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:17319ece687c
  • TP-98 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:73a3067eea66
  • TP-99 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:4a0e33c0a32d
  • TP-100 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:bd499d662e81
  • TP-101 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:17e7b91e974d
  • TP-102 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:865945761354
  • TP-103 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:442fe31bb4ef
  • TP-104 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:c651ea5f4133
  • TP-105 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:57700df8991c
  • TP-106 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:26c71e53aa51
  • TP-107 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:0e07014edeb6
  • TP-108 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:665ae7be663f
  • TP-109 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:3a560d966bf9
  • TP-110 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:319783aaa9b4
  • TP-111 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:232ff252aa1f
  • TP-112 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:e01b572f44ca
  • TP-113 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:10e6ace0627f
  • TP-114 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:68e76f75c249
  • TP-115 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:07ab42919d12
  • TP-116 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:d2bf7a8718e9
  • TP-117 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:6176a7c4ca1b
  • TP-118 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:631661cda8f1
  • TP-119 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:f262dd2d88d9
  • TP-120 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:2072e5c3b22b
  • TP-121 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:d295297c576d
  • TP-122 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:2933a95ad636
  • TP-123 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:e058d9e2d2f7
  • TP-124 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:2a90b9dd6162
  • TP-125 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:e8c8f2d0022a
  • TP-126 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:9a9ba23adc71
  • TP-127 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:754ff5ad8b0b
  • TP-128 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:e4270af322b4
  • TP-129 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:72a013ef31a6
  • TP-130 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:8d21752109d4
  • TP-131 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:336632c46e8f
  • TP-132 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:628a861cff7c
  • TP-133 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:907eebb65842
  • TP-134 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:cebdf84894a7
  • TP-135 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:d747a7bf2729
  • TP-136 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:753e1d70a826
  • TP-137 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:c7bb1faf79f8
  • TP-138 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:e133d384b30c
  • TP-139 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:2c5473c3c0e8
  • TP-140 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:aa4a1d000796
  • TP-141 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:4219fec5a024
  • TP-142 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:5fc1222b78ee
  • TP-143 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:8f49661ee2b4
  • TP-144 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:54ebdee94cf0
  • TP-145 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:2f89e65c86a5
  • TP-146 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:f5a1577e9e0a
  • TP-147 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:a254447b4534
  • TP-148 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:8c1aec2986a9
  • TP-149 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:ebdb5501f01b
  • TP-150 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:26d82f3579f5
  • TP-151 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:771864f3ccec
  • TP-152 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:97ad57ef9e64
  • TP-153 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:661fc101763a
  • TP-154 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:3738091a54dc
  • TP-155 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:39a1c9bdd125
  • TP-156 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:cd0c1aa519a5
  • TP-157 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:8d283ac24024
  • TP-158 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:d5386ce8f733
  • TP-159 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:48cefb2503a3
  • TP-160 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:3fbd3ae6a003
  • TP-161 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:ad9e82855b79
  • TP-162 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:ca5f4fc78b34
  • TP-163 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:133409371038
  • TP-164 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:afb52e57bf70
  • TP-165 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:2d201e9b51b3
  • TP-166 ATTESTED-LOCAL sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS exit:0 h:f8318502c210
  • TP-167 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:fc25e880941f
  • TP-168 UNVERIFIED sha:7558785d9f25571ae66d1879faa74121481185eb out:SKIP h:88ed2d91a6d4
  • TP-169 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:4cde3bafc030
  • TP-170 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:5c8a342c5c6a
  • TP-171 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:6f94917f780f
  • TP-172 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:90529cc489b2
  • TP-173 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:766c0962628c
  • TP-174 VERIFIED-CI sha:7558785d9f25571ae66d1879faa74121481185eb out:PASS run:38059788778/1 exit:0 h:207f8dd28aaa

@dean0x
dean0x merged commit 361dc9e into main Oct 10, 2026
3 checks passed
@dean0x
dean0x deleted the feat/token-diet-wave-3 branch October 10, 2026 15:09
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 -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant