You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ux(cli): sequential rule approve fails with raw FAILED_PRECONDITION after proposals refresh #3884
As an operator reviewing several pending policy proposals, I want approving them one at a time to either work or tell me clearly what I need to do, so that I don't mistake a refreshed proposal for a failed or silent approval.
Problem Statement
When a sandbox has several pending proposals, approving one re-evaluates the others: they get new candidate hashes and a new prover run. Approving the next chunk ID from the earlier rule get then fails:
$ openshell rule approve my-sb --chunk-id ed6547bd-... # OK, policy v2
$ openshell rule approve my-sb --chunk-id a208defa-...
Error: × code: 'The system is not in a state required for the operation's
│ execution', message: "proposal inputs changed; evaluation refreshed,
│ refetch and review again"
rule get shows the second chunk's Candidate changed (63a700fa3fbf became a19fc994fa5c) while its rule text stayed the same. Running the identical approve again succeeds.
Impact / Why This Matters
Requiring re-review after re-evaluation is a sensible safety property, but:
The error opens with gRPC's generic FAILED_PRECONDITION description.
Step 4 of "Run Your First Agent" teaches this per-chunk loop, and OpenCode's first run produces two proposals, so a new user hits this immediately.
In scripts that keep only the last line of output, the failure looks like a silent no-op.
The workaround is rule approve-all, but the docs don't mention it at that step, and it approves everything rather than a reviewed subset.
Acceptance Criteria
The CLI renders this condition as an actionable message, for example: "Proposal was re-evaluated after policy vN; review it with openshell rule get <sandbox> --status pending and approve again". Ideally it also shows the refreshed prover result.
The docs mention the refresh behavior and approve-all in step 4 of "Run Your First Agent".
Optional: approve several reviewed chunk IDs in one call.
Reproduction Steps
Create a sandbox with curl and no policy for example.com or example.org.
Inside it, run curl https://example.com; curl https://example.org. Two proposals become pending.
Approve the first chunk, then the second, using IDs from the original rule get. The second approve fails as shown above.
Narrowed this down. The CLI isn't reusing a stale token: sandbox_draft_approve fetches a fresh review_token from GetDraftPolicy right before calling ApproveDraftChunk. The stale state is on the gateway side.
After chunk A is approved and the policy version changes, the other pending chunks are not re-evaluated. GetDraftPolicy keeps returning their old evaluation and review token. The refresh only happens inside the next approve, in require_current_proposal_evaluation (crates/openshell-server/src/grpc/policy.rs, around line 1568 at c0eb3db), which persists the refreshed evaluation and then returns FAILED_PRECONDITION.
So even the careful flow (approve A, re-run rule get, review B, approve B) fails once, and the prover result the user reviewed for B was stale:
B candidate before approving A: 095b1fce221f
approve A -> OK, policy v5
B candidate shown by `rule get` after A: 095b1fce221f <- still the old evaluation
approve B -> "proposal inputs changed; evaluation refreshed"
B candidate after the failed approve: 36a8614251ce
Possible directions, for maintainers to pick:
Re-evaluate a sandbox's remaining pending chunks when an approval changes its policy, or mark them stale so rule get shows it.
Have GetDraftPolicy refresh (or flag) pending chunks whose inputs no longer match the live policy before returning them.
At minimum, make the CLI show the refreshed evaluation when it gets this error, so the user can approve again after reviewing it.
Activity
fede-kamel commentedon Sep 29, 2026
Narrowed this down. The CLI isn't reusing a stale token:
sandbox_draft_approvefetches a freshreview_tokenfromGetDraftPolicyright before callingApproveDraftChunk. The stale state is on the gateway side.After chunk A is approved and the policy version changes, the other pending chunks are not re-evaluated.
GetDraftPolicykeeps returning their old evaluation and review token. The refresh only happens inside the next approve, inrequire_current_proposal_evaluation(crates/openshell-server/src/grpc/policy.rs, around line 1568 at c0eb3db), which persists the refreshed evaluation and then returnsFAILED_PRECONDITION.So even the careful flow (approve A, re-run
rule get, review B, approve B) fails once, and the prover result the user reviewed for B was stale:Possible directions, for maintainers to pick:
rule getshows it.GetDraftPolicyrefresh (or flag) pending chunks whose inputs no longer match the live policy before returning them.I'm happy to work on whichever you prefer.
docs(first-agent): note which proposals rule approve-all skips
fix(policy): make pending proposal refresh race-safe and bounded
docs(policy): describe proposal rechecks after approvals and approve-all
fix(policy): refresh pending proposals after a full policy replacement
fix(policy): refresh pending proposals when the sandbox policy changes (