Skip to content

fix(retention): gate must target the git main worktree, not the twin holding main - #105

Merged
nish3451 merged 16 commits into
mainfrom
fix/retention-gate-canonical-main-worktree
Aug 13, 2026
Merged

fix(retention): gate must target the git main worktree, not the twin holding main#105
nish3451 merged 16 commits into
mainfrom
fix/retention-gate-canonical-main-worktree

Conversation

@nish3451

Copy link
Copy Markdown
Collaborator

What

The Friday retention gate still passed on a stale, empty workspace after the closed twin. Live reproduction on the current main (d898707), running the gate from the fresh main worktree:

{ "status": "pass", "clientCount": 0,
  "repo": "/home/nish/workspaces/products/tinystudio-in-autonomous-service",
  "failures": [] }

The real main worktree holds 3 client records (clients/ai-converter, five-to-nine-0509, siterep) — invisible to the gate.

Root cause

canonicalMainWorktree() resolved "the repository's main worktree" as whichever worktree hosts refs/heads/main. When the git main worktree is detached (the closed twin), that is some other worktree — here products/tinystudio-in-autonomous-service, 45 commits behind at 24e516a with empty clients/ and no prospect directories. Both the canonical state inspection and the automation-workspace check followed the twin, so the gate inspected empty roots (0 clients) and considered the automation correctly pointed at the twin. The live automation.toml also points at that twin, so Friday additionally ran the twin's ancient gate-less check-retention-automation.mjs.

Fix

  • canonicalMainWorktree() now returns the first entry of git worktree list --porcelain — the git main worktree that owns .git — never a twin holding the main branch.
  • The gate additionally proves the canonical retention workspace itself is fresh (not behind remote main) when the script runs from a different checkout, so a stale main worktree running old gate code fails closed.
  • Regression tests cover both: the twin holding refs/heads/main must not hijack the canonical workspace, and a stale canonical workspace must fail even when the gate runs from a fresh checkout.

Verification

  • node scripts/test-retention-automation.mjs passes; the new assertions fail against the old canonicalMainWorktree logic.
  • With the fixed gate, the live check now resolves products/tinystudio-in, reports clientCount: 3, and fails loudly with the replacement prompt (previously a silent pass).
  • All other hermetic suites in the npm test chain pass. npm test on the machine now exits 1 because the live gate correctly surfaces the true divergent state (3 clients, service-decisions 0, runs/service-engine 0) — the designed fail-closed contract; the parity repair is human-owned via the replacement prompt.

Operational follow-up (outside the repo)

The live ~/.codex/automations/tinystudio-retention-checkups/automation.toml was re-pointed from the stale twin to /home/nish/workspaces/products/tinystudio-in, so Friday runs current gate code instead of the twin's gate-less version.

…holding main

canonicalMainWorktree resolved the canonical retention workspace as
whichever worktree hosts refs/heads/main. When the repository's main
worktree is detached, that is some other worktree — the closed twin —
whose state roots may be empty and whose checkout may run ancient gate
code. The Friday gate then passed on the stale empty twin while real
client records sat untouched in the main worktree.

The main worktree is always the first entry of 'git worktree list
--porcelain'; resolve it from there and never substitute a twin. The
gate additionally proves the canonical workspace itself is fresh when
the script runs from a different checkout, and the regression suite now
covers both the twin-hijack and the stale-canonical-workspace cases.
The Friday gate's canonical workspace is the git main worktree (first
entry of git worktree list), never a twin that happens to hold the
refs/heads/main branch while the main worktree is detached; a stale
canonical workspace fails just like a stale checkout.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@nish3451, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Free

Run ID: 942096ea-1e48-4b89-9e96-a0aa1ca06757

📥 Commits

Reviewing files that changed from the base of the PR and between c617364 and 105f474.

📒 Files selected for processing (4)
  • README.md
  • scripts/check-retention-automation.mjs
  • scripts/lib/retention-preflight.mjs
  • scripts/test-retention-automation.mjs

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps 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.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea3debc17e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// otherwise prove the canonical workspace directly.
const expectedRepo = normalizedPath(expectedCwd);
const canonicalFreshness =
normalizedPath(repoForFreshness) === expectedRepo ? preflight.freshness : proveFreshness(expectedCwd);

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 Badge Reject workspace overrides that select a linked twin

When TINYSTUDIO_AUTOMATION_WORKSPACE is set to a linked twin, expectedCwd refers to that twin, so this freshness check validates the twin and the later workspace comparison also accepts an automation configured there. Meanwhile, stateRoot still defaults to the actual main worktree, allowing the gate to pass while Friday runs the twin's potentially stale gate code—the exact failure this change intends to prevent. Derive the canonical workspace independently of this override, or reject an override that differs from canonicalMainWorktree(repoForFreshness).

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@nish3451
nish3451 merged commit a0e8e78 into main Aug 13, 2026
2 checks passed
nish3451 pushed a commit that referenced this pull request Aug 13, 2026
…le with main after PRs #135/#104/#99/#112/#100/#105)

The PR #130 branch had already been reconciled with origin/main through
c617364 (PRs #131 + #133/#134/#132/#116). Since then origin/main moved to
d4f3ef4 with six more merges (#135 #104 #99 #112 #100 #105). Pull them in
so PR #130 can be reviewed against current main without weakening the
capture/rollback/reverification implementation or the PR #131 fail-closed
missing-secret gate.

The merge resolved cleanly:
- PR #130 capture/rollback logic (publish-public-site.mjs canonical_deployment,
  rollbackTo, restored identity re-verify, accept-again) is intact.
- PR #130 test file (scripts/test-pages-release.mjs) is intact and wired
  into the npm ci/test scripts.
- PR #131 fail-loud missing-secret workflow step in
  .github/workflows/deploy-public-site.yml is preserved byte-for-byte.
- The new origin/main changes touch operator surfaces and the deploy
  bundle prep; none of them modify the publish-public-site.mjs rollback
  contract or the Pages token gate, so there were no conflict markers.
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