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

  1. Read the plan's required invariants and include each required.sourceArtifact verbatim in sourceArtifacts
  2. 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)
  3. Check for path-root/case mismatches: the entry must satisfy sourceReferencesArtifact(entry, required.sourceArtifact)
  4. 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

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


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/f257817f0fbf7ae2. Report an issue: GitHub.