Yeachan-Heo/oh-my-codex · error · UltragoalError
Final architecture-invariant gate sourceArtifacts must inclu
Error message
Final architecture-invariant gate sourceArtifacts must include required invariant source artifact: ${required.sourceArtifact} What it means
None of the gate's sourceArtifacts entries reference a source artifact that a required architecture invariant derives from. For every required invariant, at least one sourceArtifacts entry must match via sourceReferencesArtifact (e.g. path containment/suffix match on required.sourceArtifact).
Source
Thrown at src/ultragoal/artifacts.ts:1645
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)) {
throw new UltragoalError(`Final architecture invariant "${text}" source must reference one of architectureInvariantGate.sourceArtifacts; decorative provenance labels are not sufficient.`);
}
if (record.status !== 'proved') throw new UltragoalError(`Final architecture invariant "${text}" is not proved; record blocker-resolution work before final completion.`);
if (record.blockers !== undefined) throw new UltragoalError(`Final architecture invariant "${text}" has blockers; record blocker-resolution work before final completion.`);
assertNonEmpty(record.implementationEvidence, 'architectureInvariantGate.invariants[].implementationEvidence');View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Read the plan's required invariants and include each required.sourceArtifact verbatim in sourceArtifacts
- If docs were moved, include both the new path and a reference that still matches the stored source (or re-derive invariants from the new location)
- Check for path-root/case mismatches: the entry must satisfy sourceReferencesArtifact(entry, required.sourceArtifact)
- List every required invariant's source, not just one
Example fix
// before (invariant requires 'docs/architecture/invariants.md') sourceArtifacts: ['notes/adr-001.md'] // after sourceArtifacts: ['notes/adr-001.md', 'docs/architecture/invariants.md']
Defensive patterns
Strategy: validation
Validate before calling
const plan = await readUltragoalPlan(cwd); const required = plan.architectureInvariants ?? []; // required invariants const cited = new Set(gate.architectureInvariantGate.sourceArtifacts); const missing = required.filter(r => ![...cited].some(c => c === r.sourceArtifact || c.endsWith(r.sourceArtifact) || r.sourceArtifact.endsWith(c))); if (missing.length) gate.architectureInvariantGate.sourceArtifacts.push(...missing.map(m => m.sourceArtifact));
Prevention
- Mirror each required invariant's sourceArtifact into sourceArtifacts when building the gate
- Keep architecture docs at stable paths after invariants are derived
- When docs move, include both old and new paths or re-derive invariants
When it happens
Trigger: Finalizing with sourceArtifacts that cite different files than the ones the plan's required invariants were derived from — e.g. ['docs/my-notes.md'] when an invariant requires 'docs/architecture/invariants.md'.
Common situations: Renaming/moving architecture docs after invariants were derived; citing a summary doc instead of the canonical invariant source; case or path-root differences between the stored artifact path and the cited string; multiple invariants each needing distinct sources that weren't all listed.
Related errors
- Final architecture-invariant gate requires architectureInvar
- 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/f257817f0fbf7ae2.
Report an issue: GitHub.