Yeachan-Heo/oh-my-codex · error · UltragoalError
Final architecture-invariant gate requires architectureInvar
Error message
Final architecture-invariant gate requires architectureInvariantGate.status="passed"; record blocker-resolution work for unproved invariants.
What it means
The architectureInvariantGate was provided but its status is not 'passed'. The final gate treats unproved invariants as unresolved: you must either prove them (status='passed' with supporting evidence) or do blocker-resolution work first.
Source
Thrown at src/ultragoal/artifacts.ts:1637
}
}
return uniqueRequiredArchitectureInvariants(invariants);
}
async function collectRequiredArchitectureInvariants(cwd: string): Promise<RequiredArchitectureInvariant[]> {
const briefInvariants = extractArchitectureInvariantsFromBrief(await readFile(ultragoalBriefPath(cwd), 'utf-8'));
const steeringInvariants = extractArchitectureInvariantsFromAcceptedSteering(await readSteeringLedgerEntries(cwd));
return uniqueRequiredArchitectureInvariants([...briefInvariants, ...steeringInvariants]);
}
function validateArchitectureInvariantGate(gate: Partial<UltragoalQualityGate>, requiredInvariants: readonly RequiredArchitectureInvariant[]): void {
const invariantGate = gate.architectureInvariantGate;
if (!invariantGate || typeof invariantGate !== 'object') {
throw new UltragoalError('Final quality gate is missing architectureInvariantGate evidence; include derived architecture/domain invariants, source artifacts, implementation/test/review evidence, or record final blockers for unproved invariants.');
}
if (invariantGate.status !== 'passed') {
throw new UltragoalError('Final architecture-invariant gate requires architectureInvariantGate.status="passed"; record blocker-resolution work for unproved invariants.');
}
if (!Array.isArray(invariantGate.sourceArtifacts)) {
throw new UltragoalError('Final architecture-invariant gate requires architectureInvariantGate.sourceArtifacts.');
}
const sourceArtifacts = invariantGate.sourceArtifacts.map((source) => assertNonEmpty(source, 'architectureInvariantGate.sourceArtifacts[]'));
for (const required of requiredInvariants) {
if (!sourceArtifacts.some((source) => sourceReferencesArtifact(source, required.sourceArtifact))) {
throw new UltragoalError(`Final architecture-invariant gate sourceArtifacts must include required invariant source artifact: ${required.sourceArtifact}`);
}
}
assertNonEmpty(invariantGate.evidence, 'architectureInvariantGate.evidence');
if (!Array.isArray(invariantGate.invariants)) {
throw new UltragoalError('Final architecture-invariant gate requires architectureInvariantGate.invariants.');
}
const provided = new Map<string, UltragoalArchitectureInvariantEvidence[]>();
for (const invariant of invariantGate.invariants) {
if (!invariant || typeof invariant !== 'object') throw new UltragoalError('Final architecture-invariant gate invariants must be objects.');
const record = invariant as Partial<UltragoalArchitectureInvariantEvidence> & { blockers?: unknown };View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Resolve the outstanding invariants and set architectureInvariantGate.status = 'passed'
- Complete the blocker-resolution work recorded in the gate, then update status and evidence
- If the plan genuinely cannot pass, keep it unfinalized (or reopen it) rather than forcing a non-passed final gate
- Add a pre-finalize assertion in your pipeline that gate.architectureInvariantGate?.status === 'passed'
Example fix
// before
architectureInvariantGate: { status: 'blocked', sourceArtifacts: ['docs/i.md'], evidence: 'x', invariants: [] }
// after
architectureInvariantGate: { status: 'passed', sourceArtifacts: ['docs/i.md'], evidence: 'invariants proved via tests + review', invariants: [] } Defensive patterns
Strategy: validation
Validate before calling
if (gate.architectureInvariantGate?.status !== 'passed') throw new Error('resolve invariant blockers before finalizing'); Type guard
function invariantGatePassed(gate: UltragoalQualityGate): boolean { return gate.architectureInvariantGate?.status === 'passed'; } Prevention
- Run a pre-finalize assertion that all gate sections have status 'passed'
- Treat 'blocked' as a stop signal: do blocker-resolution work first
- Don't reuse draft gate snapshots in finalization
When it happens
Trigger: Submitting a final gate with architectureInvariantGate.status set to 'blocked', 'pending', or omitted, while still attempting to finalize the plan.
Common situations: Marking invariants as blocked during a mid-flight check and forgetting to flip status before finalizing; automation copying a draft gate snapshot; misunderstanding that recorded blockers are acceptable mid-plan but not at finalization.
Related errors
- Final quality gate is missing architectureInvariantGate evid
- Final architecture-invariant gate requires architectureInvar
- Final architecture-invariant gate sourceArtifacts must inclu
- Final code-review must use an independent architect subagent
- Invalid --codex-goal-mode; expected aggregate or per-story.
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/bced7e9c4818c282.
Report an issue: GitHub.