Repository navigation
v2.0.8
Maul Team for Mac v2.0.8
Reliability release. No Mac app UI changes — everything here is in the
bundled framework.
A whole-codebase audit (8 read-only axes, 81 raw findings, ~71 distinct)
turned up 12 real bugs. Six of them were invisible from inside the
framework repo: they only fire in deployed target projects, which is why
the test suite stayed green throughout and CI never caught them. The
framework repo is immune to each for a structural reason — files are
git-tracked here, and the guards under discussion are deliberately not
registered here — so the masking was systematic rather than incidental.
The phase/path guard was inert in every deployed project (#85)
hooks/status-gate.sh emitted a top-level {"decision":"allow"|"deny"}.
Claude Code's top-level decision enum is ["approve","block"] —
allow/deny belong to the nested
hookSpecificOutput.permissionDecision. The object failed schema
validation, no permission decision was applied, and with no exit 2 path
the tool call proceeded.
Unenforced as a result: "only pbi-designer may write
docs/design/specs/", phase-based source-write gating, catalog.md
read-only, and catalog-config enablement.
The tell was in-repo: hooks/lib/validate.sh calls exit 2 "the Claude
Code hook deny convention", all five other guards use it, and
status-gate.sh's own header comment described the contract it did not
implement. Verified against the shipped binary (2.1.220).
The allow path deliberately stays a bare exit 0 rather than
{"decision":"approve"} — an explicit approve would bypass the user's
normal permission prompt for every Write/Edit.
Also: Claude Code matchers built only from [A-Za-z0-9_|, -] take the
exact-name membership branch, not a regex branch, so "Write|Edit" never
substring-matched NotebookEdit. All three PreToolUse guards now name the
notebook tools explicitly, in both settings surfaces.
Two breaking schema changes shipped with no migration (#85)
Both affected files sit in migrate-state.sh's blocking STRICT_MAP,
so an affected project hard-failed launch at exit 65 and the team never
spawned — with no documented repair.
003-drop-improvement-category.sh—e1958ecremovedcategoryfrom
improvements.schema.jsonunderadditionalProperties: false. Its own
commit message said "Existing state files must be migrated (strip the
key)"; none was written.004-drop-pbi-state-phase.sh—001-legacy-to-ssot.shskipped per-PBI
files on the stated grounds that "projects in flight legitimately carry
richer fields", which contradictsadditionalProperties: false. A v1
file still carryingphaseblocked the launch gate.
Each migration's first test asserts the pre-migration file is genuinely
rejected, so the launch-block is proven, not assumed.
Cross-model review was unreachable from PBI worktrees (#85)
Reviewers cd into .scrum/worktrees/<pbi-id> before sourcing
codex-invoke.sh, and a worktree checked out at sprint.base_sha cannot
see untracked files from the main working tree. In any project set up by
setup-user.sh, the source failed and every codex second opinion reported
itself "unavailable" — for an entire Sprint at a time.
The helper now deploys to .scrum/scripts/lib/, which is symlinked into
every worktree. This also stops setup-user.sh scattering four
framework-repo-only helpers into the target's scripts/ tree, where a
plain cp could clobber a same-named file.
Gates that passed vacuously (#85)
- Pragma audit.
jq -r '.pragma_pattern'yields JSON text for the
schema-sanctioned object form and the literalnullwhen the key is
absent. Either matches nothing, andallover an empty array is true —
so the pragma leg of Definition of Done was a silent no-op. All three
shapes are now handled explicitly and an unresolvable one fails loudly.
Narrowing the schema instead was rejected:config.jsonis in
STRICT_MAP, so that would have created a third launch-brick. c0_threshold/c1_thresholdwere mandated by four documents but
declared in no schema and in no example config — a typo silently
reinstated the 100% requirement.
Stalls, duplicate work, and dropped events (#85)
- A dead Developer teammate stranded its PBI at
escalatedwith a null
reason, so the handler never wroteescalation-resolution.mdand
completion-gate.shblocked every subsequent SM Stop. Adds the
teammate_unrecoverableescalation reason end to end. session-context.shreported merge-queued PBIs as active pipelines (the
in_progress_mergeexclusion its two sibling copies carry was missing),
triggering duplicate Developer re-spawn.session-context.shechoedhookEventName:"PostCompact", which is not a
member of the hook output union, so post-compaction context was silently
discarded.json_update_atomictook no lock on a read-modify-write that
dashboard-event.shruns on everyPostToolUsefrom every concurrent
agent; a droppedsubagent_stopinflatedcount_in_flight_subagents
into a spurious "N subagent(s) still running — do NOT re-spawn".pbi-mergecollapsed preflight refusals (64/65/67) with real conflicts
(exit 1), so the SM told a Developer to resolve conflicts in a worktree
that had none while the actual remedy sat in discarded stderr.
Hardening and hygiene (#85)
- All 23 scrum-state wrappers now resolve schemas through
lib/atomic.sh::resolve_schema_dirinstead of an inline probe that used
only its first candidate, with no$PWDfallback and no diagnostic. - The four top-level contract schemas under
docs/contracts/, which
nothing validated, gain fixtures and 13 tests — valid fixtures must
pass and invalid ones must be rejected. A drift guard asserts every
criterion_keya reviewer declares is in the envelope enum, the exact
defect1b2d9aafixed by hand. Both guards were mutation-tested. - Stale references,
flock-vs-mkdirlock drift in four places (the
difference is load-bearing: a mkdir lock does not auto-release on process
death), undocumentedStopFailure/PostCompacthooks, the missing
retrospective → completephase edge, and ~158 lines of duplicated prose
collapsed to pointers. Deliberate duplication was left intact and
documented as such. - Target-project Sprint identifiers removed from three files, per this
repo's own no-private-project-references rule.
Upgrading
Existing projects pick all of this up on the next scrum-start.sh launch:
the two new migrations run automatically and idempotently before the team
spawns. No manual step.
Verified: shellcheck clean · ruff clean · 715 tests green
(unit+lint 225, scrum-state 445, integration 45).
Full diff: #85