Skip to content

feat(workflows): restack-verify — the post-merge gate, as a relayflow - #230

Open
kjgbot wants to merge 5 commits into
mainfrom
feat/restack-verify-flow
Open

feat(workflows): restack-verify — the post-merge gate, as a relayflow#230
kjgbot wants to merge 5 commits into
mainfrom
feat/restack-verify-flow

Conversation

@kjgbot

@kjgbot kjgbot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

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#3270 in one evening. Three of the defects it catches 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's the point: a clean git merge is 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:

input result
real main all three PASSED (126 journal entries, 86 env keys)
the actual pre-fix 0127 snapshot, restored from 567724e06~1 migration-journal FAILED, exit 1 — naming the same three missing tables CI surfaced hours later
injected conflict marker no-conflict-markers FAILED, exit 1

In 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_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.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

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
@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: f952a6e1-3191-4fcd-bd88-ac5794366d63


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread ops/restack-verify/migration-journal.sh
Comment thread ops/restack-verify/no-conflict-markers.sh Outdated
Comment thread ops/restack-verify/migration-journal.sh Outdated
Comment thread ops/restack-verify/worker-bindings.sh Outdated
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
@kjgbot

kjgbot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Review swarm: maintainability

No fresh transcript was produced for run 95a34434-5f01-4efe-a0fb-6453266effc0 (MISSING).

@kjgbot

kjgbot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Review swarm: history

No fresh transcript was produced for run 95a34434-5f01-4efe-a0fb-6453266effc0 (MISSING).

@kjgbot

kjgbot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Review swarm: structure

No fresh transcript was produced for run 95a34434-5f01-4efe-a0fb-6453266effc0 (MISSING).

@kjgbot

kjgbot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

🎯 review-swarm: FAILED (M:fail H:fail S:missing)

Lens transcripts posted as sibling comments above.

@kjgbot kjgbot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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>

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

Review swarm: FAILED

  • maintainability: MISSING
  • history: MISSING
  • structure: MISSING

Cloud run: 95a34434-5f01-4efe-a0fb-6453266effc0

@kjgbot kjgbot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 kjgbot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

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.

1 participant