Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
f43a8f9
Merge remote-tracking branch 'origin/cs-13103-policy-grant-reaches-un…
habdelra Oct 1, 2026
3746ddd
Merge remote-tracking branch 'origin/main' into cs-13396-base-6418-an…
habdelra Oct 1, 2026
cb6c245
Test the reach-warning mark beside a live grant on the policy card, a…
habdelra Oct 1, 2026
a526e6d
Merge remote-tracking branch 'origin/cs-13363-policy-declare-validate…
habdelra Oct 1, 2026
b10ec86
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
c96503a
Merge remote-tracking branch 'origin/cs-13092-policy-lowering-issues-…
habdelra Oct 1, 2026
a843c42
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
8088f1e
Merge remote-tracking branch 'origin/cs-13363-policy-declare-validate…
habdelra Oct 1, 2026
7c932c9
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
8f148b3
Merge remote-tracking branch 'origin/cs-13363-policy-declare-validate…
habdelra Oct 1, 2026
60e487d
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
3a50a46
Pin the catalog commit that styles a grant's warning messages
habdelra Oct 1, 2026
ad51720
Add preview images of the warning mark for the PR description
habdelra Oct 1, 2026
9a49f93
Remove the preview images, which the PR description references by commit
habdelra Oct 1, 2026
32acd5e
Assert the Issues list marks a reach warning as one, and marks nothin…
habdelra Oct 1, 2026
e7d6097
Pin the catalog commit that marks a warning in the Issues list
habdelra Oct 1, 2026
91b08a7
Add refreshed preview images of the warning marks for a PR comment
habdelra Oct 1, 2026
a696446
Remove the refreshed preview images, which the PR references by commit
habdelra Oct 1, 2026
5e5052b
Merge remote-tracking branch 'origin/cs-13103-policy-grant-reaches-un…
habdelra Oct 1, 2026
1225392
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
0c3ada9
Merge remote-tracking branch 'origin/main' into cs-13396-base-6418-an…
habdelra Oct 1, 2026
d159983
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
a83e936
Merge remote-tracking branch 'origin/cs-13103-policy-grant-reaches-un…
habdelra Oct 1, 2026
2ea6b7c
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
74cb7f6
Merge remote-tracking branch 'origin/cs-13092-policy-lowering-issues-…
habdelra Oct 1, 2026
ec57031
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
699e75a
Merge remote-tracking branch 'origin/cs-13363-policy-declare-validate…
habdelra Oct 1, 2026
4f37bea
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
d10610f
Merge remote-tracking branch 'origin/cs-13363-policy-declare-validate…
habdelra Oct 1, 2026
988376d
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
e7dddf4
Merge remote-tracking branch 'origin/cs-13092-policy-lowering-issues-…
habdelra Oct 1, 2026
32e3964
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
f5b5c29
Merge remote-tracking branch 'origin/cs-13363-policy-declare-validate…
habdelra Oct 1, 2026
86c604c
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
d00e387
Pin the catalog commit that brings catalog main into the warning mark…
habdelra Oct 1, 2026
9300c97
Merge #6418 (a9ec8993) and #6415 (9ad34f32) into cs-13396-base-6418-a…
habdelra Oct 1, 2026
bdf4bc1
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
03d35b4
Merge remote-tracking branch 'origin/cs-13103-policy-grant-reaches-un…
habdelra Oct 1, 2026
c768836
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
be66a58
Merge remote-tracking branch 'origin/cs-13363-policy-declare-validate…
habdelra Oct 1, 2026
b3b221f
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
a1d62c6
Merge remote-tracking branch 'origin/cs-13363-policy-declare-validate…
habdelra Oct 1, 2026
e832935
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
51a3be8
Merge remote-tracking branch 'origin/main' into cs-13396-base-6418-an…
habdelra Oct 1, 2026
55a0678
Merge branch 'cs-13396-base-6418-and-6415' into cs-13396-policy-show-…
habdelra Oct 1, 2026
193ac5d
Merge remote-tracking branch 'origin/cs-13103-policy-grant-reaches-un…
habdelra Oct 1, 2026
affae9a
Merge remote-tracking branch 'origin/main' into cs-13396-policy-show-…
habdelra Oct 1, 2026
62eecd1
Merge remote-tracking branch 'origin/main' into cs-13396-policy-show-…
habdelra Oct 1, 2026
95b1132
Pin the catalog main commit that merged the warning mark
habdelra Oct 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/catalog/test-subset.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"repository": "cardstack/boxel-catalog",
"revision": "5af17c66c6fcc194b34997f108cb6e5b01ea0a57",
"revision": "eea1391f7230d8858426c66636da9e30159f22c4",
"files": [
{
"path": "realm-policy/realm-policy.gts",
Expand Down
121 changes: 121 additions & 0 deletions packages/host/tests/integration/realm-policy-test.gts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,22 @@ const classroomModule = `
}
`;

// A roster links to its students, so a grant that serves a roster's document
// hands over the students it links to.
const rosterModule = `
import { contains, containsMany, field, linksToMany, CardDef } from "@cardstack/base/card-api";
import StringField from "@cardstack/base/string";

export class Student extends CardDef {
@field name = contains(StringField);
}

export class Roster extends CardDef {
@field teacherIds = containsMany(StringField);
@field students = linksToMany(() => Student);
}
`;

function classroom(teacherIds: string[]) {
return {
data: {
Expand Down Expand Up @@ -631,11 +647,13 @@ module('Integration | realm policy', function (hooks) {
async function renderPolicyNamed(
path: string,
rules: Record<string, unknown>[],
modules: Record<string, string> = {},
) {
let { realm } = await setupIntegrationTestRealm({
mockMatrixUtils,
contents: {
'classroom.gts': classroomModule,
...modules,
[`${path}.json`]: policyDocument(rules),
},
});
Expand Down Expand Up @@ -893,6 +911,109 @@ module('Integration | realm policy', function (hooks) {
assert.dom('[data-test-realm-policy-validate-failure]').doesNotExist();
});

// The grant a warning mark is beside, as the card lists it.
function grantMarkedWithWarning() {
return document
.querySelector('[data-test-policy-grant-warning]')
?.closest('[data-test-policy-grant-status]');
}

test('a live grant that hands over cards of a type no rule grants is marked live with its warning beside it', async function (assert) {
await renderPolicyNamed(
'policies/reaching',
[
{
targetType: { module: '../roster', name: 'Roster' },
grants: [
{ operation: 'read', where: teachesPredicate },
{ operation: 'update', where: teachesPredicate },
],
},
],
{ 'roster.gts': rosterModule },
);

assert.deepEqual(
await grantStatuses(),
['live', 'live'],
'a warning leaves its grant live',
);
assert
.dom('[data-test-policy-grant-inactive]')
.doesNotExist('and marks nothing inactive');
assert
.dom('[data-test-policy-grant-warning]')
.exists({ count: 1 }, 'one grant is marked with a warning');
let marked = grantMarkedWithWarning();
assert.strictEqual(
marked?.getAttribute('data-test-policy-grant-status'),
'live',
'the marked grant is live',
);
assert
.dom('[data-test-operation-grant-operation]', marked ?? undefined)
.hasText('read', 'it is the read, whose document carries the students');

await click('[data-test-policy-grant-warning] summary');
assert
.dom(
'[data-test-policy-grant-warning-message="grant-reaches-ungranted-type"]',
)
.exists({ count: 1 })
.isVisible('opening the mark shows the warning')
.includesText(
'no rule grants a read of Student',
'which says what the grant hands over',
);

assert
.dom('[data-test-policy-issue]')
.exists({ count: 1 })
.hasAttribute('data-test-policy-issue', 'grant-reaches-ungranted-type');
assert
.dom('[data-test-policy-issue-rule]')
.hasText('Roster', 'the warning is listed with its rule');
assert
.dom('[data-test-policy-issue-operation]')
.hasText('read', 'and its grant');
assert
.dom('[data-test-policy-issue] [data-test-policy-issue-warning]')
.exists('and is marked there as a warning');
assert
.dom('[data-test-policy-issue-message]')
.includesText('no rule grants a read of Student');
});

test('a policy whose issues only leave grants inactive marks no warning', async function (assert) {
await renderPolicyNamed('policies/inactive-only', [
{
targetType: { module: '../classroom', name: 'Classroom' },
grants: [
{ operation: 'read' },
{ operation: 'delete', where: MISTYPED },
{ operation: 'query', where: UNFILTERABLE },
],
},
]);
Comment on lines +987 to +997

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 test can't fail on the 'live' gate in grantWarnings, which is what keeps a warning off a grant that admits nothing. Its policy draws no warning-severity issue at all, because Classroom has no links, so it renders the same with or without the gate.

That case can happen. A snapshot-annotated read on Roster compiles with admitsNothing: 'snapshot', but reachLane still walks it, so validate records grant-reaches-ungranted-type on that grant. I traced this through policy.ts and validate.ts but did not run it. A test (or a policy with this grant) pins that a warning never replaces the inactive mark:

{
  targetType: { module: '../roster', name: 'Roster' },
  grants: [{ operation: 'read', where: { bxl: teachesPredicate, snapshot: true } }],
}
// grantStatuses() is ['inactive']; [data-test-policy-grant-inactive] and the
// snapshot note exist; [data-test-policy-grant-warning] does not;
// [data-test-policy-issue="grant-reaches-ungranted-type"] exists.

Keep that last assertion. It shows the policy really draws a warning. If the reach check stops walking admits-nothing grants (see the review body), the test then fails instead of silently no longer testing the gate. This gap belongs in this PR, but it's non-blocking: the gate itself is correct.

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 🤖] Not adding this one, because #6415 is removing the case it relies on. Its author is changing the reach check to walk only grants that can admit something: a query grant needs a filter, and no other grant may carry a snapshot predicate. Under the same rule, a snapshot read stops counting as granting a read of its type, which is the second point in your review body. After that, no compile records a warning on a grant that admits nothing. The suggested policy would then draw no warning, so the test would fail on #6415's next head instead of pinning the gate.

The gate stays as the card's rule: a warning is marked only beside a live grant. No real policy can reach it, and stubbing the validate answer to reach it would test the stub rather than the card. This branch follows #6415's head, so it picks the reach change up when that lands.

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 🤖] That change is on #6415 now (f5f38c0, "Neither walk nor count as readable a grant that admits nothing"), and this PR's base has it as of 5e5052b. A snapshot read on Roster now draws no reach warning, so no compile produces the case the gate guards against.


assert.deepEqual(await grantStatuses(), ['live', 'inactive', 'inactive']);
assert
.dom('[data-test-policy-grant-inactive]')
.exists(
{ count: 2 },
'each grant an issue leaves out is marked inactive',
);
assert
.dom('[data-test-policy-issue]')
.exists({ count: 2 }, 'and each issue is listed');
assert
.dom('[data-test-policy-grant-warning]')
.doesNotExist('no grant is marked with a warning');
assert
.dom('[data-test-policy-issue-warning]')
.doesNotExist('and no listed issue is marked as one');
});

test('a view created in an index render never asks what the policy compiles to', async function (assert) {
await setupIntegrationTestRealm({
mockMatrixUtils,
Expand Down
Loading