Skip to content

Lint the whole catalog at the pinned revision against each boxel change - #6587

Merged
habdelra merged 5 commits into
mainfrom
cs-13447-the-catalog-pin-should-mean-this-boxel-works-with-the-whole
Oct 7, 2026
Merged

habdelra merged 5 commits into
mainfrom
cs-13447-the-catalog-pin-should-mean-this-boxel-works-with-the-whole

Conversation

@habdelra

@habdelra habdelra commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Important

Merge order: this, then cardstack/boxel-catalog#810.
cardstack/boxel-catalog#810 documents this rule on the catalog side; merge it right after this one.

Merges before: cardstack/boxel-catalog#810

Production deploys a boxel commit with the catalog at the revision it pins in packages/catalog/test-subset.json. Until now, nothing checked that this boxel works with the whole catalog at that revision. --check-pin and the subset consumers cover only the subset files, and Lint Catalog lints catalog main and the paired pull request's head, not the pin. A boxel change could break a non-subset catalog file at the pin, merge green, and ship broken.

Lint Catalog now lints the whole catalog at the pin against the change, and compares it with the base branch's pin linted against the base branch. Every error the change adds there fails, paired or not. When the pin is the revision an earlier step already linted, that recording is reused.

The case that used to merge with a stale pin is a boxel-first pair: a boxel change that breaks the catalog, followed right after by the catalog fix. That change now pins the head of its catalog pull request. --check-pin accepts a pin at the head of an open catalog pull request only when that pull request is the one this change says Merges before:, it is approved and ready for review, and neither side is stacked. These are the terms under which Lint Catalog already lets catalog main wait for it (pinVerdict in pairing.ts). The catalog pull request then merges right after. Once it has merged, the lockstep deploy (dispatch-catalog-deploy.sh) deploys its merge commit in place of the pinned head, whatever the merge method. The head branches off before production's catalog, so deploying it would be refused as diverged. Catalog Test Subset now reads the pairing, and re-runs when the description is edited.

The catalog-pairing, catalog-test-subset and catalog-deploy skills and the catalog README describe the new rule. The paired boxel-catalog pull request documents it on the catalog side.

🤖 Generated with Claude Code

Production deploys a boxel commit with the catalog at the revision it pins,
so Lint Catalog now lints the whole catalog at the pin and fails on every
error a change adds there. A boxel-first change that breaks the catalog pins
the head of its approved Merges-before catalog pull request, which
--check-pin accepts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@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: 43e66c2761

ℹ️ 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".

Comment thread .github/workflows/lint-catalog.yaml Outdated
Comment on lines +155 to +158
if [ "$pin" = "$linted" ]; then
echo "The pin is the catalog revision linted against this change above."
echo "errors=0" >> "$GITHUB_OUTPUT"
exit 0

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 Badge Compare the reused pinned recording against the base pin

When the pin equals the initially checked-out catalog revision—as it does for the boxel-first paired flow—this hard-codes zero errors and skips creating the pinned recording. The ordinary report compares that revision against itself on the Boxel base branch, so lint errors already present in the newly pinned catalog revision are classified as existing rather than compared with the base branch's old pin. The check can therefore pass while the whole catalog at the new production pin contains errors, defeating this change's primary safeguard. Reuse catalog-lint-head.json, but still report it as pinned and compare it with the base branch's pin.

AGENTS.md reference: AGENTS.md:L278-L278

Useful? React with 👍 / 👎.

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.

[Claude Code 🤖] Fixed in 2973385. When the pin is the revision the first lint step linted, the pin step now copies that recording to catalog-pin-lint-head.json and outputs its error count. The pin report then compares it with the base branch's pin linted against the base branch, so errors a new pin brings count as this change's.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-06T22:55:43.938480Z 43e66c2 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@habdelra habdelra 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.

[Claude Code 🤖] This review covers the Lint Catalog step logic (step order, if: conditions, which recording is compared with which baseline, the skip cases, push to main, fetch by sha), whether the --check-pin exception is safe, and whether the docs match the code. It does not cover lint-sweep.ts error matching beyond the new --pinned branch.

One blocking issue: when the pin equals a revision an earlier step already linted, the pin is compared with the wrong baseline. That is the case for the boxel-first pair and for most --bump PRs. The merge-commit requirement behind the new exception also has nothing enforcing it.

  1. Pin equals the linted revision: reuse the recording instead of reporting errors=0, so the pin is compared with the base branch's pin (inline on lint-catalog.yaml, pin step).
  2. Merge-commit dependency: make it mechanical, and fix the quoted deploy refusal text (inline on the catalog-pairing skill, step 4).
  3. || true in Catalog Test Subset lets a resolution with own-side problems satisfy pinVerdict (inline on catalog-test-subset.yml).

Comment thread .github/workflows/lint-catalog.yaml Outdated
Comment on lines +155 to +159
if [ "$pin" = "$linted" ]; then
echo "The pin is the catalog revision linted against this change above."
echo "errors=0" >> "$GITHUB_OUTPUT"
exit 0
fi

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.

[Claude Code 🤖] When the pin equals the revision the first lint step checked out, this sets errors=0, so the pin is never compared with the base branch's pin. The head report covers it, but against a different baseline: the same catalog revision linted against the base branch. Errors the new pin has against both boxel trees get excused as "already present", even when the old pin didn't have them. That is the case the comment at line 175 rules out ("a change that moves the pin is answerable for the errors the new pin brings too").

This branch is taken in the two most common pin moves:

  • an unpaired --bump PR, when catalog main hasn't moved since the bump (catalog_ref is main, so linted equals the pin);
  • the boxel-first pair this PR is built for, where the pin is the Merges before: head and catalog_ref is that same head.

So whether a --bump onto a catalog main that is already broken against boxel main gets through depends on whether catalog main moved before CI ran.

Fix: in this branch, reuse the recording rather than skipping. Copy catalog-lint-head.json to catalog-pin-lint-head.json and output its error count, the same way the MAIN_REVISION branch does. The base step then lints the base pin, and the pin report compares the right pair. The comment at lines 142–145 and step 4 of the catalog-pairing skill ("that recording is reused") would then hold as written.

Regression, blocking: the PR's own rule doesn't hold on the paths it was written for.

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.

[Claude Code 🤖] Fixed in 2973385. When the pin is the revision the first lint step linted, the pin step now copies that recording to catalog-pin-lint-head.json and outputs its error count. The pin report then compares it with the base branch's pin linted against the base branch, so errors a new pin brings count as this change's.

Comment thread .claude/skills/catalog-pairing/SKILL.md Outdated
1. Open the catalog pull request with the fix, paired with the boxel pull request in both descriptions.
2. In the boxel pull request, set `revision` in `packages/catalog/test-subset.json` to the catalog pull request's head, run `pnpm --dir packages/catalog catalog:test-subset`, run the manifest's tests, and commit the new pin. Re-pin each time the catalog pull request gets new commits.
3. Get the catalog pull request approved, then re-run the boxel pull request's Lint Catalog and Catalog Test Subset checks.
4. Merge the boxel pull request, then the catalog pull request right after it, **with a merge commit**. A squash or a rebase leaves the pinned commit off catalog `main`, and production's deploy of the pin then refuses with "isn't an ancestor of".

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.

[Claude Code 🤖] Only this sentence and the pinVerdict message ask for "with a merge commit", and cardstack/boxel-catalog has squash and rebase merges enabled (allow_squash_merge / allow_rebase_merge are both true). Until now a pin had to be on catalog main before boxel merged, so the merge method didn't matter. With the new --check-pin exception, one squash click in the catalog leaves boxel main pinning a commit that is never on catalog main. The post-release run of "Deploy to production" then refuses, so production runs the new boxel against the older catalog this change breaks, until someone lands a re-pin PR and deploys again.

Can this be made mechanical? Two options:

  • Recommended: have the catalog side resolve a pin that is the head of a merged pull request to that pull request's merge_commit_sha, and deploy that when the trees are equal. This removes the dependency on merge method.
  • At least, have a boxel-catalog check fail a pull request that says Merges after: cardstack/boxel#N while #N pins its head, with text naming the required merge method. That is advisory only.

Separately, the quoted refusal text is wrong. deploy-production.yml refuses a revision that isn't on main with "… isn't on catalog main. Deploy a revision on main." "isn't an ancestor of" is a different check in catalog-deploy-check.ts.

Regression, introduced by the exception. Your call whether it blocks: it is safe only as long as every merger reads this line.

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.

[Claude Code 🤖] Fixed in 2973385, by making the merge method not matter rather than enforcing one. dispatch-catalog-deploy.sh now checks whether the pinned commit is on catalog main. If it isn't, and it is the head of a catalog pull request that merged into main, the script deploys that pull request's merge commit, with a notice naming both. I checked the lookup against cardstack/boxel-catalog#787, which was squash-merged: its head is diverged from main and resolves to its merge commit ffcfb5c92c. A commit already on main, an open pull request's head and an unknown sha are left as they are. The docs no longer ask for a merge commit, and the wrong "isn't an ancestor of" quote is gone.

--number="$PR_NUMBER" \
--base="$PR_BASE" \
--counterpart=cardstack/boxel-catalog \
--out="$RUNNER_TEMP/catalog-pairing.json" || true

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.

[Claude Code 🤖] || true keeps the resolution file even when pairing.ts exits 1, and resolvePairing still pushes pairs when the problem is on this pull request's own side. In openSide, the problem and landed cases, and stacked with a merged parent, all return a problem with no stackedOn. pinVerdict then sees resolution.stackedOn undefined, treats "neither side stacked" as met, and --check-pin goes green for a boxel pull request that targets a dead parent branch. Lint Catalog does go red on the same pairing, but main has no required status checks, so a green Catalog Test Subset beside it is misleading.

Fix: drop the file when the pairing has problems, so the pin waits on the catalog pull request like any other:

Suggested change
--out="$RUNNER_TEMP/catalog-pairing.json" || true
--out="$RUNNER_TEMP/catalog-pairing.json" || rm -f "$RUNNER_TEMP/catalog-pairing.json"

Then update the comment above the step (Lint Catalog reports a malformed or one-sided pairing…) to match. Regression, non-blocking.

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.

[Claude Code 🤖] Fixed in 2973385. A failed pairing.ts run now deletes catalog-pairing.json (|| rm -f …), so a pairing with a problem can't excuse a pin, and the pin waits on its catalog pull request as before.

habdelra and others added 2 commits October 6, 2026 19:00
…shed pin's merge commit

Lint Catalog now reports the catalog at the pin against the base branch's
pin even when an earlier step already linted that revision. Catalog Test
Subset drops a pairing resolution that has a problem. The lockstep catalog
deploy maps a pinned head that a squash or rebase left off catalog main to
its pull request's merge commit, so the merge method no longer matters, and
the docs say which check holds a boxel-first pin to approval.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keeps both pin checks: the pin lint and the head-of-a-Merges-before-pair
exception here, and #6585's wait for a Merges-after pair's merge commit.
The exception's verdict is openPinVerdict, beside #6585's pinVerdict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@habdelra
habdelra changed the base branch from main to cs-13451-lint-catalog-fails-a-boxel-pr-whose-merges-after-catalog-pr October 6, 2026 23:02
@habdelra
habdelra requested a review from a team October 6, 2026 23:04
…xel-pr-whose-merges-after-catalog-pr' into cs-13447-the-catalog-pin-should-mean-this-boxel-works-with-the-whole

@richardhjtan richardhjtan 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.

[Claude Code 🤖] One blocking issue, inline on dispatch-catalog-deploy.sh.

Comment thread packages/catalog/scripts/dispatch-catalog-deploy.sh Outdated
The head of a catalog pull request descends only from its branch point, so
production's catalog is usually past it on another line and the deploy
check refuses it as diverged, even after a merge commit puts the head on
main. Once the pull request has merged, deploy its merge commit instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@habdelra
habdelra changed the base branch from cs-13451-lint-catalog-fails-a-boxel-pr-whose-merges-after-catalog-pr to main October 7, 2026 07:56
@habdelra
habdelra merged commit ba9a547 into main Oct 7, 2026
48 of 50 checks passed
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.

2 participants