Yeachan-Heo/oh-my-codex · error · UltragoalError
Final quality gate is missing architectureInvariantGate evid
Error message
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.
What it means
The final quality gate validation found no architectureInvariantGate object. Finalizing a plan requires explicit architecture-invariant evidence: derived invariants, source artifacts, implementation/test/review evidence, or recorded blockers.
Source
Thrown at src/ultragoal/artifacts.ts:1634
].filter((value): value is string => typeof value === 'string' && value.trim().length > 0);
for (const text of texts) {
invariants.push(...extractArchitectureInvariantsFromArtifact(text, `${ULTRAGOAL_DIR}/${ULTRAGOAL_LEDGER}`, sourcePrefix));
}
}
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[]>();View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Add an architectureInvariantGate object to the gate with status, sourceArtifacts, evidence, and invariants
- Populate invariants from the plan's required architecture/domain invariants and cite the source artifacts
- If invariants cannot be proved, record final blockers per the guidance instead of omitting the gate
- Update stale gate-building code from earlier tool versions
Example fix
// before
gate = { tests: { status: 'passed', evidence: '...' } }; // no architectureInvariantGate
// after
gate = {
tests: { status: 'passed', evidence: '...' },
architectureInvariantGate: {
status: 'passed',
sourceArtifacts: ['docs/architecture/invariants.md'],
evidence: 'invariants verified in review + tests',
invariants: [],
},
}; Defensive patterns
Strategy: type-guard
Validate before calling
const gate = buildGate();
if (!gate.architectureInvariantGate || typeof gate.architectureInvariantGate !== 'object') throw new Error('add architectureInvariantGate before finalize'); Type guard
function hasInvariantGate(gate: Partial<UltragoalQualityGate>): gate is UltragoalQualityGate { return !!gate.architectureInvariantGate && typeof gate.architectureInvariantGate === 'object'; } Try / catch
try { await finalize(cwd, { gate }); } catch (e) { if (e instanceof UltragoalError && e.message.includes('architectureInvariantGate evidence')) { /* build and attach invariant gate, retry */ } else throw e; } Prevention
- Type gate payloads as UltragoalQualityGate so missing fields fail at compile time
- Keep a gate-builder helper that always emits all required sections
- Record blockers explicitly rather than omitting the gate when invariants are unproved
When it happens
Trigger: Calling finalize with a gate object that omits architectureInvariantGate entirely or sets it to a non-object (null, string, array) — e.g. building a minimal gate with only tests/review evidence.
Common situations: Older integrations written before the architecture-invariant gate existed; minimal/hand-rolled gate payloads; spreading partial gate objects where the field is optional in the author's local type but required at runtime.
Related errors
- Final architecture-invariant gate requires architectureInvar
- 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/1499822b46354a70.
Report an issue: GitHub.