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
permissions: the gate marks every call costly, so the surface cannot tell rm -rf from ls #953
The engine's permission gate marks every call StakesCostly, deliberately and alike — rm -rf *, git push --force, and ls:
// THE STAKES ARE `costly` AND NOT `irreversible`, deliberately and for every// call alike. This gate does not know what a command will do — that judgement// is internal/approval's, and it is expressed as WHETHER TO ASK rather than as// how much is at stake …
internal/session/consent.go (consentAsk). Nothing in internal/approval ever produces StakesIrreversible; only test fixtures reach that value.
So the surface cannot tell a grave call from an ordinary one, and every drawing that would like to — where the pointer opens, whether always is offered, whether a clock may run — has to treat them the same. The owner ruled on 2026-09-11 that a permission's pointer opens on allow once for an ordinary call and on deny for a grave one; the second half is unreachable until the engine says which is which, so #933 ships deny-first for all of them.
Replication
Deterministic (no model). In internal/session, raise the gate's own question for a destructive command and read its stakes:
q:=agent.consentAsk(1, ai.ToolCall{Name: "bash", Arguments: `{"command":"rm -rf *"}`}, decision, true, "run it?")
// TODAY: q.Stakes == session.StakesCostly, and q.Options still carries the widening `always`.
Acceptance
internal/approval's judgement — the shapes it will never stop asking about — is what says a call is irreversible; the gate passes it through to Question.Stakes. No hardcoded command list in the surface, and no string matching on the command in internal/session (the repository's generic-and-meta law).
rm -rf *, git push --force, a drop table, a delete of a path outside the workspace: Stakes == StakesIrreversible, always not offered, no clock (Question.Check already refuses a clock on irreversible).
ls, go test, a read: StakesCostly, unchanged.
The manual (internal/manual/chat/permissions.md) says what enter does on each, in the asker's words ("what happens if I just press enter?").
Implementation pointer — the mechanism already exists, so this is a pass-through, not a new judgement.
internal/approval/floor.go has AlwaysAsks(tool string, args json.RawMessage) bool. It is true for exactly the grave shapes: bash.go's critical table (criticalHit) and ActsInThePersonsName. Its doc comment already states the reason in the words this issue needs — these are the shapes that hold under a blanket allow, so a memo cannot swallow them.
That is the same set as "irreversible", and it is why always must not be offered on them: a memo provably cannot cover one, so drawing the option is a lie the gate will refuse to honour.
So the change is:
internal/session/consent.go, consentAsk: Stakes = StakesIrreversible when approval.AlwaysAsks(call.Name, call.Arguments), StakesCostly otherwise. Replace the paragraph of the comment that says the stakes are the same for every call alike — do not leave it beside the new behaviour.
Same place: drop the widening always option when the stakes are irreversible. One condition, from the same predicate — not a second list.
internal/tui3: the pointer already reads questionHandsOnly(q) alone (Questions: one frame, one chooser, one key grammar — and the amber on the marks alone #933, deny-first for all). With stakes now graded, the ruling's other half becomes reachable: pointer on allow once for StakesCostly, on deny for StakesIrreversible. TestTheGateGradesNoCallAsIrreversibleAndMarksDenyTheSafeAnswer is the tripwire Questions: one frame, one chooser, one key grammar — and the amber on the marks alone #933 left behind and it asserts today's behaviour — it is now the thing to REPLACE, deliberately and in the same commit, with a test that asserts the graded split. That is the one existing assertion this issue is allowed to change; nothing else.
NO string matching on the command in internal/session, and no command list anywhere outside internal/approval/bash.go — the generic-and-meta law. If a shape is missing, it is added to that table with its own test, never to a second table elsewhere.
Activity
consent: enter denies on the engine's own permission, proved against …
questions: several from one step are one panel with a tab each, and p…
consent: enter denies on the engine's own permission, proved against …
questions: several from one step are one panel with a tab each, and p…
consent: enter denies on the engine's own permission, proved against …
Questions: one frame, one chooser, one key grammar — and the amber on…
questions: several from one step are one panel with a tab each, and p…
questions: several from one step are one panel with a tab each, and p…
santoshkumarradha commentedon Sep 11, 2026
Implementation pointer — the mechanism already exists, so this is a pass-through, not a new judgement.
internal/approval/floor.gohasAlwaysAsks(tool string, args json.RawMessage) bool. It is true for exactly the grave shapes:bash.go's critical table (criticalHit) andActsInThePersonsName. Its doc comment already states the reason in the words this issue needs — these are the shapes that hold under a blanket allow, so a memo cannot swallow them.That is the same set as "irreversible", and it is why
alwaysmust not be offered on them: a memo provably cannot cover one, so drawing the option is a lie the gate will refuse to honour.So the change is:
internal/session/consent.go,consentAsk:Stakes = StakesIrreversiblewhenapproval.AlwaysAsks(call.Name, call.Arguments),StakesCostlyotherwise. Replace the paragraph of the comment that says the stakes are the same for every call alike — do not leave it beside the new behaviour.alwaysoption when the stakes are irreversible. One condition, from the same predicate — not a second list.internal/tui3: the pointer already readsquestionHandsOnly(q)alone (Questions: one frame, one chooser, one key grammar — and the amber on the marks alone #933, deny-first for all). With stakes now graded, the ruling's other half becomes reachable: pointer onallow onceforStakesCostly, ondenyforStakesIrreversible.TestTheGateGradesNoCallAsIrreversibleAndMarksDenyTheSafeAnsweris the tripwire Questions: one frame, one chooser, one key grammar — and the amber on the marks alone #933 left behind and it asserts today's behaviour — it is now the thing to REPLACE, deliberately and in the same commit, with a test that asserts the graded split. That is the one existing assertion this issue is allowed to change; nothing else.NO string matching on the command in
internal/session, and no command list anywhere outsideinternal/approval/bash.go— the generic-and-meta law. If a shape is missing, it is added to that table with its own test, never to a second table elsewhere.questions: several from one step are one panel with a tab each, and p…
questions: several from one step are one panel with a tab each, and p…
questions: several from one step are one panel with a tab each, and p…
permissions: the gate grades its own calls, and enter follows the sta…
questions: several from one step are one panel with tabs, and permiss…
permissions: one predicate decides the safe answer, on a panel and on…
permissions: the frame names its way out wherever the pointer stands …