infiquetra/mission-control
SDLC management for Operations, Asgard, and CAMPPS: prepared issue drafts, live schema sync, board schemas, labels, project fields, metrics, and card validation.
Changelog
[2.21.5] - 2026-10-14
Changed
-
Presentation metadata (author, repository, license, and keywords) now lives in the portable
plugin.json. The Claude manifest keepsauthorand its paths, and no longer repeatsrepositoryorkeywords. -
The bundled
staffing.jsonfollows fleet-core: Muse now runsmuse-wonder-full-1.0.
[2.21.4] - 2026-10-07
Changed
- The bundled Fleet Core staffing data is regenerated for the Haiku effort shift (fleet-core 0.33.2).
[2.21.3] - 2026-10-07
Changed
- The bundled Fleet Core staffing data is regenerated with the 2026-10-07 tier defaults
(Haiku 5.5 and the Sonnet 5.5 cache-read cut), and
sdlc-operatorpinseffort: high.
Changed
- The bundled Fleet Core
staffing.jsonandjev_verbs.pyare regenerated for issue 158 (atargeted-reviewerstaffing role and aconsequenceJev verb). Mission Control calls neither.
Fixed
issue prepareno longer records the handoff source's path as the card'sObjective(#111). TheObjectiveproject field is carried only when the operator names it with the new--objectiveflag, which must match a live Objective option on--project(checked at the command line, so the flag needsghauth;issue_prepare()itself stays offline). Without the flag no Objective is recorded and the suggestion step logs no objective override, so the verdict log stops accumulating false operator choices that skew the objective judgment's agreement figures.- New
issue invalidate-bogus-objectivescommand: a one-off, idempotent cleanup that appends oneinvalidationrecord per pre-#111 verdict that logged a source path as the Objective. The log is append-only, so nothing is rewritten; the evaluation harness excludes invalidated verdicts from scoring.--dry-runreports first;--log-diroverrides the log location. - The bundle loader no longer inserts this package's
_bundleddirectory intosys.pathnor imports by short name (#111 CI). In a process shared with saga's tests, the old loader let this package'sstaffingcopy win the short-name cache, and saga's tier consults silently failed open against a bundle withoutjev_verbs._fleet_commonsnow delegates to the path-scoped_load_bundled, and the bundle carriesjev_verbsso the bundledstaffingcopy can consult on its own.
Tests
tests/test_issue_prepare.py: a sourced prepare records no Objective; a named one records it verbatim; the dispatch rejects an unknown--objectivewithout it ever reachingissue_prepare, and makes no live field call when the flag is absent.tests/test_issue_prepare_suggest.py: a sourced prepare without--objectivelogs no objective override and a null label; with--objectivethe comparison uses that value.- New
tests/test_issue_invalidate_bogus_objectives.py: dry-run writes nothing, a run retires only the bogus objective verdicts with the originals byte-identical, and a rerun is a no-op. - New
tests/test_fleet_bundle.py: bundled loads leavesys.pathand the short-name cache untouched, and the bundledstaffingcopy runs a tier consult instead of failing open.
[2.21.2] - 2026-10-04
Stops the test suite from reading the SDLC schema live from GitHub.
Fixed
- The package's test suite no longer reads the SDLC schema live from GitHub.
load_config()fetchedinfiquetra-sdlc/config/sdlc-schema.jsonthroughgh apibefore falling back to the vendored copy, so one suite run spent 291 REST calls on the operator's token. A new test seam,INFIQUETRA_SDLC_SCHEMA_OFFLINE=1, skips that read;tests/conftest.pysets it for every test, and the four tests of the live-read path clear it and stubgh. Production callers never set it.
Removed
issue prepareno longer stamps a### Recommended Tier Bandsection on new issues (#94). The band came from the issue type alone, admission never read it, and the saga/planfunctions meant to apply it no longer exist. Jev's per-issue judgment replaces it._ISSUE_TYPE_TIER_BANDS,derive_tier_band,_has_tier_band_section,_open_fence_closerand_append_tier_bandare gone._source_to_issue_body_unstampedis now_source_to_issue_body, since nothing wraps it any more.- The issues skill no longer documents the band.
Changed
- Regenerated the bundled
staffing.pyandjev_log.pyfor the tier judgment (issue #96). Nothing in mission-control calls the changed functions. - The build-time Fleet Core bundle now carries
staffing.py, because the bundledintent_envelope.recommend_tiertakes its base tier from the staffing resolver (issue #93). Regeneratedstaffing.jsonandtier_resolver.pycarry the newimplementationwork shape. - Existing issues keep their band section. Nothing reads it, so it is inert and is not rewritten. A
--fromrevision of a draft prepared before #94 still strips the section, through the_RETIRED_TIER_BAND_SECTIONentry in the handoff strip list.
Tests
- The four stamping tests in
tests/test_card_validator.pyare replaced by tests that no issue type gets the band, that a body carrying the legacy section still validates, and that the retired helpers are gone.tests/test_issue_prepare.pychecks a prepared draft of each issue type, andtests/test_sdlc_draft_revision.pychecks that revising a pre-#94 draft leaves one Source context and one Handoff maturity section.
[2.21.1] - 2026-09-22
2.21.1 — imported from infiquetra-claude-plugins@acc99fe7 (upstream 2.21.0); authored here from this commit; no provenance manifest from now on.
Changed
- The package is maintained in this repository.
PROVENANCE.jsonis gone. The port descriptor keeps the assessment block and no longer names an upstream pin. - Version 2.21.1 is upstream 2.21.0 plus a patch bump for this authored cut. The portable manifest, the root Claude manifest, and
com.infiquetra.claude/plugin.jsonall say 2.21.1. fleet-bundle.jsondeclaresintent_envelope,plugin_resolution,tier_palette,tier_resolver,typesafe_client,retry_backoff,jev_log, andstaffing.json. The guarded import rewrite covers only_load_intent_envelope. The saga-readiness loader and_fleet_commonsstill calledfleet_commons_shimafter that rewrite, so both now import the bundle beside the script.- Skill script locations no longer walk from
INFIQUETRA_SDLC_PATHintoinfiquetra-claude-plugins. Runpython3 scripts/sdlc_manager.pyfrom this package root. Claude commands still use$CLAUDE_PLUGIN_ROOT/scripts/sdlc_manager.py, which is correct becausescripts/stays at the package root. flow repair-windowis a GitHub label write. It is listed in the assessment's mutating operations.- A
triageskill documents the same script the/triagecommand runs, so a skill-scoped harness can reach it.
Dropped tests
tests/test_executor_profile_lint.py::test_palette_arrives_via_shim_rung_provenance— its premise isfleet_commons_shimdiscovery viaFLEET_COMMONS_ROOT. The lint imports the bundledtier_palettedirectly, so a fake fleet-core root is not on its path. The rest of that file is carried.
tests/test_prompt_alignment.py and tests/test_card_validator_agreement.py are carried. The first walks .claude-plugin/plugin.json, which this layout has at the package root, and its command and agent paths now point under com.infiquetra.claude/. The second skips when the home-lab card validator is not on disk. tests/test_saga_readiness_alignment.py skips the cases that load saga's handoff_envelope.py when that file is not in this catalog.
[2.21.0] - 2026-09-20
Fixed
- The retired Mount Olympus status vocabulary is gone from agent-facing prose and the
board movehelp (issue #1042). The milestones and metrics skills still namedAssigned,In Review, andNeeds Questionas live states -- none is a Status option on any active board, so each of those instructions failed at runtime with no migration hint. The prose now names the live statuses (Implementing,Code review,Needs clarification), the history notes no longer spell the retired active-start name, and theboard move --statushelp examples --Assigned,In Review, andActive, the last a Stage rather than a Status -- are replaced with statuses the committed census records. The board-schema drift guard now scans prose for the Mount Olympus names and pins the help examples to the census.
[2.20.0] - 2026-09-20
Changed
- The saga readiness reader loses its handoff-envelope and reversibility-certificate paths
(issue #1030). Both saga modules were removed with the
/handoffcommand and the ship ceremony;sdlc_manager.pyno longer resolves or loads them. Mission Control's own command surface, its board writes and its issue handling are unchanged -- this is the consumer side of a removal, not a change to what this plugin does.
[2.19.0] - 2026-09-19
Renumbered from 2.18.0 and 2.17.0 when main was folded into parent/1018: issue #1035 took
2.17.0 on main first (commit 49618446), so the two integration-branch sections sit above it.
Fixed
- The recommended-tier-band comment names the merged staffing data (#1021). The band map in
scripts/sdlc_manager.pysaid it mirrorstier_policy.json's work-shape bands. Issue #1021 merged that file intoplugins/fleet-core/scripts/fleet_commons/staffing.json, so the comment now names itswork_shapesblock. The bands themselves are unchanged, and no behaviour moved.
[2.18.0] - 2026-09-19
Fixed
-
Board vocabulary drift, three surfaces at once (issue #1020). The cached board census
config/board-schema.jsonwas last regenerated on 2026-07-14 and had fallen two board migrations behind: it recorded noStagefield on any board, gave Operations and Asgard the retiredIdea / Shaping / Ready / Active / Verify / Doneladder, and gave CAMPPS an even olderTodo / In Progress / Done. It is regenerated from the live boards and now records, for all three, theStagefield with six stages and theStatusfield with the 26 stage-flow options thatconfig/sdlc-schema.jsondeclares. The regeneration also picks up field membership that had drifted:Stageadded to all three boards andPriorityto Asgard and CAMPPS, withRiskandWork Typegone from Operations,Jeff Needed,Mode,Risk,Target RepositoryandTransfer Targetgone from Asgard, andInitiative,Start,TargetandTest Strategygone from CAMPPS. No live board changed; only the snapshot of it did. -
The vendored
config/project-mappings.jsonno longer overrides each board's declared workflow. It pinnedintent_flowfor Operations and Asgard — a workflowsdlc-schema.jsonno longer defines at all — andcampps_initiativefor CAMPPS, which the schema does define but marksretired_historicalwithactive_routingfalse. Because_project_workflow_nameprefers the mapping's label over the board's own declaration, an environment resolving through the vendored file got a one-entry status order (['No Status']) for Operations and Asgard and the retired five-status ladder for CAMPPS. The three labels are removed so resolution falls through to the board's declaredstage_flow. This was invisible on any machine with aninfiquetra-sdlccheckout, whose external copy is current and wins the resolution ladder. -
The board and metrics prose stopped describing a vocabulary the boards do not have.
skills/board/references/kanban-workflow.mdandskills/board/SKILL.mdeach carried the retired ladder plus a sentence admitting the section was stale and deferring the fix; both now describe the single sharedstage_flow. The withdrawn work-in-progress limits are removed rather than restated, matching the schema decision that retired them. Terminal-status and cycle-time boundary tables acrossskills/metrics/SKILL.mdandskills/metrics/references/metrics-targets.mdno longer nameDoneorIn Progress, neither of which is an option on any live board; the terminal isReady to closeeverywhere. The work-in-progress age tables in both metrics files are rewritten to the one threshold_active_age_thresholdsactually computes — three days for every non-terminal Status on every board — replacing a per-board split with a five-day CAMPPS row keyed on the retired StatusIn Progress. The cycle-time boundary tables now also record how the start boundary is really detected:_cycle_start_statusesreturns the literal option nameActive, and the timeline query it feeds captures only the option name of a single-select change — it records no field name, so it cannot tell aStagechange from aStatuschange.Activeis a liveStageoption and not aStatusoption, so the boundary is whichever field first carried an option of that name. Making the field explicit is left to a separate card. -
No prose surface tells an agent to write a Status the boards reject. Fourteen lines across six files did:
skills/board/SKILL.md,README.md,commands/triage.md,agents/sdlc-operator.md,skills/flow/SKILL.mdandskills/issues/SKILL.mdvariously wroteActive,Shaping,In Progress,Ready,IdeaorCommitted— Stage names or retired names, none of them a valid Status option — through three different syntaxes and in plain English.LIVE_LEGACY_STATUS_ALIASEScarries no entry for any of them, so an agent following one got a rejected option with no migration hint.tests/test_board_schema_drift.pynow guards this from the names outward rather than from one syntax: it reads every Status value written as--status <value>(quoted or bare) or as--field Status --option <value>, and separately catches a retired name used bare in a sentence, across all 21 Markdown instruction surfaces underskills/,commands/andagents/plus the README. Lines inside a section marked as history are exempt, andCHANGELOG.mdis not swept at all — a record of what a release retired must be free to name it. The guard was confirmed red against the unfixed tree before being trusted.
Changed
- BREAKING (consumed shape):
board-schema.jsonkeysfieldsby field name instead of listing them. A cached expression like.boards.operations.fields[] | select(.name=="Status")stops working; the equivalent is.boards.operations.fields.Status. Every in-repository consumer is updated, and the artifact has no documented external consumer, which is why this ships as a minor rather than a major version. A consumer outside this repository must update its path. A consumer can now ask for one field directly —.boards.operations.fields.Status.options[].name— instead of scanning a list. Keys are emitted sorted and--writestill serializes withsort_keys=True, so the committed file diffs as stably as before. A duplicate field name now raises rather than silently overwriting, since a name-keyed mapping can lose a field that a list cannot.config/generated/check_issue_contract_parity.pyconsumes the census by calling the producing function rather than by reading the file, so its live parity leg moves to the mapping too; it was verified against the real boards rather than only against its own fixtures.
Added
tests/test_board_schema_drift.py, a credential-free drift guard. The existingboard_census.py --checkcompares against the live boards but deliberately prints SKIPPED and exits 0 without aproject-scoped token, which is every normal continuous-integration run — so a drift can sit unnoticed, and this one did for two months. The new guard needs no network: it asserts that every Status and Stage option in the committed census matches the vocabularysdlc-schema.jsondeclares, that no retired status name survives, and thatfieldsis a mapping. The live comparison remains available as an opt-in leg underBOARD_SCHEMA_LIVE=1. The guard was confirmed to fail against the pre-regeneration census before being trusted.
[2.17.0] - 2026-09-19
Bumped from 2.16.0, the version on main at the time this work branched (commit 866d3670).
Added
-
Advisory triage suggestions on
issue prepare(issue #1035). A new opt-in--suggestflag records a suggested issue type with its full probability distribution over all five types, a suggested risk tier over the repository's own four-tier vocabulary, and — when candidates are supplied with the repeatable--objective-option— a suggested Objective and board Status, in the draft's sidecar JSON.type_suggestionandrisk_suggestionare mirrored at the top level of the sidecar beside the fullsuggestionsblock.Nothing is applied. The author's own
--type,--riskand--statusflags remain the decision; a suggestion that differs from one of them is recorded as an override in the verdict log, linked to its verdict by hash. Suggestions never reach the compiled issue body, so the card validator is untouched, and the risk suggestion never reaches the card's own Risk, which the body still owns. Without the flag the command makes no model call, imports no client, and writes a byte-identical draft. -
Advisory label union on
labels auto-label(issue #1035). The same--suggestflag turns the command into a read-only advisory: it prints the union of the labels the existing regular-expression rules matched and the labels a model judged applicable, each taggedrule,modelorboth, and applies nothing. The union is widen-only — every rule-derived label survives every possible model answer — and the threshold is the answer's yes-probability, not its banding confidence, so a confident rejection cannot add a label. Without the flag the command posts its regular-expression matches exactly as it always has. -
plugins/mission-control/scripts/triage_suggest.py, a new module holding the question set, the answer shaping and the pureunion_labelsfunction. Every judgment goes through the fleet-core TypeSafe client (plugins/fleet-core/scripts/fleet_commons/typesafe_client.py, shipped by issue #1032); this plugin adds no HTTP code and no second client. The key is read only by that client, fromTYPESAFE_API_KEYin the environment, and is never printed, logged or written to a sidecar.
Notes
- A client failure of any kind — error, timeout, malformed body, or an unexpected exception — leaves the draft and the sidecar exactly as they would have been, plus one note recording the failure. No readiness gap is added and the exit status is unchanged.
- Every new test drives a fake client; none touches the network or the real verdict log.
[2.16.0] - 2026-09-13
Added
- Saga-owned readiness for prepared sources (issue #942).
issue prepareno longer infers handoff maturity from folder location or a local vocabulary. All readiness assessment is delegated to the saga plugin's handoff envelope (scripts/handoff_envelope.py), resolved through the installed-plugin registry (orSAGA_ROOT) and gated by contract major, API surface, and a six-value functional vocabulary probe; an absent, incompatible, or broken Saga dependency fails closed with a repairable diagnostic naming the fix. Undeclared drafts and Saga state files are refused (unknown:sentinels), out-of-root sources are refused outright, and only the four ready states route —pending-confirmationanddeferred-contextcreate with warnings and never carry a live/planor/workcommand. Explicit--maturityvalues are classified by the owner, so those two states are now accepted alongside the four ready ones. flow repair-windowverb (issue #1000). Opens or closes the schema-markerrepair-windowlabel on an issue over REST labels: citation-first refusal (missing or blank citation never reaches the network), idempotent transitions (open no-ops when the label is present, close no-ops when absent), one citation comment per real transition, and no GraphQL project-field writes. When the vendored schema declaresmarker_source, the marker definition resolves from the loaded config and an old schema lacking the marker refuses.
Changed
- Risk is a common body field (issue #1000).
### Riskis the 14th required card section:low,medium,high, orvery-highon the first line plus a non-placeholder justification, or uppercaseUNKNOWNwith an Architect-assessment justification. UNKNOWN cards pass validation and creation with a warning naming the missing assessment; the Planning-to-Active gate refuses UNKNOWN, missing, or malformed Risk.--riskseeds only compiled scaffolds — supplied bodies pass through untouched — and risk is re-derived from the body on every read. - Count-only WIP tracking (issue #999). WIP limits count cards only; the per-card estimate reading is retired along with the schema's estimate keys.
- Vendored SDLC schema re-synced to
2026-09-07.5(issue #999). The vendored snapshot and generated contract files move to the authoritative source;workflows.intent_flowis retired in favor ofworkflows.stage_flow.
Removed
- Technical Risk GraphQL project field (issue #1000). The prepared-field Risk producer, its project-field mapping, and the interactive prompt entry are gone; Technical Risk is retired as a project field (E1) and Risk lives in the body only.
[2.15.2] - 2026-08-30
Fixed - retire the CAMPPS ladder from the board skill and kanban reference (unit W19, issue infiquetra/infiquetra-sdlc#100)
skills/board/SKILL.mdandskills/board/references/kanban-workflow.mdno longer document the retired CAMPPS ladder as current. CAMPPS is documented with the sharedstage_flowworkflow (Intake -> Shaping -> Planning -> Active -> Verify -> Retro), whose stages and per-stage Status sets are transcribed fromconfig/sdlc-schema.jsonat schema version2026-08-29. No active board carries a pause column; a paused card is expressed through labels and issue state.- Command examples now name flags and values that exist:
board moveexamples use livestage_flowStatus values, the Stage-column example usesflow set-field --field Stage, and the CAMPPS archive and standup-review prose follow the schema (Ready to closeis the terminal status). - The Mount Olympus legacy timeline facts are preserved as pointers to
LIVE_LEGACY_STATUS_ALIASESinsdlc_manager.pyinstead of hand-copied value lists. - Document Review cycle 1 dropped the kanban-reference claim that CAMPPS uses "the same
six stages as Operations and Asgard." That phrase was schema-true and contradicted the
still-stale Operations/Asgard
intent_flowsection in the same file. The CAMPPS section now statesstage_flowand points at the deferred correction, matching the board skill.
[2.15.1] - 2026-08-30
Fixed - prepared-issue starting Status derives from Stage (unit W10 repair, issue infiquetra/infiquetra-sdlc#91)
- The prepared-issue starting-status rule no longer keys off the team. The
retired
_TEAM_SAFE_STATUSEStable (asgard → Shaping,campps → Idea) made readiness unsatisfiable: neither name exists in the live Status vocabulary, so every author-supplied Status was refused and the default was unwritable. - Starting Status is now author-supplied, with its default derived from the
declared Stage: the schema's
workflows.stage_flowentry option (first name of the stage's list, R49 — Intake→Capturing, Shaping→Discovering, Planning→Designing, Active→Implementing, Verify→Awaiting verification, Retro→Gathering evidence). The map resolves through_resolve_sdlc_schema()(GitHub main → vendored → local), so the vocabulary lives in one versioned source. Readiness accepts any Status configured WITHIN the declared Stage plus the cross-cuttingBlocked, and refuses retired, unknown, and out-of-Stage values (operator ruling of 2026-08-30 settling cycle-5 F-2; the entry option is a default, not a closed set). A declared Stage outsidestage_statusesblocks instead of silently skipping the Status rule. - The
issue prepare/create-preparedoperator docs no longer teach the retired team-keyed starting statuses (Asgard Shaping/CAMPPS Idea): the issues skill, README, and sdlc-operator prompt now state the Stage-derived entry-option default and the in-Stage acceptance rule (F-1). - The vendored
config/sdlc-schema.jsonwas refreshed to the authoritative 2026-08-29 file from infiquetra-sdlc main — the previous vendored copy was 2026-06-17 with nostage_flowblock at all, which masked the defect online and degraded it silently offline. _resolve_sdlc_schema()'s remote branch now degrades on ANYghfailure or undecodable payload (matchingload_config's remote fallback) instead of escaping asUnicodeDecodeError/JSONDecodeError.- A stage-less prepare no longer emits a
status:front-matter line (the default is not derivable without a Stage); the draft stays blocked on the missingStage. The R3b fill-in may now addstage:alone: an empty Status with a declared Stage is defaulted to the entry option on read, before readiness evaluates.
[2.15.0] - 2026-08-29
Added - the Intake exit initializes both lifecycle fields (unit W10, issue infiquetra/infiquetra-sdlc#91)
issue create-preparedcreates the issue, adds it to the correct project, and then performs TWO sequential constrained lifecycle writes —Stagefirst, thenStatus— through the sameflow_set_fieldcall site (W6's cross-board mutation), stopping at the first failure; each step is removed fromremaining_stepsand persisted only after its own write returns, so the existingpost_create_pendingresume state recovers a partial run without creating a duplicate issue.PreparedIssuecarries an author-suppliedstagewith no default and no handoff-maturity mapping (operator ruling on sdlc#91 OQ1). A prepared draft missing or holding an emptyStagefails readiness blocking, before any GitHub issue is created;issue preparestill emits the draft file so the author can fillstage:in (R3b — CLI prepare has no--stageflag by settled decision). The draft writer emits thestage:front-matter line only when a value is present, so the naive front-matter splitter can never store the literal stringNoneas an authored Stage.- A still-blocked
None-approval sidecar whose re-read readiness now passes (the R3b fill-in path) is STAMPED into the U11 approval gate even when--skip-approvalis passed — the stamp and the per-invocation bypass are separate decisions — and the refusal then honors--skip-approvalfor that invocation only. A stopped run (e.g. mapping_pending) therefore leaves a durable gate record, and a later create without the flag refuses instead of falling through as a pre-U11 legacy draft.ready_to_createsidecars withNoneproceed unchanged (deliberate pre-U11 back-compat). - The mutation plan shown for confirmation names both writes (
set-stagebeforeset-status). - A legacy
post_create_pendingsidecar whose remaining lifecycle steps lack a"stage"token is migrated: the Stage write is inserted before"status"and still runs on resume (flow_set_fieldis idempotent).
Changed
- The interactive
issue createpath prints a non-blocking NOTE immediately after the pasted issue number — before the card-contract gate — reporting that the issue was created outside the Intake exit and that the prepared-path initialization of bothStageandStatusdid not run. It never claimsStatusis unset. Abort, decline-browser, no-number, and--skip-metadatastay silent (no issue exists on those paths).
[2.14.0] - 2026-08-29
Added - identity-preserving single-select option-set helper (issue infiquetra/infiquetra-sdlc#94)
fields set-options --project <p> --field <f> --options-file <json> [--dry-run]: replaces a single-select field's option set viaupdate_field_single_select_options, which refuses — before any mutation is sent — a submission that is not the COMPLETE desired list, drops a live option, or omits an existing optionidon a retained or renamed option (singleSelectOptionsoverwrites the whole set; omitting an id mints a new option and clears every item value pointing at the old one).fields create-optioncorrected: it has never mutated anything; its doc, help, and runtime output now say so plainly, and the destructive one-optionQUERY_CREATE_FIELD_OPTIONreference example was removed fromskills/board/references/graphql-queries.md.QUERY_GET_PROJECT_FIELDSnow returns optioncoloranddescriptionalongsideidandname, so the helper has the full live attribute set when composing a submission. The--options-fileitself must still be a JSON array of option objects ([{name, color, description?, id?}]), not the discover command's printed report (whose shape is{project, fields}): runfields discoverto read the current names, ids, colours and descriptions, then write the complete list file by hand.
[2.13.0] - 2026-08-28
Added - one constrained lifecycle-field mutation, atomic across every board carrying the issue (#87)
- Added the constrained cross-board lifecycle mutation in
sdlc_manager.py(_set_lifecycle_field_cross_board): it writesStageandStatus— and only those fields — to EVERY project board carrying the issue, discovered from the issue's ownprojectItems(QUERY_GET_LIFECYCLE_FIELD_BOARDS), never from a repo-to-board mapping. Reuses the existingassert_correction_field/correction_identitycorrection seam, so the emitted identity strings stay byte-identical to saga's ledger key. Proves acceptance examples AE3/AE9/AE27/AE37 (infiquetra/infiquetra-sdlc#87). - Atomicity is apply-then-compensate: every carrying board is preflighted (field + option resolve there) before the first write; a failed write restores each already-written board's captured prior value; a board whose prior value was unset counts as a FAILED restore (no clear primitive exists). A failed compensation halts with a named divergence (
LifecycleMutationHaltError, carryingboard_state) — non-zero exit, no silent retry, never swallowed by the prepared-issue loop'sexcept RuntimeError. - Added
--reasontoflow set-field(optional; recorded verbatim per board; omitted writes the explicitreason-not-suppliedsentinel, never a fabricated justification). - Added tests:
test_lifecycle_field_boards.py,test_lifecycle_field_mutation.py,test_lifecycle_field_routing.py,test_lifecycle_field_identity.py, and the KTD12 AST writer-census gatetest_lifecycle_writer_census.py.
Changed - flow set-field and board move blast radius for Status/Stage (KTD10/KTD13)
flow set-field(andflow_set_fields_bulk, hence every--numbers/repeated---fieldpath) now routes EVERYStatus/Stagewrite — with or without--correction— through the cross-board mutation, selected by field name, not by flag. A single-boardStatus/Stagewrite is no longer possible:--projectis validated as a carrying board, never obeyed as a restriction. Affected callers: saga's board writer (board_progression.py, unchanged argv, now cross-board), the issue-create post-step, and the prepared-issue field loop.board move(KTD13/D2): the per-project best-effort write loop is gone;board moveroutes itsStatuswrite through the constrained cross-board mutation and no longer continues past a failed board.#609holds: an ordinary failure still returnsFalseand exits 1 via the CLI arm; a compensation halt raises instead.- Non-lifecycle fields (
Initiative,Objective,Priority, ...) keep the exact previous single-board behavior.
[2.12.9] - 2026-08-26
Removed - dead rollout update subcommand and beads-config write claims (#821)
- Removed the dead
rollout updatesubcommand parser, therollout_update()function, the dispatch arm, the module-docstring example, the reader-list comment mention, and documented invocations acrossREADME.mdandskills/rollout/SKILL.mdper operator ruling C2. - Left the
legacy_rollout_configmechanism and its three surviving readers (board_wip,rollout_status,config_show) intact and gracefully degrading to{}on missing files. - Added
TestMissionControlRolloutSurfaceGuardintests/test_mission_control.pyverifying thatrollout --helplists exactly five subcommands with noupdate,rollout updateis rejected as an argparse invalid choice, no document namesbeads-config.jsonas a write target, the three readers handle missing configs gracefully, and mutation tests fail on reintroducing the dead command or write claims.
[2.12.8] - 2026-08-26
Fixed - add flow row to skills table in README (#820)
- Added the
flowrow to the "Skill | Activates When..." table inREADME.mdwith activation text consistent withskills/flow/SKILL.md(Project field assignment, sub-issue linking, label verification, card validation), restoring complete alignment between shipped skills and the README activation map. - Added
TestMissionControlSkillsTableBijectionGuardintests/test_mission_control.pyasserting a bidirectional 1-to-1 match (bijection) between directories underplugins/mission-control/skills/and README table rows, ensuring failure messages name specific missing or extra skills, verifying activation content, and mutation-proving that removing theflowrow or altering skills fails the guard.
[2.12.7] - 2026-08-26
Fixed - delete self-referential alias clauses in issue command docs (#819)
- Deleted the self-referential compatibility alias clause from
commands/issue.mdand historical changelog entries, restoring clarity that/issueis the primary issue command surface with no unrecoverable rename claim or nonexistent compatibility alias. - Added focused assertions in test suite verifying that no sentence names a command as its own compatibility alias, that neither repaired sentence carries an alias clause, and that the plugin ships exactly four commands (
board,issue,metrics,triage).
[2.12.6] - 2026-08-26
Fixed - update-surviving installed script paths in docs and commands (#818)
- Replaced six version-pinned installed script paths (pinned to legacy cache directories) in
README.md,commands/board.md,commands/issue.md,commands/metrics.md, andcommands/triage.mdwith the settled convention"$CLAUDE_PLUGIN_ROOT/scripts/sdlc_manager.py"(noskills/segment). - Added drift guards in
tests/test_mission_control.pyverifying no version-pinned paths exist across tracked Mission Control documents, confirming all six sites use the$CLAUDE_PLUGIN_ROOTinvocation form, mutation-proving that restoring a pinned path fails the guard, and confirmingagents/sdlc-operator.mdremains unaffected.
[2.12.5] - 2026-08-26
Fixed - dynamic package-root resolution in sync_template_docs.py (#822)
- Replaced fixed
REPO_ROOT = Path(__file__).resolve().parents[3]atsync_template_docs.py:16with dynamic_find_package_root()discovering.claude-plugin/plugin.json, adopting the identical package-root resolution pattern established in issue #829. - Updated derived paths (
REFERENCE_PATHandCONTRACT_DATA_PATH) to resolve relative toPACKAGE_ROOT, enabling relocated and staged copies ofmission-controlto run entrypoints including--helpand--checkregardless of directory depth. - Maintained fail-loud behavior naming the resolved path when
.claude-plugin/plugin.jsonor required data files are missing. - Added relocated-copy guard tests and mutation proof in
tests/test_mission_control.pyandplugins/mission-control/tests/test_template_sync.pyverifying--help,--check, missing-file fail-loud behavior, and failure of the staticparents[3]pattern.
[2.12.4] - 2026-08-26
Fixed - defer module-scope PyYAML import in sdlc_manager.py (#828)
- Moved
import yamlfrom module scope into_load_live_mimir_coverage(), allowing--helpand every YAML-free subcommand to run on an interpreter without PyYAML installed. - When PyYAML is absent, invoking Team Mimir coverage validation raises a clear
RuntimeErrornaming the missingPyYAMLdependency rather than failing at module import with an unhandledModuleNotFoundError. - Behavior with PyYAML present is unchanged; PyYAML remains a declared dependency.
- Added
test_sdlc_manager_optional_deps.pywith unimportable-yaml simulations and an AST/mutation proof.
[2.12.3] - 2026-08-25
Added - field-named Status/Stage correction identity on flow set-field (#812)
flow set-field --correctionrejects any project field other than Status (and Stage by name). Operator writes of Initiative / Objective / other live fields omit the flag and are unchanged. No new operation.- Correction results carry the field name in operation, authorization, and retry identity
so a saga submission and a retried write name the same field. The reported retry identity
is byte-identical to saga's ledger key
(
reversibility_certificate.idempotency_key), so the two sides correlate on one string. --correctionnow also covers the bulk (--numbers/ repeated--field) path: the restriction is enforced insideflow_set_fields_bulkrather than only in the CLI parser, and a bulk correction is marked in its result with per-card identity.
[2.12.2] - 2026-08-24
Fixed - GraphQL project fields pagination, query-scoped pagination lint, and explicit live parity skip (#584)
- Paginated
QUERY_GET_PROJECT_FIELDSwith cursor andpageInfoviapaginate_or_raiseinget_project_fieldsandboard_census.py:fetch_project_fields_census, eliminating silent field truncation on boards with >30 fields (R1). - Made
check_pagination.pyquery-scoped so unpaginatedfirst:query literals inside files that paginate elsewhere are flagged (R2). - Updated
check_issue_contract_parity.pyto explicitly printSKIPPED live parity leg: <reason>on the default (no--live) path instead of silently omitting the leg (R3 residual). - Verified R4 fail-loud behavior is satisfied by existing #609 test suite.
- Made the query-scoped GraphQL guard fail safe rather than fail open: the literal extractor now backreferences its triple-quote delimiter (a stray
'''previously desynchronized every literal after it) and anyfirst:it cannot place inside a query block still gets the original whole-file check, so the narrowed guard is never weaker than the file-scoped one it replaces. - Recorded a reason on each
# pagination-lint: allowsuppression, matching the convention the helper's own suppression already used.
[2.12.1] - 2026-08-24
Fixed - prepared-issue draft revision replacement semantics and multi-fence readiness validation (#785)
- Fixed
sdlc_manager.pydraft preparation write paths to strip existing YAML front matter and top-level H1 titles from source text and prior drafts, ensuring draft revision replaces prior content instead of accumulating duplicate front-matter blocks and titles. - Added readiness validation blocking check that flags multi-fence drafts (fences > 2) and duplicated section headers (
###duplicates and##/###duplicate section pairs) before creation. - Defensively stripped front-matter fences and H1 titles from
_issue_body_for_githubto guarantee created GitHub issue bodies are clean.
[2.12.0] - 2026-08-16
Changed - retire the hermes-task / hermes-not-actionable dispatch markers
_ISSUE_TYPE_LABELSno longer applies either marker. The Hermes orchestrator that read them was frozen on 2026-07-18 (empty repository list, plan phase off, webhook relay stopped and disabled), so they marked cards for a consumer that cannot run.- Interactive
issue createnow applies the canonical type labels instead. That path previously guaranteed only the dispatch marker — not even the type label — so a card whose browser template failed to prefill landed untyped. _HERMES_ACTIONABLE_TYPESrenamed to_CONTRACT_ISSUE_TYPES: it names the types that carry the eight-section card contract, which is what it has actually meant since the orchestrator froze.- Generated template reference:
Hermes actionable: yes/nobecomesCard contract: required/not required. - Prompts, skills, the operator agent, and the triage command updated to match. The claim that "the orchestrator silently skips cards without
hermes-task" is removed — nothing filters on the label any more.
Paired with the infiquetra-sdlc change that removes both labels from the five issue templates. Writers stop here; the label definitions and the GitHub-side labels are retired separately.
[2.11.0] - 2026-08-15
Fixed - interactive issue create now validates the body before carding it
issue create-preparedhas always refused a draft with blocking readiness gaps. Interactiveissue createhad no equivalent: it opened the browser template, took the pasted issue number, and applied labels + board membership with no body check in between, so a blank or half-filled template landed on the board wearing the same labels as a conformant card.- Adds
_gate_created_issue_body, called between paste-back and metadata, reusing the existingvalidate_card_body_for_contextrather than adding a second contract. - Scoped to the Hermes-actionable types —
explorationandcontext-updateship different field sets by design and are not checked. - A failed issue fetch proceeds with a warning: an API hiccup is not a validation failure and must not strand a good issue.
- Under
--format jsonit emits a structured refusal and never prompts, so non-interactive callers cannot hang. - A failing body is a stop, not a veto: the operator sees the specific gaps plus the exact
gh issue editandflow validate-cardcommands, and must opt in explicitly to card it anyway.
[2.10.2] - 2026-08-08
Added - house-style presentation contract on the SDLC operator agent (#704)
sdlc-operatoragent definition gains a "Presentation contract (Infiquetra house style)" section, copied verbatim fromplugins/house-style/references/subagent-presentation-preamble.md.
[2.10.1] - 2026-07-16
Fixed
- Make
board moveexit nonzero after reporting all project results when an item, Status field or option, or mutation is unavailable. Invalid Status requests still list provider-read options and never call the mutation.
[2.10.0] - 2026-07-14
Added
- Add idempotent
flow assign-mimir --repo REPO --number N. Before its only mutation it reads Team Mimir's exact live coverage from authenticated GitHub, requires an active issue route, an open issue, triage-or-higher operator authority, and the repository's existingintake:mimirlabel. Success is emitted only after issue readback and includes trigger state, coverage route, issue URL, and live Objective project-field values when present. - Fail closed for unsupported repositories, closed issues, insufficient authority, missing or malformed policy, missing trigger labels, API failures, and failed mutation readback. The command creates no labels, comments, repository admissions, or alternate credential paths.
[2.9.0] - 2026-07-14
Added - ship-policy intent envelope at issue capture (#380)
sdlc_manager.py issue intent-envelope— renders the schema-validated ship-policy envelope block (### Intent envelope+ a fencedintent-envelopeJSON block) for embedding in an issue body at capture time, so the operator's autonomy answers are recorded ONCE on the issue and/outcome startskips its run-start interview (ask-once, S-22). Answer values are typed against the fleet interview's closed vocabulary (canonical schema: fleet-corefleet_commons/intent_envelope.py, loaded through the vendored shim) — off-vocabulary values fail loudly at render, never silently coerce.- Prepared-issue readiness validates the envelope —
_readiness_for_prepared_issuetreats a present-but-invalidintent-envelopeblock as a BLOCKING gap (fail closed at capture: a ship policy a downstream/outcome startwill trust must be schema-valid the moment it is authored). An absent block stays fine — the envelope is optional at capture.
[2.8.0] - 2026-07-14
Fixed
- Route every mission-control REST list-fetch (labels, milestones) and GraphQL cursor-paginated
fetch (project items, issue timelines) through a shared
paginate_or_raise/_rest_list_paginatedhelper that raisesPaginationExhaustedErrorinstead of silently returning a partial page when a paginated fetch never reaches a real terminal signal — closes the "item-list pagination silently truncating at 200 of 375 items" fleet defect pattern (#424).
Added
- Add
board_census.py: a live-derived board schema census (field/option shape, mirroring the context-library'scontext_census.pypattern) committed asconfig/board-schema.json, with a--checkmode that fails on drift from a fresh live derivation and prints an explicit SKIPPED line (never a silent pass) when live GitHub Projects access is unavailable. - Add
check_pagination.py: a static lint over mission-control's own scripts, skills, commands, and agent docs for unguardedghlist invocations, bare per-page REST fetches, and GraphQL queries that set a page-size arg without ever checkinghasNextPage. - Add a third,
--live-gated reconciliation leg tocheck_issue_contract_parity.py(live_status_option_errors) that resolves each tracked board's live Status field options and flags anysdlc-schema.json-declared status that no longer resolves upstream — a genuine three-way match (source schema, generated shim, live GitHub) where the prior gate only reconciled the first two.
[2.7.0] - 2026-07-10
Changed
- Retire Objective as a creatable issue type across the CLI and guidance. Objectives are project field options plus Outcome Scorecard docs; Capabilities are top-level by default.
- Make the interactive parent prompt default to no parent and describe native sub-issues as decomposition rather than Objective grouping.
Added
- Add idempotent
flow unlink-sub-issuefor removing accidental or retired parent layers without closing either issue.
[2.6.3] - 2026-07-08
Fixed
- Validate the loaded SDLC label taxonomy before
labels deploycalls GitHub, surfacing overlong descriptions and missing mission-control issue-taxonomy labels as one clear preflight error.
[2.6.2] - 2026-07-08
Fixed
- Add
flow set-field --numbers <n1,n2,...>plus repeated--field/--optionpairs for bulk project-field updates in one CLI invocation. Bulk mode reuses one field-discovery and project-item discovery pass, reports per-item/per-field failures, and exits non-zero after reporting when any requested update fails.
[2.6.1] - 2026-07-08
Fixed
- Normalize mission-control repo arguments at the CLI boundary so
--repo infiquetra/<repo>behaves like--repo <repo>across board, issue, label, milestone, rollout, and flow commands. Repos qualified with a non-Infiquetra owner now fail clearly before any GitHub call.
[2.6.0] - 2026-07-06
Added — auto-stamped Recommended Tier Band on created issues (#368 AC5)
derive_tier_band(issue_type): type→band map mirroring the shared tier registry's work-shape bands — defect/capability →opus/high, enhancement/context-update →sonnet/medium, exploration →sonnet/low;objective(parent tracking card) carries none.- The compile path (
_source_to_issue_body) stamps the derived band as an auto-populated### Recommended Tier Bandbody section (mirroring the Lifecycle Origin discipline: compile-time, never author-required). Idempotent — a body already carrying the band is not double-stamped, so prepare/compile round-trips are safe. - The card validator needs no change: it checks required H3 sections only, so the extra
auto-populated section is accepted as-is. Saga's
tier_defaults.parse_tier_bandis the consumer (format contract covered by a cross-plugin roundtrip test).
[2.5.1] - 2026-07-05
Reformat CHANGELOG title and date separator to canonical grammar (#429)
- Drop the plugin-name suffix from the file title and switch version-heading date separators from em dash to hyphen-minus, as part of the release-surface single-source generator work.
[2.5.0] - 2026-07-04
Executor-profile lint against the fleet tier palette (#463)
- New standalone
scripts/executor_profile_lint.py: parses aRecommended Executor Profileblock from--body-fileor stdin, validates model/effort membership against the canonical fleet-core tier palette (resolved through the new vendoredscripts/fleet_commons_shim.py), and enforces the above-sonnet-requires-justification rule via the palette's ordering. Exit codes: 0 pass, 1 named findings, 2 no-profile-block. No gh calls — callers pipe the issue body in (gh issue view N --json body -q .body | ...).
[2.4.0] - 2026-06-29
Issue-write verbs for autonomous /outcome board-sync (#279)
- New idempotent
issuesubcommands:close/reopen(PATCHstate, naturally idempotent),comment(plain POST; coalescing is the consumer's ledger job), and standalonelabel-add/label-remove(DELETE; swallowsApiNotFoundError404 = label already absent, propagates other errors for the consumer's retry path). Added a_rest_deleteREST helper. - These are the reversible board ops that saga's reversibility certificate authorizes and the
/outcomeboard-sync consumer drives across the saga↔mission-control boundary. Pairs with saga 0.42.0.
[2.3.1] - 2026-06-28
Fixed
- Fixed
issue create-preparedpost-create recovery by resolving issue/PR nodes withissueOrPullRequest(number:), keeping GraphQL mutation failures strict, and recording resumable sidecar state when board-add or Status assignment fails after issue creation.
[2.3.0] - 2026-06-27
Migration notes
- GitHub Project #3 was renamed from Jeff Intent to Operations. The
--projectvalue is nowoperations(wasjeff-intent) across all board / metrics / flow / triage commands, and the internal board key isoperations(wasjeff_intent). Update any saved invocations or scripts that passed--project jeff-intent. Project number (#3), node id, and the sharedintent_flowworkflow are unchanged, so existing cards stay on the board.
Changed
- Renamed the project surface
jeff-intent/jeff_intent/ "Jeff Intent" →operations/ "Operations" acrossproject-mappings.json,sdlc-schema.json,sdlc_manager.py(PROJECT_CHOICES), command and skill guidance, and thesdlc-operatoragent prompt. Mirrors the canonical rename ininfiquetra-sdlc. Dated provenance and migration notes preserve the former name as historical record.
[2.2.0] - 2026-06-21
Changed
sdlc-operatoragent: pinmodel: sonnet(wasinherit). Orchestration work is structured/mechanical — Sonnet is the right cost-quality tier (R1/R2a tiering).
[2.1.0] - 2026-06-17
Migration notes
- Mission Control now treats Jeff Intent, Asgard, and CAMPPS as the active board surfaces. Mount Olympus remains historical context in the vendored SDLC schema and should not be used as an active routing target for new work.
sdlc-schema.jsonresolves frominfiquetra-sdlcon GitHubmainbefore falling back to the vendored snapshot, so operators are less likely to read stale local checkout state.
Changed
- Re-vendored the current
infiquetra-sdlcschema snapshot atschema_version: 2026-06-17. - Prepared actionable issue drafts now compile fallback bodies from generated SDLC issue-contract data instead of hand-written prompt template sections.
- Readiness validation now applies issue-type and risk-aware required fields, including high-risk sections and empty-section rejection.
- Updated issue command and skill guidance to route through
issue preparefor generated contract bodies. - Bumped plugin and marketplace metadata to
2.1.0.
[1.6.1] - 2026-05-31
Changed
- Tracked the
infiquetra-loop→sagarename: updated the ignored runtime-state path to.claude/saga/and refreshed plugin references in/issue, thesdlc-operatoragent, and theissuesskill.
[Unreleased]
Added
- Added
/issueas the primary issue command surface. - Added prepared-issue handoff maturity metadata and source artifact resolution for local files, GitHub issue/PR URLs, branch refs, and natural hints such as "from the brainstorm".
Changed
- Synced the vendored SDLC schema and Asgard prepared-issue readiness with the corrected Asgard/Olympus model: sibling target boards with explicit cross-team transfer, not an implied Asgard-to-Olympus promotion path.
[1.6.0] - 2026-05-30
Added
- Added
issue prepareto write team-aware Asgard or Mount Olympus issue drafts underdocs/sdlc-issue-drafts/with JSON readiness sidecars. - Added
issue create-preparedto re-run readiness, render a full mutation plan, ask for final confirmation, repair missing labels/templates, add issues to the requested project, set safe starting status, and record the created issue back onto the draft. - Added Asgard and Olympus readiness profiles. Asgard accepts shaping-quality drafts with mode,
constraints, risk, and transfer notes; Olympus requires the strict actionable card body,
expected labels, project presence, author-visible risk, and safe
Backlogstatus. - Added mocked tests for blocked create, declined confirmation, direct create, missing labels/templates, missing mapping PR stop, and mapping override creation.
Changed
- Updated
issues,/issue,sdlc-operator, README, and release metadata so natural-language requests like "create an Olympus issue from this text" route through prepared drafts instead of bypassing readiness checks. - Bumped plugin and marketplace metadata to
1.6.0.
[1.5.0] - 2026-05-30
Migration notes
- No breaking CLI changes. Existing commands continue to work. Operators should update
installed cache paths from
mission-control/1.0.0or1.4.0tomission-control/1.5.0after installing this release. - Current actionable issue templates use
hermes-task,needs-plan, and the type label.needs-analysisandneeds-triageremain documented only as legacy auto-label fallback labels fromlabels.json.
Changed
- Added vendored
config/sdlc-schema.jsonand taught board/metric helpers to consume schema-backed boards, workflows, WIP limits, and terminal statuses. - Added live Jeff Intent (#3) and Asgard (#2) project mappings plus explicit
--projecttargeting for board add/move. - Refreshed board, metrics, milestones, labels, rollout, and operator guidance around Jeff Intent, Asgard, Olympus, direct project fields, and deployment-state separation.
- Preserved read compatibility for live/legacy Olympus statuses such as
In Progress,In Development, andDeployedwhile guiding new movement to the current schema. - Synced
issuestemplate guidance with canonical issue forms ininfiquetra-sdlc, including current Hermes actionable labels and required card sections. - Added a deterministic template documentation generator plus drift guard tests for
templates-reference.md. - Restored legacy rollout WIP-limit fallback when schema-backed limits are absent.
- Aligned
sdlc-operator,/triage, and issue/label references so prompts no longer teach stale actionable labels or initiative/objective labels as current practice. - Bumped plugin and marketplace metadata to
1.5.0.
[1.4.0] - 2026-05-04
Migration notes
- No breaking changes. The
sdlc-operatorClaude Code subagent (agents/sdlc-operator.md) was rewritten to reflect 2026-05-04 reality (Phase C minimum-viable + foundation + interactive flow are all merged). Operators using the prior subagent will see the same trigger phrases but newer command examples.
Changed (Phase C deferred — sdlc-operator subagent rewrite)
agents/sdlc-operator.md— full rewrite. The previous version was authored before Phase C; it referenced removed concepts:- Beads/Dolt CLI (
bd ready,bd claim, etc. — removed in PR #114; coordination is now Redis pub/sub + GitHub Projects v2 + Discord per-card threads) - "2 project boards" (Strategic Direction + Mount Olympus — Strategic Direction was dropped in PR #16 from
infiquetra-sdlc; Mount Olympus is the only board) labels sync-fieldscommand (deprecated — Initiative/Objective are project fields, not labels per PR #16 ininfiquetra-sdlc. The command itself still exists in the script for back-compat;flow set-fieldis the canonical mechanism. Removing the command is queued for a follow-up after a deprecation cycle.)initiative:*/objective:*colon-prefixed labels (anti-pattern; removed in PR #11)
- Beads/Dolt CLI (
- The rewrite reflects the cumulative Phase C work — references the
flowsubcommand group (PR #114), per-user defaults file + typed exceptions (PR #115), the sub-issue-first interactiveissue createflow (PR #116). Each workflow pattern (full issue lifecycle, board grooming, new initiative/objective setup, objective tracking, blueprint-driven batch creation, triage) updated to use the canonicalflow set-field/flow link-sub-issue/flow verify-label/flow validate-cardhelpers. - The
Today's reality (2026-05-04)callout is repeated in the agent's docstring + the issue-create workflow section — an operator using the agent learns immediately that today onlyStatusis a single-select field on the Olympus board, and theflowhelpers will silently skip prompts for the other fields until they're created via the runbook.
Plugin manifest: 1.3.0 → 1.4.0. All deferred Phase C items are now closed.
[1.3.0] - 2026-05-04
Migration notes
- No breaking changes. Existing
issue create --repo X --type Yinvocations work identically; new behavior is additive. - The new flow is interactive by default. Pass
--skip-metadatafor the prior thin-wrapper behavior (just open browser, no post-create work). - Operators who ran
config init-defaults(Phase C foundation) get personalized prompt defaults automatically.
Added (Phase C deferred — Interactive issue create rewrite)
The issue create subcommand was rewritten as a sub-issue-first, per-project-schema-aware, defaults-driven, capability-adaptive interactive flow. Replaces the previous thin wrapper that just deferred to gh issue create --web.
The 10-step flow:
- Determine type (decision-tree prompt if
--typenot set; default from~/.claude/sdlc-defaults.jsonif present) - Sub-issue-first prompt: every new card has a parent by default. Operator pastes a
repo#Nref or typesnofor free-floating - Discover which project the repo maps to (per-user
default_projectpreferred, else first match) - Per-project schema discovery: for each candidate field (Initiative, Objective, Status, plus capability-adaptive ones), check if the project actually exposes the field. Silently skip prompts for fields that don't exist
- Prompt for field values, defaults seeded from
~/.claude/sdlc-defaults.json(e.g.,default_initiative,default_objective) - Capability-adaptive fields: for
capabilityorobjectivetypes only, AND only when the project exposes them, prompt forCapability Size,Business Value,Technical Risk,Target Quarter - Open
gh issue create --repo X --template <type>.yml --webin the browser; operator fills the body - Operator pastes back the issue number (URL or bare integer accepted); skip post-create metadata if blank
- Apply post-create metadata, each step isolated:
hermes-taskfor actionable types /hermes-not-actionablefor objectiveboard addto the project boardflow set-fieldfor each gathered field valueflow link-sub-issueif a parent was set
- Paired-card prompt (opt-in, default no): create a sibling card on a different repo with the same parent. Useful for cross-service capabilities (backend + Flutter app)
New CLI flags
--parent-ref <repo#N>: pre-supply the parent ref (skips the sub-issue prompt — useful for batch-create scripts)--skip-metadata: skip steps 2-10 entirely, just open the browser. Returns to the prior thin-wrapper UX
New helpers (composable for tests + scripts)
_select_issue_type(default=None)— decision-tree prompt with EOF-safe fallback_prompt_parent_issue()— sub-issue-first prompt; parsesrepo#Noryesfollowed by ref_project_field_options(project_name, field_name)— per-project schema discovery; returns None if field doesn't exist (caller skips silently)_prompt_choice(label, options, default)— generic field-value prompt with options listed + default +-to skip_open_gh_issue_create_web(repo, issue_type)— browser launch helper_prompt_issue_number(repo)— captures URL or bare int from operator_apply_post_create_metadata(...)— orchestrates label / board / fields / sub-issue link with per-step error isolation_prompt_paired_card(repo, issue_number)— opt-in paired-card prompt; returns target/title/body deltas or None
Tests
- 23 new tests in
tests/test_issue_create_interactive.pycovering each helper in isolation:_select_issue_type: typed choice, default-on-blank, garbage-fallback, EOF-safe_prompt_parent_issue: direct ref, yes-then-ref, blank-then-ref, no, garbage-warns, EOF_prompt_choice: skip-when-no-field, valid option, default-on-blank, dash-skip, invalid-warns_prompt_paired_card: default-no, yes-collects-deltas, yes-empty-target-aborts_apply_post_create_metadata: hermes-task for actionable, hermes-not-actionable for objective, label-failure-doesnt-abort-others, no-project-skips-fields-but-keeps-link_PARENT_REF_RE: realistic refs accepted;#42,repo-only, garbage rejected
- Total: 92 / 92 passing
[1.2.0] - 2026-05-04
Migration notes
- No breaking changes. Existing CLI invocations work identically.
- Recommended one-time setup: run
python3 sdlc_manager.py config init-defaultsafter upgrading to seed~/.claude/sdlc-defaults.jsonwith your gh login + project defaults. Future Phase C PRs will read these as suggestion values in the interactiveissue createflow. - External override still wins: if you have
$INFIQUETRA_SDLC_PATH/config/project-mappings.json, it continues to take precedence over the new vendored copy. - Exception class names changed (PEP 8 / N818):
ApiNotFound→ApiNotFoundError,ApiAlreadyExists→ApiAlreadyExistsError,ApiRateLimited→ApiRateLimitedError. Backward-compat aliases retained for the old names; can be removed in a follow-up after a deprecation cycle.
Added (Phase C deferred — Foundation)
- Typed exception classes (
GhApiError,ApiNotFound,ApiAlreadyExists,ApiRateLimited,ApiAuthError,CardValidationError)._ghnow raises the appropriate subclass via_classify_gh_errorinstead of bareRuntimeErrorwith a stringy message. Replaces the fragile"422" in str(e)substring matching pattern. Downstream callers catch by type:Thetry: _rest_post(...) except ApiAlreadyExists: # idempotent re-run — treat as successflow_link_sub_issueandflow_verify_labelhelpers were refactored to use this pattern.flow_verify_labelalso now handles a real race (two operators creating the same label simultaneously) viaApiAlreadyExistson POST. - Per-user defaults file at
~/.claude/sdlc-defaults.json. Stores:assignee(gh login fromgh api user --jq .login— not OS$USER),default_project,default_status,default_priority,default_initiative,default_objective,preferred_repos. Sticky across CLI invocations; future interactive flows read defaults as suggestion values.load_user_defaults()/save_user_defaults()helpers (atomic write via tempfile + rename; tolerant of missing file + malformed JSON)get_default(key)convenience reader- New subcommands:
config show-defaults— display current per-user defaultsconfig init-defaults [--non-interactive]— first-run wizard. Interactive by default;--non-interactiveseeds fromgh api user+ auto-detectsdefault_projectif exactly one project is mapped (no guessing on multi-project orgs).
- Vendored
project-mappings.jsonatplugins/mission-control/config/project-mappings.json. The plugin now works without an externalinfiquetra-sdlccheckout for canonical Infiquetra projects.- New
_resolve_project_mappings(sdlc_path)helper implements the documented resolution order: external override ($INFIQUETRA_SDLC_PATH/config/project-mappings.json) → vendored canonical → remotegh apifallback. - Project node IDs captured in the vendored file are best-effort + verified 2026-05-04. Field/option IDs are NEVER cached; always fetched live.
- New
Tests
- 33 new tests across 3 new test files:
test_typed_exceptions.py(15 tests) — status-code parsing (404/401/403/429/422 disambiguation), 422-already-exists vs 422-validation-failure distinction, integration withflow_link_sub_issue+flow_verify_label(including race detection)test_user_defaults.py(13 tests) — read-side (missing file, malformed JSON, non-object root, unset key), write-side (atomic via tmpfile+rename, parent-dir auto-create, round-trip),--non-interactivewizard (gh-login seeded, multi-project no-guess, preserves unknown future keys)test_project_mappings_resolution.py(5 tests) — override wins over vendored, vendored used when no override, remote fallback when neither, empty dict on all-three failure, vendored file declares expected canonical state
- Updated 3 PR #114 tests in
test_flow_subcommands.pyto use typed exceptions instead of bareRuntimeErrorwith string-matching (the old pattern was testing the old implementation; new tests test the new contract). - Total: 64 / 64 passing locally.
[1.1.0] - 2026-05-04
Fixed (during PR #114 review)
validate_card_bodyheader text: was"Out-of-scope or non-goals"(with "or"); home-lab card_validator.py and ALL actionable issue templates use"Out-of-scope / non-goals"(with "/"). Every real card would have failed validation with a phantom missing-section error. Corrected to match home-lab exactly. Same iteration also corrects_PATH_LINE_RE(now accepts plain filenames + bullet-prefixed paths matching home-lab) and_CHECKLIST_RE(now requires\Safter[ ]and accepts*bullets matching home-lab).- Drift-guard test: added
test_required_headers_match_actual_issue_templatesthat loads the real issue templates and verifies every_REQUIRED_H3_HEADERSentry has a matchinglabel:in capability/enhancement/defect.yml. This is the test that would have caught the original "or" vs "/" bug. - Pre-existing test breakage:
tests/test_sdlc_manager.pyhad aTestBeadsClaimclass referencing the deletedbeads_claimfunction. Class deleted; CHANGELOG note added in the test file. - WIP-limits test fixture: was using the old
beads_configconfig-key (which now degrades to{}); the override test passed only by coincidence. Renamed fixture tolegacy_rollout_config; tightened assertion to verify the OVERRIDDEN column (Ready) renders with the override limit, not just any column happening to have a "5" in its render. flow_validate_cardexit-code path: was callingsys.exit(1)directly, bypassing main()'s formatted-error path and breaking programmatic callers. Now raisesCardValidationError(RuntimeError subclass) which main() catches; preserves the standard CLI exit code behavior + lets future callers inspect the failure._resolve_project_fielderror message: now includes a hint pointing at the field-creation runbook ininfiquetra-sdlc/docs/operations/operational-reference.mdwhen a field doesn't exist.- Stale
# BEADSbanner: removed (Beads removal had left an empty banner in the argparse section). - Duplicate
import re: removed (validator block had a redundant import; top-levelimport reat line 61 already covers it). - Unused
project_id_checkvariable: replaced with_, items = ...discard idiom (consistent withboard_wippattern). - README.md: removed the "Beads coordination" capability bullet + stale
bdCLI prerequisite + staleBeads Operationssection + staleconfig/beads-config.jsonrow. AddedFlow Operationssection with the 6 new commands.
Added
- New
flowsubcommand group — operator-facing GraphQL + REST helpers:flow set-field— set a single-select project field on a card (Initiative, Objective, Status, etc.)flow field-options— list current options for a project field (live discovery, IDs not cached)flow discover-project— resolve which project a repo maps toflow link-sub-issue— link child as native sub-issue of parent (cross-repo, idempotent)flow verify-label— self-healing label create (404 → create, exists → no-op, other errors raise)flow validate-card— pre-flight check an existing issue body against the card_validator schema
- New
validate_card_body()Python helper — mirrors home-lab card_validator.py's high-leverage checks (6 required H3 headers, AC has ≥1 checklist, Verification has ≥1 fenced code block, Files-expected has ≥1 path-like line, no placeholder-only sections) - New
flowskill (SKILL.md) describing theflowcommands + idempotency contract + hard rules + integration with the blueprint-to-issue workflow - 20 tests in
tests/test_card_validator.pyandtests/test_flow_subcommands.pycovering: validator schema rules, idempotency contracts, error classification (404 vs auth vs server), live-discovery vs cached, helpful error messages with current options listed
Changed
- Renamed
beads_configconfig-key tolegacy_rollout_configinload_configand downstream readers (board_wip,rollout_status,rollout_update,config_show). The underlying file (beads-config.json) was already removed from infiquetra-sdlc on 2026-04-26; the key now degrades gracefully to{}for back-compat. Comment inload_configdocuments the migration.
Removed
beadssubcommand group (ready,claim,update,complete,status)_bdshell helper for invoking thebdCLI- All
beads_*Python functions - The Beads/Dolt coordination layer was removed from Mount Olympus on 2026-04-26 (see
infiquetra-sdlc/docs/engineering-journal/narratives/2026-04-26-beads-dolt-removed.md); the agent fleet now coordinates via Redis pub/sub + GitHub Projects v2 + Discord per-card threads. The plugin'sbeadscommands targeted infrastructure that no longer exists.
Migration notes
- Operators previously running
sdlc_manager.py beads <action>will get anargparseerror. There is no replacement — the underlying coordination has moved off Beads entirely. Usegh+ the newflowcommands for direct board operations. - The
flow set-fieldcommand is the canonical mechanism for Initiative + Objective assignment per the 2026-05-03 DECISION (seeinfiquetra-sdlc/docs/engineering-journal/DECISIONS.md). The fields don't exist on the Olympus board today; create them via the operator runbook inoperational-reference.mdbefore usingflow set-field.
[1.0.0] - 2026-03-29
Added
- Initial release of mission-control plugin for the Mount Olympus agent team
- Updated wording to use Infiquetra conventions consistently
- 6 skills: board, issues, labels, metrics, milestones, rollout
- 4 commands: /board, /issue, /metrics, /triage
- 1 agent: sdlc-operator
- Python CLI: sdlc_manager.py (zero external dependencies, gh CLI wrapper)
- New: beads resource group for Beads/Dolt task coordination
- Removed: Rally sync, GHE hostname requirements, Chainproofers/Identifiers team split
- Coordination model: Beads-first with GitHub Issues as backing store
- Notifications: Discord (not Slack)
- 2 project boards: Strategic Direction + Mount Olympus Operations