[Bug]: sculptPipeline.completedPasses can claim passes the ledger never credited

Author: mariusargintCreated Aug 31, 2026Updated Sep 1, 2026
Labelstriage: needs-review

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I confirmed this is a reproducible behavior problem, not a request for help using the project.

What happened?

sculptPipeline.completedPasses reads as a statement of fact and can be fiction.

A spec in hand listed seven completed passes:

json
"completedPasses": ["blockout","structural-pass","form-refinement","material-pass",
                    "surface-pass","lighting-pass","interaction-pass"]

while reviewHistory was [] and the gate had credited zero. Anyone reading the spec — or resuming work on it weeks later — would reasonably believe six or seven passes of review had happened. None had.

The field is derived by sync_pipeline(), which is correct. The problem is that a stale value persists in any spec not re-synced since it was written, and nothing in the file marks it as derived rather than authored.

Reproduction steps

  1. Take any spec whose sculptPipeline.completedPasses was written before the ledger existed, or hand-edit that array.
  2. Do not run append_review.py, so reviewHistory stays [].
  3. Read the spec, or run --strict-quality. The array still claims the passes; nothing flags the contradiction.

Reference image or input characteristics

Not input-dependent. Any spec, any subject.

Relevant output and evidence

Expected: a reader cannot mistake a cached projection for the ledger.

Actual: the cached projection is a plain array of pass names at the top of the pipeline block, and the ledger it projects lives somewhere else entirely.

Any of these would close it, smallest first:

  1. Have sync_pipeline() write provenance alongside the value, e.g. "completedPassesDerivedFrom": "reviewHistory@<n> entries", and have the validator warn when completedPasses is non-empty while reviewHistory is empty.
  2. Make validate_sculpt_spec.py fail on exactly that contradiction. It is never legitimate.
  3. Drop the field from the persisted spec and compute it on read.

Worth saying that the gate itself is good and strict — credit requires action=continue plus a browser render, a map-stripped render for blockout, a comparison sheet, five layer scores, and a per-feature score at or above threshold for every critical featureReviewTarget. That rigour is undermined if the artifact people actually read can disagree with it silently.

Environment

img2threejs v1.5.1.