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

  1. Add an architectureInvariantGate object to the gate with status, sourceArtifacts, evidence, and invariants
  2. Populate invariants from the plan's required architecture/domain invariants and cite the source artifacts
  3. If invariants cannot be proved, record final blockers per the guidance instead of omitting the gate
  4. 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

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


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