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

  1. Add sourceArtifacts as an array of artifact reference strings (e.g. ['docs/architecture/invariants.md'])
  2. Ensure each entry references the source artifacts required by the plan's required invariants (they are matched via sourceReferencesArtifact)
  3. If unsure which artifacts, read the plan's requiredInvariants and mirror their sourceArtifact values
  4. 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

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


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