Add the RealmPolicy, PolicyRule and OperationGrant definitions and PolicyPredicateField - #751
Conversation
…licyPredicateField Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Staging Submissions PreviewThis PR's content is pushed to the staging submissions realm: https://realms-staging.stack.cards/submissions/ Changed folders:
Updated at 2026-09-24 14:01:27 UTC for commit |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
backspace
left a comment
There was a problem hiding this comment.
[Claude Code 🤖] This review checked two things: whether the new workflow's gate catches the catalog changes it exists to catch, and whether the two definitions match the policy-predicate serializer and the host tests on the boxel side. I did not review the boxel-side PRs themselves.
No blocking issues. There are two non-blocking fixes. Each is one line:
- Include
previous_filenamein the changed-file list, so that a renamed subset file runs the tests. See the thread on the scope step inboxel-test-subset.yaml. - Make the predicate editor's placeholder match the empty-condition state. See the thread on
@placeholderinpolicy-predicate.gts.
Lint is red only because the PolicyPredicate export is missing. That is expected until the boxel serializer change is on boxel main.
| gh api "repos/$GITHUB_REPOSITORY/pulls/${{ github.event.pull_request.number }}/files" \ | ||
| --paginate --jq '.[].filename' > changed.txt | ||
| else | ||
| gh api "repos/$GITHUB_REPOSITORY/compare/${{ github.event.before }}...${{ github.sha }}" \ | ||
| --jq '.files[].filename' > changed.txt |
There was a problem hiding this comment.
[Claude Code 🤖] A PR that renames a subset file skips all subset tests. For a rename, .filename is the new path, and the old path is only in previous_filename. Nothing imports realm-policy/realm-policy.gts, so a rename of that file changes no other subset file. The new path matches nothing in subset.txt, and the gate reports "No subset file touched". The pin check on the boxel side also passes, because the pinned revision is still an ancestor of main. Thus the deployed catalog loses the definition and neither repo shows a red check. The compare API that the push path uses has the same field.
| gh api "repos/$GITHUB_REPOSITORY/pulls/${{ github.event.pull_request.number }}/files" \ | |
| --paginate --jq '.[].filename' > changed.txt | |
| else | |
| gh api "repos/$GITHUB_REPOSITORY/compare/${{ github.event.before }}...${{ github.sha }}" \ | |
| --jq '.files[].filename' > changed.txt | |
| gh api "repos/$GITHUB_REPOSITORY/pulls/${{ github.event.pull_request.number }}/files" \ | |
| --paginate --jq '.[] | .filename, (.previous_filename // empty)' > changed.txt | |
| else | |
| gh api "repos/$GITHUB_REPOSITORY/compare/${{ github.event.before }}...${{ github.sha }}" \ | |
| --jq '.files[] | .filename, (.previous_filename // empty)' > changed.txt |
When the old path is in changed.txt, the jobs run, and the subset sync fails because the file is missing from this checkout. That is the red check that this case needs. Regression in the new workflow. Non-blocking, but this is the case that the workflow exists to catch.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in c88ab58. Both the PR-files path and the push compare path now emit previous_filename alongside filename. A rename or move of a subset file therefore counts as touching it, the jobs run, and the subset sync fails on the missing file in this checkout.
There was a problem hiding this comment.
[Claude Code 🤖] Verified. This resolves it. Both paths now emit previous_filename for a rename, and a local jq test on a renamed entry gives the old path. The subset jobs ran and passed on c88ab58.
| @value={{@model.source}} | ||
| @onInput={{this.setSource}} | ||
| @readonly={{not @canEdit}} | ||
| @placeholder='Always allowed' |
There was a problem hiding this comment.
[Claude Code 🤖] The editor shows "Always allowed" for an emptied condition. But this field keeps that state separate from always-allowed. When the author clears the text, the value is { source: '', snapshot }. That is a present predicate: View shows it as "empty condition", and the serializer keeps it as '', not null. The placeholder does not look at which of the two states it is in.
| @placeholder='Always allowed' | |
| @placeholder={{if @model 'Empty condition' 'Always allowed'}} |
Regression. Non-blocking.
There was a problem hiding this comment.
[Claude Code 🤖] Agreed. This file mirrors the boxel-side definition byte-for-byte except for its import specifiers, so the change goes into the boxel source first and is then ported here, rather than letting the two copies differ. I've passed the suggestion (@placeholder={{if @model 'Empty condition' 'Always allowed'}}) to that change. I'll reply here with the commit once it's ported.
There was a problem hiding this comment.
[Claude Code 🤖] That order is correct. Keep the two copies the same. This item stays open until the port lands here. It does not block this change.
There was a problem hiding this comment.
[Claude Code 🤖] Ported in 0817dc0. @placeholder={{if @model 'Empty condition' 'Always allowed'}} now matches the boxel source, which gained the same change together with edit-test assertions for both placeholders. Apart from import specifiers and formatting, the two copies are again identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
backspace
left a comment
There was a problem hiding this comment.
[Claude Code 🤖] This re-review checks the new commit against the two open threads from the last review.
No blocking issues. Approved.
- Renamed subset file: resolved. Both the PR path and the push path now count the old path. The subset jobs passed on this commit.
- Empty-condition placeholder: still open, not blocking. The fix goes into the boxel source first and then comes here.
- Lint fails only on the missing
PolicyPredicateexport from@cardstack/runtime-common. That is expected until the boxel side ships.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
richardhjtan
left a comment
There was a problem hiding this comment.
[Claude Code 🤖] This review covered the scope gate and the job setup in boxel-test-subset.yaml, and the two definitions against the boxel-side serializer and the Switch/BoxelInput APIs. It did not cover the boxel-side PRs.
No blocking issues. There is one non-blocking fix and one follow-up.
- Treat a push whose compare lists 300 files as touching the subset. See the thread on the push path of the scope step.
- Follow-up, not this PR: the
hostandrealm-serverjobs copy boxel's CI stack setup, and that copy has already drifted from boxel's ownci-host.yaml, which no longer installsdbus-x11/upowerthrough apt. If boxel exposes this as a reusableworkflow_callworkflow that takes the catalog ref, this file shrinks to the scope job plus one call. Setup then has a single home, and a boxel infra change can no longer turn catalog PRs red where catalog authors can't fix it.
Lint is red only on the missing PolicyPredicate export from @cardstack/runtime-common. That clears once the boxel serializer change is on boxel main.
| gh api "repos/$GITHUB_REPOSITORY/compare/${{ github.event.before }}...${{ github.sha }}" \ | ||
| --jq '.files[] | .filename, (.previous_filename // empty)' > changed.txt |
There was a problem hiding this comment.
[Claude Code 🤖] On a push, the compare API lists at most 300 files, and --paginate doesn't return more (only the first page carries files). I checked this against this repo: a compare spanning 1,040 changed files returned exactly 300. A merge that touches a subset file among more than 300 changed files therefore reports "No subset file touched". Three merges on main have been that large. The simplest guard is to run whenever the list is full:
gh api "repos/$GITHUB_REPOSITORY/compare/${{ github.event.before }}...${{ github.sha }}" > compare.json
jq -r '.files[] | .filename, (.previous_filename // empty)' compare.json > changed.txt
# The compare API lists at most 300 files, so a full list may hide a subset file.
[ "$(jq '.files | length' compare.json)" -ge 300 ] && truncated=trueThen skip the grep gate when truncated is set. Regression in the new workflow. Non-blocking: the PR path uses the paginated files API (up to 3,000 files) and gates first, so this only weakens the post-merge backstop.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in d805670. The scope step now treats a listing at the API's cap as touching the subset, and the same guard covers the PR path's 3,000-file cap:
- push:
.files | length≥ 300; - PR: the PR's
changed_files≥ 3000.
A full list skips the grep gate, so the jobs run. The rename handling is unchanged.
The reusable-workflow follow-up (a single home for the stack setup the host/realm-server jobs copy from boxel) is tracked separately.
richardhjtan
left a comment
There was a problem hiding this comment.
[Claude Code 🤖] Approving. The notes in my earlier review are non-blocking.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Background and Goal
This adds the system-level policy definitions to the catalog realm.
realm-policy/realm-policy.gtsholdsRealmPolicy,PolicyRuleandOperationGrant, the shapes that the realm's operation-permission checks read.fields/policy-predicate/policy-predicate.gtsholdsPolicyPredicateField, the BXLwherecondition on a grant. Nothing reads a policy yet, so no behaviour changes. The field'spolicy-predicateserializer is platform code in boxel'sruntime-common, and the field refers to it by key.These are the first entries in boxel's catalog test subset (
packages/catalog/test-subset.jsonin cardstack/boxel). That subset is a pinned selection of the catalog's system-level definitions, which boxel's test stacks serve as the catalog realm so the platform can be tested against the catalog's one copy.Merge order
policy-predicateserializer andPolicyPredicatetype these definitions use. This PR's Lint is red until it is on boxelmain.Key decisions
.github/workflows/boxel-test-subset.yamlruns the boxel tests the subset manifest names whenever a PR here touches a subset file, using this checkout as the subset source. The deployed catalog serves this repo'smain, so a subset change has to keep boxel's tests green before it merges.cs-13167-plan-let-boxel-repo-tests-use-select-card-definitions-that), and otherwise against boxelmain. That lets a catalog change and the boxel change depending on it be validated together.cs-13077-policy-realmpolicy-policyrule-operationgrant-definitions-andmerges first, because it adds thepolicy-predicateserializer and thePolicyPredicatetype these definitions use. Until then, this PR's Lint fails on the missing export. This PR merges next. The boxel subset branch then re-pins to the merge commit, and its CI enforces that the pin is on this repo'smain.🤖 Generated with Claude Code