affaan-m/ECC · error · RetrospectiveError

retrospective.invalid_capsule

retrospective.invalid_capsule

Error message

capsule at input index ${index} failed verification

What it means

readProjection verifies each capsule directory before including it in a retrospective report. When capsule.project(dir) fails — meaning the capsule's journal fails integrity, digest, or structural verification — the error is replaced by a sanitized RetrospectiveError carrying only the input index. Raw reader errors are deliberately swallowed because they can contain private paths or journal content that must not cross the report boundary.

Solutions

  1. Regenerate the failing capsule snapshot and re-run the retrospective
  2. Verify the directory is a complete capsule produced by this harness version (check journal and digests)
  3. Re-copy the capsule directory in full if it was truncated during transfer
  4. Confirm you passed the directory itself, not a parent or a file path

Example fix

// before
node scripts/eval-harness.js capsule group ./random-dir ./capsule-b
// after (regenerate the invalid capsule first)
node scripts/eval-harness.js capsule group ./.claude/capsules/run-123 ./capsule-b
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check: capsule dir contains a journal before grouping
if (!fs.existsSync(path.join(dir, 'journal.jsonl'))) throw new Error(`${dir} is not a capsule`);

Type guard

const looksLikeCapsule = (dir) => typeof dir === 'string' && fs.existsSync(dir) && fs.statSync(dir).isDirectory() && fs.existsSync(path.join(dir, 'journal.jsonl'));

Try / catch

try { report = groupCapsules(dirs); } catch (e) { if (e.code === 'retrospective.invalid_capsule') console.error(`capsule #${e.index} failed verification — regenerate it`); throw e; }

Prevention

When it happens

Trigger: Passing a directory to groupCapsules that is not a valid capsule (missing journal, corrupted hash chain, tampered artifacts, or a non-capsule path). The failing input is identified by its zero-based index in the dirs array.

Common situations: Pointing the tool at an ordinary project directory instead of a capsule snapshot; a capsule truncated by an interrupted copy or sync; editing capsule files by hand so link digests no longer verify; version drift where the capsule format differs from what this harness version projects.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/2dc03053e6a0dca5. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/eval-harness/retrospective.js:29

const MAX_INPUTS = 100;
const SOURCE_FIELDS = ['entry_count', 'root_hash', 'journal_sha256', 'projection_hash'];

class RetrospectiveError extends Error {
  constructor(code, message, inputIndex) {
    super(message);
    this.name = 'RetrospectiveError';
    this.code = code;
    if (inputIndex !== undefined) this.input_index = inputIndex;
  }
}

function readProjection(dir, index) {
  try {
    return capsule.project(dir);
  } catch {
    // Reader errors can contain private paths or journal content. No partial
    // report or unchecked diagnostic content crosses the report boundary.
    throw new RetrospectiveError('retrospective.invalid_capsule', `capsule at input index ${index} failed verification`, index);
  }
}

const compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
const identity = projection => JSON.stringify([projection.run_id, projection.capsule_id]);

function summarizeGroup(harnessVersion, projections) {
  const total = keys => Object.fromEntries(keys.map(key => [key, 0]));
  const byLineage = total(LINEAGES);
  const byEffect = total(EFFECT_CLASSES);
  for (const projection of projections) {
    for (const key of LINEAGES) byLineage[key] += projection.by_lineage[key];
    for (const key of EFFECT_CLASSES) byEffect[key] += projection.by_effect_class[key];
  }
  return {
    harness_version: harnessVersion,
    capsule_count: projections.length,
    entry_count: projections.reduce((sum, item) => sum + item.entry_count, 0),

View on GitHub (pinned to 8321021c54)