Skip to content

Verdict provenance: falsifiable engineVersion and an engineRef field - #247

Merged
mmcky merged 3 commits into
mainfrom
fix/244-engine-ref
Aug 5, 2026
Merged

Verdict provenance: falsifiable engineVersion and an engineRef field#247
mmcky merged 3 commits into
mainfrom
fix/244-engine-ref

Conversation

@mmcky

@mmcky mmcky commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Closes #244.

package.json on a branch carries the previous release's number until the release commit bumps it, so every @main-produced verdict — including every harness verdict ever written, the exact place pre-release behaviour is meant to be observed — asserted a release that did not produce it (all five ml calibration verdicts said 0.24.0 while their run logs showed the post-#237 glossary).

The mechanism is runtime, not build-time, per the review comment: the runner sets GITHUB_ACTION_REF for the executing action step, and nothing needed to change in the build. Build-time embedding (the issue's "cheapest" shape) was rejected because it would break the release pre-flight's byte-identical dist-action/ rebuild check, make version bumps require rebuilds, and embed the parent commit's SHA — the bundle committed in commit C is necessarily built before C exists.

Behaviour by pin:

Pin engineVersion engineRef
Production @v0 (release-tag checkout, bumped package.json) 0.25.0 — unchanged, was always truthful v0
Harness @main 0.25.0+main — falsifiable main
SHA-pinned workflow 0.25.0+<sha> — exact tree attribution for free the SHA
Jest / CLI (env absent) unchanged absent

Contract: pure field addition — no schemaVersion bump, per the contract's own versioning rules ("field additions are non-breaking") and the diffCheckSources precedent. docs/user/metadata-contract.md documents both the new field and the suffix semantics. The validator treats a present-but-malformed engineRef as a wrong-shape block, fail-closed like wouldAutoMerge.

Known limit, accepted deliberately: on @main the ref is lineage, not an exact tree. Resolving ref→SHA via the API at review time would add a network failure mode and a checkout-vs-query race to every review; the verdict's timestamp plus harness run logs already resolve main-at-time when it matters, and the harness is the only @main surface.

A pre-PR adversarial review (six lenses over the diff, findings verified by independent refuters) found the correctness and fail-closed lenses clean but caught two gaps, both fixed here: the engineRef wiring at the two verdict-construction sites was pinned by no test — a verifier empirically deleted both lines and the full suite stayed green, because Jest has no GITHUB_ACTION_REF and JSON.stringify drops the undefined field, so a future refactor could silently drop the field this PR exists to add; there are now end-to-end tests on both the normal review path and the deletion path that set the env var and assert the parsed block carries engineRef and the suffixed version. And the sanitisation rule was missing from the contract doc — the code appends the sanitised ref (feat/x+feat-x) while engineRef is verbatim, so a consumer implementing from the spec could wrongly assume engineVersion.split('+')[1] === engineRef; the contract row now states the character rule and the non-equality explicitly.

Tests: the suffix logic across release-tag / branch / SHA / absent-env refs, ref sanitisation for semver build metadata, getEngineRef trimming and empty-string handling, verdict round-trip with and without engineRef, fail-closed rejection of malformed values, and the two end-to-end wiring tests above. Verification beyond CI: the first harness review run after this merges should show engineVersion: 0.25.0+main and engineRef: main in its verdict block — observable on the next organic harness PR without any dedicated run.

Why now: QuantEcon/project-translation#22's edit-density-per-engine-version analysis and the Stage 4 floor calibration both need verdicts attributable to the code that produced them, and the WS8 dashboard lanes — the verdict block's first real consumers — are about to be built. Field and format changes cost least while consumer count is zero.

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings August 5, 2026 01:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves review verdict provenance by (1) adding an optional engineRef field (verbatim GITHUB_ACTION_REF) to the translation-review-verdict block and (2) making engineVersion falsifiable on non-release refs by suffixing the package version with sanitized build metadata (e.g. 0.25.0+main). This aligns harness (@main) verdict attribution with the actual code that produced the verdict while keeping release-tag verdicts unchanged.

Changes:

  • Add engineRef?: string to ReviewVerdictV2 and wire it into both verdict construction sites (normal review path + deletion-only path).
  • Update getEngineVersion() to suffix non-release refs as semver build metadata and add getEngineRef().
  • Extend tests and documentation to cover the new field, suffix semantics, and fail-closed validation behavior.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/reviewer.ts Wires engineRef: getEngineRef() into both normal and deletion-only verdict block construction.
src/review-verdict.ts Adds engineRef to the verdict contract, introduces getEngineRef(), and updates getEngineVersion() to suffix non-release refs; validator fail-closes on malformed engineRef.
src/tests/reviewer-deletion.test.ts Adds an end-to-end wiring test ensuring deletion-path verdicts include engineRef and a suffixed engineVersion when GITHUB_ACTION_REF is set.
src/tests/review-verdict.test.ts Adds round-trip and malformed-value tests for engineRef, plus ref/suffix behavior tests for getEngineVersion()/getEngineRef(), and an end-to-end emitted-block wiring test.
docs/user/metadata-contract.md Documents engineRef and clarifies engineVersion suffix sanitization and non-equality with engineRef.
dist-action/index.js Updates the committed action bundle to include the new runtime provenance logic and verdict fields.
CHANGELOG.md Adds an Unreleased entry describing the provenance fix and new engineRef field.
.dev/log/2026-08-05-engine-ref.md Adds a maintainer log entry documenting the design and rationale for runtime ref provenance.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/review-verdict.ts
… provenance (#244)

package.json on a branch carries the previous release's number until the
release commit bumps it, so every @main-produced verdict — including all
harness verdicts — asserted a release that did not produce it.

The fix is runtime, not build-time: GITHUB_ACTION_REF is read at verdict
construction, the version gains a +<ref> build-metadata suffix when the
ref is not a release-shaped tag, and a new optional engineRef field
carries the ref verbatim. Build-time embedding was rejected — it would
break the byte-identical dist rebuild check and embed the parent
commit's SHA. Field addition only: no schemaVersion bump, per the
contract's own versioning rules.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mmcky
mmcky force-pushed the fix/244-engine-ref branch from ff37944 to 9dab95d Compare August 5, 2026 01:36
mmcky and others added 2 commits August 5, 2026 12:16
Copilot catch on #247: the suffix rule ran unconditionally, so an
unresolvable package.json on a branch ref produced 'unknown+main',
breaking the contract's documented sentinel for consumers keying off
the exact string. The suffix decision is now a pure exported helper
(formatEngineVersion) so the sentinel branch — unreachable through
getEngineVersion in Jest — is directly unit-tested.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts:
#	CHANGELOG.md
#	dist-action/index.js.map
@mmcky
mmcky merged commit 842528b into main Aug 5, 2026
1 check passed
@mmcky
mmcky deleted the fix/244-engine-ref branch August 5, 2026 02:25
mmcky added a commit to QuantEcon/status-translations that referenced this pull request Aug 6, 2026
… backfill to v0.22.0 (#2)

* Capture review verdicts nightly into an append-only event store, with backfill to v0.22.0

collector/verdicts.py captures translation-review-verdict blocks from review comments across the production editions into data/verdicts.jsonl, run as a separate step in the nightly collect workflow. Design departs from the spec in #1 on four verified points:

- Routing keys on `recommendation`, not `wouldAutoMerge`: 44 of the 47 live verdicts ran under auto-merge-mode `off`, where wouldAutoMerge is null; recommendation exists on all 47 and shows a 12/47 auto-merge base rate the shadow-only field would render as zero.
- Events, not snapshot fields: the verdict population includes merged/closed PRs and re-review supersedes in place, so verdicts get an append-only store keyed (repo, pr, reviewedHeadSha, timestamp) rather than a place in data/history/ snapshots. No schema_version bump — latest.json is untouched.
- Backfill included: GitHub retains superseded comment bodies, so the store starts at v0.22.0 (2026-07-22), not at first capture. This commit ships the backfilled corpus: 61 events — 47 current verdicts, 7 superseded ones recovered via userContentEdits (four distinct-SHA re-reviews on one PR alone), 7 pre-v0.22.0 no-verdict records.
- engineRef captured but not relied on: the provenance field from QuantEcon/action-translation#247 is on main but unreleased, so it is null fleet-wide today.

Parsing is fail-closed per the engine metadata contract: last block only, malformed last block recorded as no-verdict, never a fallback to an earlier block. Reruns are idempotent (verified: second run appends 0).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* collect: continue-on-error on verdict capture so the snapshot still publishes

The workflow comment promised a capture failure wouldn't kill the
snapshot, but a failed step aborts the job before the commit/push and
deploy. Flagged by Copilot review on #2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

engineVersion in the verdict block reports the last release, so every @main-produced verdict — including all harness verdicts — is mislabelled

2 participants