Yeachan-Heo/oh-my-codex · error · UltragoalError
Final architecture-invariant gate requires architectureInvar
Error message
Final architecture-invariant gate requires architectureInvariantGate.sourceArtifacts.
What it means
The architectureInvariantGate is missing the sourceArtifacts array. Validation requires an array (even empty, though entries are then checked individually and required invariants must be referenced) listing the source artifacts the invariants derive from.
Source
Thrown at src/ultragoal/artifacts.ts:1640
}
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 };
const text = assertNonEmpty(record.invariant, 'architectureInvariantGate.invariants[].invariant');
const source = assertNonEmpty(record.source, 'architectureInvariantGate.invariants[].source');
if (!sourceReferencesAnyArtifact(source, sourceArtifacts)) {View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Add sourceArtifacts as an array of artifact reference strings (e.g. ['docs/architecture/invariants.md'])
- Ensure each entry references the source artifacts required by the plan's required invariants (they are matched via sourceReferencesArtifact)
- If unsure which artifacts, read the plan's requiredInvariants and mirror their sourceArtifact values
- Type the gate payload as UltragoalQualityGate so TypeScript flags the omission
Example fix
// before
architectureInvariantGate: { status: 'passed', evidence: 'ok', invariants: [] }
// after
architectureInvariantGate: { status: 'passed', sourceArtifacts: ['docs/architecture/invariants.md'], evidence: 'ok', invariants: [] } Defensive patterns
Strategy: type-guard
Validate before calling
const aig = gate.architectureInvariantGate;
if (!Array.isArray(aig?.sourceArtifacts)) throw new Error('sourceArtifacts array required'); Type guard
function hasSourceArtifacts(g: unknown): g is { sourceArtifacts: string[] } { return Array.isArray((g as any)?.sourceArtifacts); } Prevention
- Use the library's own types for gate payloads
- Centralize gate construction in one helper that includes all required arrays
- Add a schema check (e.g. zod) over the gate before submitting
When it happens
Trigger: Providing architectureInvariantGate with status='passed' and evidence but omitting sourceArtifacts, or setting it to a non-array value (string, object).
Common situations: Hand-constructing the gate object and forgetting the field; copying an example that predates the sourceArtifacts requirement; type loosened to any so the omission isn't caught at compile time.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- Final architecture-invariant gate sourceArtifacts must inclu
- Final quality gate is missing architectureInvariantGate evid
- Final architecture-invariant gate requires architectureInvar
- 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/450d651ade47e4b8.
Report an issue: GitHub.