Mission 01KSAF14 orchestration findings (10 items)
Process observations from running mission charter-ux-and-org-pack-vocabulary-01KSAF14 end-to-end on 2026-05-24: 10 WPs across 4 waves, 25-30 sub-agent dispatches, merged via squash to main at 37407a3b2. The mission itself landed cleanly. These notes are about the how, not the what — places where the operator-facing surface behaved in ways the orchestrator did not anticipate from reading the skill prompts alone.
F-01 — Bulk-edit gate has a strict schema the skill does not surface
What happened. First implement-claim for WP01 was rejected with:
Bulk Edit Gate: BLOCKED
• Missing required 'target' section
• Category 'filesystem_paths' has invalid action 'rename-in-place'; must
be one of ['do_not_change', 'manual_review', 'rename',
'rename_if_user_visible']
The mission's occurrence_map.yaml had been authored from the
spec-kitty-bulk-edit-classification skill's prose, which describes intuitive
action names (rename-in-place, rename-and-changelog, migrate-tests,
preserve-historical, delete). The actual validator accepts a much narrower
4-value enum and requires a top-level target: section with term: /
operation: / replacement: keys, none of which the skill prose mentions.
Why it matters. Two planning commits had to be authored on main and
force-pushed onto the mission branch before WP01 could even claim a worktree.
Operator-facing time loss: ~25 minutes. For first-time operators it would have
been longer.
Workaround. Read an existing mission's occurrence_map.yaml
(kitty-specs/release-3-2-0a5-tranche-1-01KQ7YXH/occurrence_map.yaml) and
mirror its schema. The skill prose should either link to that example or quote
the actual validator's allowed values inline.
Follow-up candidate. Either (a) align the skill prose with the validator, or (b) make the validator accept the skill's vocabulary as input and normalise internally.
F-02 — Lane workspaces do not auto-inherit approved-dependency commits
What happened. When WP06 (lane-f) started implementing, it needed WP05's
declarative overrides/enhances fields on the 5 doctrine models. WP05 was
already approved, but lane-f's worktree was created from the mission branch
at the time WP06 was claimed, which was before WP05 had been approved.
Result: lane-f had no view of WP05's commit. WP06's implementer had to
manually git merge kitty/mission-…-lane-e --no-edit to bring WP05's code
into the lane-f worktree before starting work.
Every downstream WP in this mission did the same thing:
- WP03 (lane-c) merged lane-b (WP02).
- WP04 (lane-d) merged lane-c (which transitively had WP02+WP03).
- WP07 (lane-g) merged both lane-d (WP02-04) and lane-f (WP05+WP06).
- WP08 (lane-h) merged lane-g.
- WP10 (lane-i) merged lane-h.
Why it matters. Spec Kitty's lane model is explicitly designed for parallel-safe execution by isolating ownership. But dependency order still implies dependency content: a downstream WP needs the upstream WP's source visible in its worktree to write against. The current orchestration delegates that import to the implementer, which works but is fragile (an implementer who forgets the merge step would author against a stale tree).
Workaround. Every dispatch prompt explicitly told the implementer to merge the upstream lane branch before reading the WP spec. This added ~30 seconds to each dispatch and added a single point of failure per WP.
Follow-up candidate. spec-kitty agent action implement WP## could
auto-merge approved-dependency lane branches into the claimed worktree at
claim time. The dependency graph is already in lanes.json; the merge is
mechanical when there are no conflicts.
F-03 — Squash-merge loses per-WP done transitions
What happened. spec-kitty merge --mission <slug> with
merge.strategy: squash (the project default) produced a single squash
commit on main (37407a3b2) consolidating all WP work. The mission
branch's status.events.jsonl carried WPxx -> done transitions but those
events were on the mission branch, not on main. After merge, the kanban
view on main showed all 10 WPs still in approved — which then blocked
spec-kitty retrospect create with:
Error MISSION_NOT_COMPLETED: Mission has WPs in non-terminal lanes:
WP01 (approved), WP02 (approved), ..., WP10 (approved).
Why it matters. The skill troubleshooting section anticipates this and
provides the manual remediation (move-task --to done --force), but the
flow is confusing: from the operator's perspective the mission has visibly
merged (the squash commit is on main, the lanes are cleaned up, the
mission-review skill is being suggested), yet the retrospective generator
refuses to run.
Workaround. Manual loop:
for wp in WP01 ... WP10; do
spec-kitty agent tasks move-task $wp --to done --force \
--done-override-reason "Feature merged to main as squash commit ..." \
--mission <slug>
done
Follow-up candidate. The squash-merge path could automatically materialise
the done events on the target branch after the squash lands. Either
(a) cherry-pick the chore commit that records the transitions on the mission
branch, or (b) regenerate the events on the target branch at merge time.
F-04 — Auto-generated retrospective is empty for missions with clear learnings
What happened. spec-kitty retrospect create succeeded but produced:
findings_status: ran_no_findings
helped: []
not_helpful: []
gaps: []
proposals: []
The same mission produced 10 distinct process findings (this document). The generator's heuristics did not surface any of them.
Why it matters. The auto-retrospective is positioned as the canonical post-merge record. An operator who trusts the tool's output would close the mission with zero captured learnings. The 3.2.0 default-policy promise of "a real retrospective lands automatically" requires the generator to actually generate.
Workaround. Author findings manually in docs/plans/engineering-notes/finding/
(this file). Treat the retrospective.yaml as a provenance record only.
Follow-up candidate. The generator could mine: (a) status.events.jsonl
for move-task --force and arbiter events, (b) the count of merge-conflict
resolutions in lane worktrees, (c) the gap between baseline and post-mission
pytest failure counts, (d) commit messages tagged with (remediate) or
(fix) on the mission branch. Any of these would have surfaced findings
for this mission.
F-05 — Bulk-edit cutover surfaces pre-existing test fragility independent of the rename
What happened. WP08 (test vocabulary migration) reported a final pytest
result of 19,323 pass / 272 fail. The DIR-013 baseline (filed as #1298 by
WP01 before any changes) was 217 failures. The 55-test delta was investigated
and confirmed independent of the cutover: the new failures lived in
tests/sync/test_events.py, glossary anchor drift, manifest hash
recomputation, and chokepoint-coverage tests flagging WP02's
computer.py:100,103.
Why it matters. The mission's NFR-003 ("zero regressions") was technically breached by 55 tests, but the regressions were not caused by the mission's intended changes. The bulk-edit acted as an involuntary integration test that exposed fragility no one had budgeted to fix. Without the cutover, those tests would have continued passing locally and failing intermittently in CI.
Workaround. Verified that no failing assertion referenced "shipped"
(the rename target); accepted the delta as out-of-scope; documented in
acceptance-matrix.json SC-004 evidence.
Follow-up candidate. A pre-mission pytest --tb=no baseline capture
could be required as a doctrine artifact, so cutover deltas are evaluated
against a fresh baseline rather than the WP01-captured one. This would
prevent slow drift between baseline-capture-time and merge-time from
being attributed to the mission.
F-06 — ADR cross-references are cross-WP work that the lane model cannot own
What happened. Three ADRs were authored across this mission:
2026-05-24-1 (WP01), 2026-05-24-2 (WP05), 2026-05-24-3 (WP07). Each
lives on a different lane branch. The plan called for each ADR to
cross-reference the other two plus the pre-existing
2026-05-16-1-doctrine-layer-merge-semantics.md. WP09 (docs) was tasked with
verifying the cross-references — but WP09 doesn't own the ADR files (they
live in WP01/WP05/WP07's owned_files). WP09 produced an
adr-cross-ref-audit.md recording 5 missing links and deferred the fixes
to "mission-merge phase". The mission merged. The cross-references are
still missing on main.
Why it matters. The lane ownership model assumes each artifact has a single owner. Cross-cutting deliverables (mutual cross-references, repository- wide invariants) are squeezed into the audit-and-defer pattern, which works only if someone notices the audit afterwards.
Workaround. Write the audit file (WP09 did). Flag in this finding document. Open a follow-up to add the missing references.
Follow-up candidate. A new lane class (lane-cross-cutting or
lane-finalize) that owns multi-file integrative work and runs after all
single-file lanes have merged. WP09 used execution_mode: planning_artifact,
which is close but doesn't articulate "I depend on artifacts other lanes are
currently producing".
F-07 — acceptance-matrix.json is required for lane-based missions but the schema is undiscoverable from the CLI
What happened. spec-kitty accept --mission <slug> failed with:
Outstanding items
- activity
• Acceptance matrix (acceptance-matrix.json) is required for lane-based
features but was not found
No template, no schema link, no CLI command to generate a skeleton. The operator must find an example via filesystem grep and reverse-engineer the shape from there.
Why it matters. Acceptance is a release gate. A gate that prescribes a required artifact but doesn't provide a way to author it correctly is a trap for first-time operators.
Workaround. find . -name "acceptance-matrix*.json" found 4+ examples;
copied the structure (criteria array with criterion_id, description,
proof_type, evidence, pass_fail, verified_by, verified_at, notes).
Follow-up candidate. spec-kitty agent mission acceptance-matrix init
that scaffolds a skeleton from the mission's success criteria in spec.md.
Same pattern as mission setup-plan.
F-08 — Mission-review skill requires issue-matrix.md; this mission produced acceptance-matrix.json
What happened. The spec-kitty-mission-review skill's Gate 4 mandates
the presence of a kitty-specs/<slug>/issue-matrix.md artifact with rows
keyed by verdict in a 3-value enum. This mission produced
acceptance-matrix.json (the artifact the accept CLI requires) instead.
The two are similar in spirit but different in schema, location, and
required fields.
Why it matters. Two production gates (pre-merge accept, post-merge review) require similar-but-incompatible artifacts. The operator is left guessing which is canonical.
Workaround. Author both. (For this mission, only acceptance-matrix.json
was authored — the mission review subagent will report Gate 4 as N/A with
a substitution rationale.)
Follow-up candidate. Unify on one artifact, or make the two formally related (e.g., issue-matrix.md is the human view, acceptance-matrix.json is the machine view, and one is generated from the other).
F-09 — Protected-branch guard blocks status commit writes even from authorised operators on solo forks
What happened. Every spec-kitty agent mission setup-plan,
finalize-tasks, and accept --no-commit invocation on main produced:
Error: Refusing to create commit '...' on protected branch 'main' in /home/.../spec-kitty.
Run status commit operations from the mission lane branch/worktree.
The operator (here, the orchestrator) is the sole maintainer of the fork and has full write rights. The guard's blast radius assumption (multiple contributors, branch protection in force) does not match the actual deployment (solo fork, no enforcement).
Why it matters. The guard forces extra manual steps (manual git commit,
manual git push) without preventing anything the operator wouldn't have
done anyway. For multi-contributor repos the guard is correct; for solo
forks it's noise.
Workaround. Every status commit was authored manually via
git add ... && git commit ... && git push.
Follow-up candidate. A config flag (vcs.allow_status_commits_on_target_branch: true) opt-in for solo forks. Default off (current
behaviour) for safety; opt-in available for operators who know what they're
doing.
F-10 — Owned-files glob linked_issues field rejection during finalize-tasks
What happened. Initial WP frontmatter included a linked_issues: field
listing GitHub issue refs. spec-kitty agent tasks map-requirements rejected
the entire batch with:
{"error": "1 validation error for WPMetadata\nlinked_issues\n Extra inputs are not permitted [type=extra_forbidden]"}
The field is intuitive (every WP in this mission was tied to one or more
GitHub issues), and the mission spec's FR/NFR table referenced the issues
explicitly. But the WPMetadata Pydantic model is extra="forbid" and rejects
the field outright.
Why it matters. Linking WPs to tracker issues is the canonical operator
behaviour (DIR-012 is built around it). The metadata schema either should
support the link, or there should be a documented alternative (a notes:
free-text field, a tracker_refs: typed field, etc.).
Workaround. Stripped linked_issues: from all 10 WP frontmatter files
before re-running map-requirements. The link survived only in the WP body
prose, which means it cannot be programmatically queried.
Follow-up candidate. Add a typed tracker_refs: list[str] field to
WPMetadata. Use it to back the DIR-012 HiC-assignment workflow and the
DIR-013 baseline-failure-reporting workflow without forcing operators to
maintain free-text links.
End of mission 01KSAF14 findings.