Skip to content

fix: refuse rollback to never-activated config epochs - #66

Open
iperev wants to merge 1 commit into
docs/roadmap-reconciliationfrom
fix/rollback-activated-only
Open

iperev wants to merge 1 commit into
docs/roadmap-reconciliationfrom
fix/rollback-activated-only

Conversation

@iperev

@iperev iperev commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

Stacked PR 11 — base: #65 (docs/roadmap-reconciliation). Do not merge yet. Review only the top commit.

Context

Binding owner decision D8 (2026-10-11), ROADMAP CI-018: rollback may only target an epoch previously activated in the same scope. Before this change, rollback admitted any retained same-scope epoch with equal or greater coverage, including a registered custom epoch that was never activated and has no review receipt.

What changed

  • The new check: under the scope lock, after the active-pointer lock and the revision/active-epoch CAS checks, the repository requires a same-scope config_epoch_activations row for the target whose audit event is config-epoch-activation/v1.
  • What doesn't count: a rollback row, another scope's activation, or registration alone is not evidence.
  • The refusal: the repository returns ConfigEpochRollbackTargetNeverActivated, reported as target_never_activated (HTTP 409, its own metric label). Nothing is written.
  • No migration.
  • Updated to match:
    • REQ-CI-CORE-016.
    • Lifecycle, operator-controls, how-to and workspace docs.
    • OpenAPI, generated.ts and the operator UI rejection handling.
    • Proof hashes.
  • Tests:
    • New PostgreSQL tests cover:
      • a never-activated target;
      • foreign-scope history;
      • a target reached only by rollback;
      • two forced orderings with a concurrent activation. Each ordering confirms through pg_blocking_pids that the contender is blocked before the holder releases.
    • Unit and frontend tests cover the result mapping.

Expected result

  • Rollback to a never-activated epoch returns 409 target_never_activated and changes nothing.
  • Rollback to an epoch that was activated before still works.
  • Revision conflict still takes precedence.

Non-claims:

  • Native PostgreSQL, unit, frontend and mutation tests were not run locally (GitHub CI only).
  • No performance measurement, deployment or provider evidence.
  • CI-018 stays open, and the custom-epoch review flow is not implemented.

Provisional decisions: none. D8 is binding. Treating only activation events as evidence is the implementation's reading of "previously activated", stated for review.

Independent review: gpt-6-astra (xhigh); findings addressed or rejected with reasons in the single commit. Provisional owner decisions (2026-10-11) are labelled in the changed documents.

Rollback admitted any retained same-scope epoch whose coverage was equal or
greater than the active one, including a registered custom epoch that was
never activated and has no review receipt. Binding owner decision D8
(2026-10-11) for CI-018 allows rollback only to an epoch previously activated
in the same scope.

The config-epoch repository now evaluates, under the scope lock and after the
active-pointer row lock and the revision/active-epoch compare-and-set, whether
the immutable config_epoch_activations journal holds a transition to the
target in the same scope whose pair-owned audit event is
config-epoch-activation/v1. A rollback transition is not activation evidence,
so a rollback admitted before this rule cannot qualify its own target; another
scope's history cannot qualify it either. Any other target returns the new
ConfigEpochRollbackTargetNeverActivated result without consuming an audit
event or writing the pointer or journal. The application maps it to
target_never_activated, the HTTP route to 409 target_never_activated, and
runtime metrics count it as its own result. No table, column, index or
migration is added; the journal is append-only, so a positive answer cannot
later become false, and RevisionConflict keeps precedence.

REQ-CI-CORE-016, the lifecycle and operator-control specifications, the
policy how-to, the operator workspace note, the OpenAPI projection, generated
TypeScript and the operator UI rollback rejection handling are updated, and
derived proof hashes are refreshed. PostgreSQL witnesses cover a never-activated
target, foreign-scope history, a rollback-only target and lock-serialized
races with a concurrent activation; unit witnesses cover the service and
route mapping. Native PostgreSQL, unit and frontend witnesses are unexecuted
locally and run only in GitHub CI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant