feat(workflows): restack-verify — the post-merge gate, as a relayflow - #230
feat(workflows): restack-verify — the post-merge gate, as a relayflow#230kjgbot wants to merge 5 commits into
Conversation
On 2026-09-07 this exact sequence was typed out by hand eight times against
cloud#3270 in one evening. Three of the defects it would have caught were
introduced BY a restack and existed in NEITHER parent:
- a renumbered migration whose cumulative snapshot still described its old
position: 87 tables where the base had 90
- a policy file whose deliberately-emptied exemption list was silently
re-populated from the stale side of a merge
- a base-branch test meeting a feature-branch guard, so neither side was
wrong and only the combination was
None of those produce a conflict marker. That is the whole point: a clean
`git merge` is not evidence, and the checks that catch this are mechanical, so
a human should not be re-deriving them from memory every time.
Proven against the real cloud checkout in BOTH directions rather than asserted:
real main no-conflict-markers PASSED
migration-journal PASSED (126 entries)
worker-bindings PASSED (86 env keys)
pre-fix 0127 snapshot migration-journal FAILED, exit 1
(restored from 567724e06~1) named the same three missing tables CI
surfaced hours later
injected conflict marker no-conflict-markers FAILED, exit 1
Every runner skips cleanly rather than failing where a repo has no such
artifact — "not applicable" must not read as "broken", or the gate gets
disabled the first time someone runs it elsewhere.
No output_contains gate is layered on the exit codes, for the reason already
recorded in workflows/preswarm-check.yaml: a substring match on a
diff-derived body is fail-open.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Empty commit. The previous run on this PR (20:53Z) predates the mint at 20:58Z, so it exercised the older CLOUD_API_KEY. This fires a synchronize event so the swarm runs with the current secret. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
There was a problem hiding this comment.
All reported issues were addressed
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
Review swarm: maintainabilityNo fresh transcript was produced for run |
Review swarm: historyNo fresh transcript was produced for run |
Review swarm: structureNo fresh transcript was produced for run |
|
🎯 review-swarm: FAILED (M:fail H:fail S:missing) Lens transcripts posted as sibling comments above. |
kjgbot
left a comment
There was a problem hiding this comment.
Spec review: original bd7fda3; fixes pushed on this PR branch at abf4442. Leave open pending credential repair and independent review.
This is a zero-agent deterministic relayflow, serving RFC-0001 §1/Gate 1 and §2 dogfooding with existing primitives (decision #13). It is a standalone restack checker, not the review/merge gate judging this assignment; those gates were not edited.
Findings fixed on this branch:
ops/restack-verify/no-conflict-markers.sh:8: Git errors falsely passed and lockfiles were excluded. Check exit 1 separately, surface errors, scan lockfiles, and use a private temporary file instead of a shared /tmp path.ops/restack-verify/migration-journal.sh:18,39,47: require entries, strictly increasing timestamps, and snapshot id/prevId lineage; the prior code never checked the documented prevId chain.- The old table-loss heuristic rejected legitimate DROP TABLE migrations. Replaced with structural lineage validation; documentation now explicitly says SQL/schema equivalence requires database replay and target repo suites run separately.
ops/restack-verify/worker-bindings.sh:11: missing configuration was mislabeled missing script. Both absent is inapplicable; a partial installation fails naming the actual missing path.
Added black-box temporary-repository tests. This is local fixture evidence, not a claim that a deployed consumer or full workflow ran. Captured command/output (also committed in ops/restack-verify/SPEC-REVIEW-EVIDENCE.md):
$ python3 ops/restack-verify/test_checks.py
test_bindings_inapplicable_skips (__main__.RestackChecks) ... ok
test_bindings_missing_config_fails (__main__.RestackChecks) ... ok
test_bindings_missing_script_fails (__main__.RestackChecks) ... ok
test_bindings_passes_config_argument (__main__.RestackChecks) ... ok
test_bindings_propagates_checker_failure (__main__.RestackChecks) ... ok
test_broken_snapshot_chain_fails (__main__.RestackChecks) ... ok
test_clean_tracked_file_ignores_untracked_markers (__main__.RestackChecks) ... ok
test_empty_journal_passes (__main__.RestackChecks) ... ok
test_equal_or_out_of_order_timestamps_fail (__main__.RestackChecks) ... ok
test_git_error_fails (__main__.RestackChecks) ... ok
test_lockfile_conflicts_fail (__main__.RestackChecks) ... ok
test_missing_entries_fails (__main__.RestackChecks) ... ok
test_missing_sql_fails (__main__.RestackChecks) ... ok
test_orphan_sql_fails (__main__.RestackChecks) ... ok
test_valid_drop_is_not_a_stale_snapshot (__main__.RestackChecks) ... ok
----------------------------------------------------------------------
Ran 15 tests in 0.375s
OK
exit_code=0
New-head CI still cannot perform the content review. gh run view 34164170263 --log-failed captured:
review Launch cloud swarm 2026-09-07T21:44:38.3541771Z Workflow prepare failed: 401 Unauthorized: Unauthorized
The CI credential owner must restore accepted credentials. Independent reviewers must examine abf4442, acknowledge the fixes in outstanding threads, and provide genuine signoff. No merge or green-CI claim.
There was a problem hiding this comment.
1 issue found across 6 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="ops/restack-verify/migration-journal.sh">
<violation number="1" location="ops/restack-verify/migration-journal.sh:56">
P1: When a restack leaves snapshot `id`/`prevId` metadata coherent but carries stale table content, this loop reports `PASSED` because it never inspects the snapshot payload. Retain a semantic snapshot/schema check, such as replaying migrations or another validated content comparison, instead of replacing the documented stale-snapshot detection with lineage-only validation.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| ident = snapshot.get("id") | ||
| if not isinstance(ident, str) or not ident or ident in seen: | ||
| problems.append(f"{name}: missing or duplicate snapshot id") | ||
| if snapshot.get("prevId") != previous: |
There was a problem hiding this comment.
P1: When a restack leaves snapshot id/prevId metadata coherent but carries stale table content, this loop reports PASSED because it never inspects the snapshot payload. Retain a semantic snapshot/schema check, such as replaying migrations or another validated content comparison, instead of replacing the documented stale-snapshot detection with lineage-only validation.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ops/restack-verify/migration-journal.sh, line 56:
<comment>When a restack leaves snapshot `id`/`prevId` metadata coherent but carries stale table content, this loop reports `PASSED` because it never inspects the snapshot payload. Retain a semantic snapshot/schema check, such as replaying migrations or another validated content comparison, instead of replacing the documented stale-snapshot detection with lineage-only validation.</comment>
<file context>
@@ -36,24 +39,25 @@ if orphans:
+ ident = snapshot.get("id")
+ if not isinstance(ident, str) or not ident or ident in seen:
+ problems.append(f"{name}: missing or duplicate snapshot id")
+ if snapshot.get("prevId") != previous:
+ problems.append(f"{name}: prevId does not match predecessor {previous}")
+ if isinstance(ident, str):
</file context>
Review swarm: FAILED
Cloud run: |
kjgbot
left a comment
There was a problem hiding this comment.
Follow-up spec review at 8229763. Leave open pending completed CI and independent signoff.
The review of abf4442 correctly identified that lineage-only validation lost the original stale-table signal. This head restores a fail-closed table-content check: removal of a table between snapshots requires semantic schema verification. The diagnostic explicitly distinguishes the unresolved possibilities (intentional drop or stale snapshot); it does not silently accept either and adds no override. SQL/schema equivalence and execution of the target repo suites remain outside this structural check.
The remaining original repairs (Git errors/lockfiles, malformed journals, strict timestamps, id/prevId chain, partial worker installation) are retained. Captured command/output, also in ops/restack-verify/SPEC-REVIEW-EVIDENCE.md:
$ python3 ops/restack-verify/test_checks.py
test_bindings_inapplicable_skips (__main__.RestackChecks) ... ok
test_bindings_missing_config_fails (__main__.RestackChecks) ... ok
test_bindings_missing_script_fails (__main__.RestackChecks) ... ok
test_bindings_passes_config_argument (__main__.RestackChecks) ... ok
test_bindings_propagates_checker_failure (__main__.RestackChecks) ... ok
test_broken_snapshot_chain_fails (__main__.RestackChecks) ... ok
test_clean_tracked_file_ignores_untracked_markers (__main__.RestackChecks) ... ok
test_empty_journal_passes (__main__.RestackChecks) ... ok
test_equal_or_out_of_order_timestamps_fail (__main__.RestackChecks) ... ok
test_git_error_fails (__main__.RestackChecks) ... ok
test_lockfile_conflicts_fail (__main__.RestackChecks) ... ok
test_missing_entries_fails (__main__.RestackChecks) ... ok
test_missing_sql_fails (__main__.RestackChecks) ... ok
test_orphan_sql_fails (__main__.RestackChecks) ... ok
test_table_loss_requires_semantic_verification (__main__.RestackChecks) ... ok
test_valid_lineage_passes (__main__.RestackChecks) ... ok
----------------------------------------------------------------------
Ran 16 tests in 0.421s
OK
exit_code=0
CI infrastructure has changed: Actions run 34164770687 now completed Launch cloud swarm successfully at 2026-09-07T21:54:49Z and is waiting for the swarm. The earlier 401 is historical, not the current blocker. This is not a content pass or permission to bypass the outstanding review thread. Await the final verdict and independent acknowledgement at 8229763. No merge.
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="ops/restack-verify/migration-journal.sh">
<violation number="1" location="ops/restack-verify/migration-journal.sh:67">
P2: This check compares every consecutive snapshot pair across the whole migration journal, so a single legitimate DROP TABLE — or a table RENAME, which drizzle snapshots as drop+create — anywhere in the repo's history permanently FAILs restack-verify on every later merge, even one that introduced no defect. The loop scans all `*_snapshot.json` files and has no way to know which migrations the merge actually introduced, so there is no scoping. That permanent-red condition is exactly the failure mode the PR description warns about ('or the gate gets disabled the first time someone runs it elsewhere'). Consider limiting the loss check to the migrations affected by the restack (e.g., only flag a removed table when that table exists on the other side of the merge), or at minimum acknowledging in the comment that renames trigger it too, so a repo that ever renames a table doesn't silently disable the gate.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| problems.append(f"{name}: tables must be an object") | ||
| continue | ||
| if previous_tables is not None: | ||
| lost = sorted(previous_tables - set(tables)) |
There was a problem hiding this comment.
P2: This check compares every consecutive snapshot pair across the whole migration journal, so a single legitimate DROP TABLE — or a table RENAME, which drizzle snapshots as drop+create — anywhere in the repo's history permanently FAILs restack-verify on every later merge, even one that introduced no defect. The loop scans all *_snapshot.json files and has no way to know which migrations the merge actually introduced, so there is no scoping. That permanent-red condition is exactly the failure mode the PR description warns about ('or the gate gets disabled the first time someone runs it elsewhere'). Consider limiting the loss check to the migrations affected by the restack (e.g., only flag a removed table when that table exists on the other side of the merge), or at minimum acknowledging in the comment that renames trigger it too, so a repo that ever renames a table doesn't silently disable the gate.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ops/restack-verify/migration-journal.sh, line 67:
<comment>This check compares every consecutive snapshot pair across the whole migration journal, so a single legitimate DROP TABLE — or a table RENAME, which drizzle snapshots as drop+create — anywhere in the repo's history permanently FAILs restack-verify on every later merge, even one that introduced no defect. The loop scans all `*_snapshot.json` files and has no way to know which migrations the merge actually introduced, so there is no scoping. That permanent-red condition is exactly the failure mode the PR description warns about ('or the gate gets disabled the first time someone runs it elsewhere'). Consider limiting the loss check to the migrations affected by the restack (e.g., only flag a removed table when that table exists on the other side of the merge), or at minimum acknowledging in the comment that renames trigger it too, so a repo that ever renames a table doesn't silently disable the gate.</comment>
<file context>
@@ -58,6 +59,18 @@ for name in snaps:
+ problems.append(f"{name}: tables must be an object")
+ continue
+ if previous_tables is not None:
+ lost = sorted(previous_tables - set(tables))
+ if lost:
+ problems.append(
</file context>
kjgbot
left a comment
There was a problem hiding this comment.
Final disposition at 8229763: LEFT OPEN.
The branch contains repairs abf4442 and 8229763 and 16 passing local fixture tests, as captured in the earlier review. A remaining P2 at ops/restack-verify/migration-journal.sh:67 is valid: scanning every historical snapshot means a legitimate DROP TABLE or table rename permanently triggers the loss guard. The guard cannot distinguish that from stale content and currently has no scoped restack baseline or semantic replay. This is not comprehensive restack acceptance. The restack flow owner must define the comparison baseline or implement semantic replay and exercise both stale snapshots and legitimate historical drops/renames. Removing the loss guard would recreate the previous P1 and is not a fix. The latest review failed with all fresh lens transcripts MISSING; review infrastructure owner must restore the swarm. Leave open with the unresolved thread; no merge.
Captured exact-head check query and output:
$ gh api repos/AgentWorkforce/flows/commits/82297638ec8c4f18ac5acd355134d20c78a1b08c/check-runs --jq '[.check_runs[] | {name,head_sha,status,conclusion,details_url}]'
[{"conclusion":"success","details_url":"https://www.cubic.dev/pr/AgentWorkforce/flows/pull/230","head_sha":"82297638ec8c4f18ac5acd355134d20c78a1b08c","name":"cubic · AI code reviewer","status":"completed"},{"conclusion":"failure","details_url":"https://github.com/AgentWorkforce/flows/actions/runs/34164770687/job/101873532992","head_sha":"82297638ec8c4f18ac5acd355134d20c78a1b08c","name":"review","status":"completed"}]
exit_code=0
Current swarm report: #230 (comment)
Review swarm: FAILED
- maintainability: MISSING
- history: MISSING
- structure: MISSING
Cloud run: 95a34434-5f01-4efe-a0fb-6453266effc0
The deterministic gate that should run after merging a base branch into a long-lived feature branch — expressed as a relayflow instead of typed out by hand.
Provenance
On 2026-09-07 this exact sequence was hand-driven eight times against
cloud#3270in one evening. Three of the defects it catches were introduced by a restack and existed in neither parent:None of those produce a conflict marker. That's the point: a clean
git mergeis not evidence, and these checks are mechanical, so nobody should be re-deriving them from memory.Proven in both directions
Against a real cloud checkout, rather than asserted:
main0127snapshot, restored from567724e06~1migration-journalFAILED, exit 1 — naming the same three missing tables CI surfaced hours laterno-conflict-markersFAILED, exit 1In this repo the migration step reports SKIPPED — every runner skips where a repo has no such artifact, because "not applicable" must not read as "broken" or the gate gets disabled the first time someone runs it elsewhere.
Design note
No
output_containsgate is layered on the exit codes, for the reason already recorded inworkflows/preswarm-check.yaml: a substring match on a diff-derived body is fail-open.🤖 Generated with Claude Code
https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR