Skip to content

Fix main: resolve the non-grantable suite's carried policy through the gated entry - #6389

Merged
lukemelia merged 2 commits into
mainfrom
cs-13310-realm-policy-non-grantable-test-the-refusal-holds-though-a
Sep 29, 2026
Merged

lukemelia merged 2 commits into
mainfrom
cs-13310-realm-policy-non-grantable-test-the-refusal-holds-though-a

Conversation

@habdelra

@habdelra habdelra commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Important

This fixes a test that is failing on main. realm-policy-non-grantable-test › "the refusal holds though a compiled policy carries the grant" has failed on main since #6349 merged (6846e6f864). It fails the same way at every later commit: the Catalog Test Subset Consumers runs at 8e0ffbd855, c77621083d and ab5fc9a2f3 all fail on this one test. Until this merges, every open PR that has merged main is red in CI's Realm Server Tests (2, 6) and in the Catalog Test Subset Consumers workflow's Realm-server tests, for this reason alone. #6371, #6379 and #6380 are among them. Neither #6349 nor #6361 breaks it alone: it's a merge skew between them. The test change below is the whole fix.

Realm Server Tests shard 2 fails on main in realm-policy-non-grantable-test › "the refusal holds though a compiled policy carries the grant". So every open PR's shard 2 is red.

resolveOperation refuses every write it leaves to the write lock. That includes a write to a stored card that a grant admits outright, because the lock judges that card's type again from its bytes. The suite's carrying helper resolved through resolveOperation, so two things went wrong:

  • The positive control, a granted annotate on a stored ledger, was refused.
  • The refusals the tests mean the gate to make (seal and update here, setMotto on a policy card elsewhere) were refused no matter what the gate decided, so those assertions could not fail.

The helper now resolves through resolveGatedOperation, the entry point dispatch.ts names for a caller that takes the lock's decision, and returns the definition. The positive control holds again, and a refusal has to come from the gate. With the gate's non-grantable checks disabled as a negative control, this test and two siblings fail, and they pass with the checks in place. This is a test-only change.

🤖 Generated with Claude Code

`resolveOperation` refuses every write it leaves to the write lock, which
is every write to a stored card, so the `carrying` helper's positive
control (a granted `annotate`) was refused along with the writes the test
means the gate to refuse, and those refusals could not fail. The helper
resolves through `resolveGatedOperation`, as a caller that takes the
lock's decision does, and returns the definition.

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

chatgpt-codex-connector Bot commented Sep 29, 2026 •

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-09-29T22:03:49.869191Z 38552c8 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 changed the title Resolve the non-grantable suite's carried policy through the gated entry Fix main: resolve the non-grantable suite's carried policy through the gated entry Sep 29, 2026

@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 checked whether each assertion that goes through carrying can fail only when the gate refuses. It was static: I read the code and ran eslint on the file, but did not run the suite.

No blocking issues. Resolving through the gated entry is the right fix. The three asks below are all non-blocking and all about the carrying helper. The first two are pre-existing gaps that let its refusal assertions pass without the gate deciding them.

  1. Nothing shows that the carried policy reaches the gate, because the realm's own compiled policy gives the same answer to every call here. Add a refusal under a carried policy that grants nothing. See the file-level comment.
  2. coarseDeclined: 'all' turns resolution failures into the gate's refusal. Use 'writes', which is what the realm gives this reader. See the thread on that line.
  3. The helper comment has the direction reversed on whose decision the caller takes. There's a wording suggestion in the thread.

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 🤖] In "the refusal holds though a compiled policy carries the grant", the positive control passes whether or not the carried policy reaches the gate, so its message ("the carried policy is the one the gate consults") claims more than it checks. The realm's own compiled policy also grants annotate on Ledger (it compiles rules[1] to ['annotate'], as "compiling records a grant of it…" asserts), and it refuses seal and update on its own. So if the helper's compiledPolicy override stopped reaching loadPolicy, every assertion in that test would still pass. The setMotto call through carrying in "no grant admits a write a subtype declares for itself" depends on the same override. Under the realm's own policy it is refused for lack of a grant, not by the policy-card rule.

A refusal under a carried policy that grants nothing would pin this, because the realm's own policy admits the same call:

await assert.rejects(
  carrying([], LEDGER_1)('annotate'),
  /operation-not-permitted/,
  'the gate consults the carried policy, which grants nothing',
);

The assertion is pre-existing, but this PR is what makes it the suite's positive control again. Non-blocking, and worth doing here.

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 e340990228. The test asserts that carrying([], LEDGER_1)('annotate') is refused. The realm's own policy admits that same call, so the assertion fails unless the carried policy reaches the gate. The existing check is relabeled "the carried grant admits annotate". realm-policy-non-grantable-test passes locally, 18 of 18.

name,
newOperationScope(carried, {
caller: scopeCallerFor(READER),
coarseDeclined: 'all',

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 🤖] With 'all', the refusal wrapper in resolveAndGate turns every resolution failure into operation-not-permitted before the gate runs. So the /operation-not-permitted/ rejections through carrying also pass when the name is one the type doesn't declare (unknown-operation) or its declaration didn't lower (invalid-operation). READER holds read, and the realm gives that caller 'writes', not 'all'. Under 'writes' those failures keep their own codes. Every call here is still a write the gate judges against the same carried policy, so the regex can only match a refusal from the gate:

Suggested change
coarseDeclined: 'all',
coarseDeclined: 'writes',

Pre-existing, 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 e340990228. The helper's scope uses coarseDeclined: 'writes', which is what the realm gives a reader. A resolution failure keeps its own code, so /operation-not-permitted/ matches only a refusal from the gate.

Comment on lines +383 to +386
// It resolves as a caller that takes the write lock's decision does. The
// plain resolution refuses every write it would leave to the lock, which is
// every write to a stored card, so through it a write the gate admits would
// be refused as well, and a refusal would say nothing about the gate.

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 🤖] "a caller that takes the write lock's decision" gets the direction backwards compared with dispatch.ts. There, the caller takes the gate's decision and hands whatever is left to the lock. This helper doesn't hand anything on; it drops the decision. It would help to say what that means for a result: a write that resolves here is one the gate didn't refuse, and the lock could still refuse it. For example:

Suggested change
// It resolves as a caller that takes the write lock's decision does. The
// plain resolution refuses every write it would leave to the lock, which is
// every write to a stored card, so through it a write the gate admits would
// be refused as well, and a refusal would say nothing about the gate.
// It resolves through the gated entry and keeps only the definition, so it
// refuses only what the gate refuses: a write the gate admits resolves,
// though the write lock would still judge its stored card. The plain
// resolution refuses every write it would leave to the lock, which is every
// write to a stored card, so through it an admitted write would be refused
// too, and a refusal would say nothing about the gate.

Wording only, 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 e340990228 with your wording. The helper resolves through the gated entry and keeps only the definition, so it refuses only what the gate refuses. The comment also says the helper judges the reader, whom the ACL lets read and not write.

@habdelra
habdelra requested a review from a team September 29, 2026 22:09
… one

The realm's own policy grants `annotate` too, so the positive control
passed whether or not the carried policy was consulted; a carried policy
that grants nothing now has to refuse it. The helper's scope declines
the reader's writes rather than everything, as the realm does for a
caller who may read it, so a resolution failure keeps its own code and
only the gate's refusal matches `operation-not-permitted`.

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

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Host Test Results

    1 files      1 suites   1h 18m 6s ⏱️
5 115 tests 5 101 ✅ 14 💤 0 ❌
5 130 runs  5 116 ✅ 14 💤 0 ❌

Results for commit e340990.

Realm Server Test Results

    1 files    284 suites   2h 11m 36s ⏱️
4 518 tests 4 518 ✅ 0 💤 0 ❌
4 570 runs  4 570 ✅ 0 💤 0 ❌

Results for commit e340990.

@lukemelia
lukemelia merged commit 0d26e74 into main Sep 29, 2026
68 checks passed
lukemelia added a commit that referenced this pull request Sep 30, 2026
Picks up #6389, which fixes realm-policy-non-grantable-test on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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