You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The gate-ledger sweep now covers the bash-wrapper surface — scripts/mavp-operator and the wrapper the installer generates — closing a blind spot every prior sweep's .js-only glob had missed (T-732) — docs/core/GATE_LEDGER.md gains a dated sweep paragraph scoping scripts/mavp-operator (bash) plus the template string buildBashWrapper() emits in mavp-install.js, using bash-appropriate methods (exit-code sites, set -e/-u propagation, stderr refusal-shaped strings, TTY/interactive branches, the adopter wrapper's framework-root resolution failure path) instead of the .js-glob approach every prior sweep used. This is the fourth instance of that same structural blind spot and the first one found by being pre-named rather than by incident, and it is also what surfaced the defects the T-736/T-737/T-741 entries below fix.
The architect's own report must now open by stating which model it believes it is running as, held to account by a drift test (T-733) — a spec-embedded "Model self-report" line, pinned canonically in docs/AGENT_SPEC.md and tested for drift, requires the architect's report to name its believed model before anything else. Worth saying plainly: this detector caught a real mismatch between spawn intent and what the architect actually reported running as one task after it shipped, which is what went on to refute T-734's proposed change.
The architect model policy now separates two mechanisms the word "fallback" used to name at once, and adds an explicit escalation threshold (T-735) — the frontmatter default (model: opus, described now as a no-override default and an Opus floor) is distinguished from the loud-failure re-spawn rule, which keeps the name "fallback." That escalation rule now has a stated threshold — two consecutive loud spawn failures on model: fable, retry Fable once first — and the reasoning behind keeping both mechanisms is recorded as DR-015 in docs/core/DECISIONS.md.
Four verification lessons that had each already cost a shipped defect are now framework rules every adopter gets, instead of habits one operator happened to remember (T-742) — all four were learned the hard way in a project using this framework and lived only in that operator's local notes, which meant nobody else was protected from repeating them. (1) docs/core/ORCHESTRATION_RULES.md gains "Reachability is not correctness": when a task wires a check into a runner — a gate in a make target, a CI step, a git hook — at least one acceptance criterion must now run the whole runner and quote the gate's own output from that run. Verifying the gate by calling it directly proves it is correct; it proves nothing about whether the workflow ever reaches it, and dry-run output cannot tell the difference because it only prints what would run. A real task wrote five criteria, every one satisfiable by calling the gate directly, and shipped a gate the build target never invoked. docs/ARCHITECT_OUTPUT.md's acceptance_criteria: guidance now carries the same requirement, so wiring tasks get a whole-runner criterion at decomposition time. (2) .claude/agents/developer.md's red-run rule now requires the mutation to hit the specific line implementing the contract under guard — two default constants were once swapped whose values were identical, so all 168 tests stayed green and only a contract-line mutation exposed it — and forbids deriving an assertion's expected value from current observed behaviour, which once froze a live bug into a test as though it were the contract. (3) A new developer rule treats any handed-down list of call sites, including an architect brief's own, as a hypothesis rather than a perimeter: re-derive it with your own tree-wide grep before editing. One such list named 14 sites, all in shell scripts, and missed one in a product file, which shipped as a regression. (4) docs/core/BOOTSTRAP_GUIDE.md now states plainly that an adopter session must never edit the resolved framework source — its docs, role specs, rules or state — and must route proposed framework changes to the framework repo's own gate, because such an edit leaves no diff in the adopter's own history while taking effect for every project on the machine.
The framework's own worth-carrying surfaces — ## Selection rules, initiative, wave_goal — are now wired into the point where tasks are born, instead of shipping unread (T-740) — a measured session on a project consuming this framework found six merged tasks, not one on the critical path, while the actual critical path was two operator actions and a single doc; the root cause was that every surface meant to catch this was inert: BACKLOG.md's Selection rules tiers were consumed by no script, doc, or spec, wave_goal was emitted by --agent but never rendered by the session-start skill despite CLAUDE.md claiming otherwise, and no role had standing to refuse a task on worth grounds. .claude/agents/architect.md gains a "Worth gate" section: every gate now tests the request against the brief's initiative/wave_goal plus the Selection rules tiers, names each proposed task's tier in the narrative, and treats an empty decomposition on worth grounds as a legitimate, complete outcome rather than scope invention. CLAUDE.md's architect-gate block now states that initiative/wave_goal are passed verbatim into every architect brief, and that a highest-value operator action is named directly in next_action instead of being turned into a task. The session-start skill now renders initiative and wave_goal in the digest, closing the discrepancy with CLAUDE.md's existing claim.
Fixed
The generated adopter wrapper now honors the framework directory it was actually installed from, and refuses loudly instead of crashing when no framework directory resolves (T-736) — buildBashWrapper(mavericksDirHint) previously never referenced its own parameter, so both call sites' install-time hint was silently discarded; the emitted resolution line then used a directory-existence test as a selector between ~/.mavericks and ~/Documents/mavericks, not a guard, so when neither existed the wrapper (running under set -euo pipefail) deferred failure to node's own module-loader stack trace. The resolution order is now: MAVERICKS_HOME env (unconditional) → the baked install-time hint, used only when it still probes framework-shaped → $HOME/.mavericks → $HOME/Documents/mavericks → a single terminal existence check that, on failure, prints every candidate tried plus the MAVERICKS_HOME remedy and exits 1.
Omitting the role argument on --reflect-skill now prints a usage message instead of aborting with bash's own unbound-variable error (T-737) — both scripts/mavp-operator and the adopter wrapper template in mavp-install.js read the role positional bare (ROLE="$2") immediately before shift 2, under set -euo pipefail; a missing role therefore crashed with $2: unbound variable instead of a named refusal. Both sites now check ${2-} for emptiness before the shift and exit 1 with Usage: mavp-operator --reflect-skill <role> on stderr; --reflect-skill <role> dispatches exactly as before.
--set-strategy-note now honors MAVERICKS_PROJECT_ROOT like every other mutating operator script (T-741) — its ROOT constant was a bare path.resolve(__dirname, '..'), the one mutating script that never checked the env var. From an adopter's wrapper (which exports MAVERICKS_PROJECT_ROOT), ROOT silently resolved to the framework clone instead of the adopter's own project, and guardMutatingRoot() correctly refused the write against that never-a-project root — so an adopter could not set a strategy note at all. ROOT now reads process.env.MAVERICKS_PROJECT_ROOT || path.resolve(__dirname, '..'), matching every sibling mutating script; canonical usage with the env var unset is unaffected.
A mutating operator ritual invoked from inside a linked worktree, targeting that same repo's own primary checkout, is now refused before any write happens (T-743) — checkNeverAProjectRoot()'s guard gains a fourth discriminator that fires when the caller's own worktree matches a non-primary git worktree of the same repo whose primary is the resolved write target, closing the vector behind this session's only successful worktree escape (a stray write into the primary checkout's PROCESS_STATE.json, caught only by the agent's own mandatory post-edit self-verification). --integrate is exempted on a recorded property: its git operations are already root-pinned and it writes no state artifacts of its own.