Approval
Status: Current. Source:
~/inhuman/harness/docs/runtime/approval.md
Approval
Section titled “Approval”Parents: ../canon/grain.md, ../canon/proposals.md, session-job.md. Speech: this file.
Not this file: operator chords (../client/operate.md); YAML dialect (../packages/policy-processor/docs/README.md); tape paint (../client/transcript.md); MCP tools globs / toolSeverity (../client/operate.md). Session modes (act / plan) are not permission mode. Honor does not approve.
Leaves authorize. Sequences project. orchestrationResult is the PEP boolean. orchestrated is orchestrationResult !== null. BODY is phase 2 after an allow-side guard.
An ApprovableJob carries floor and remainder votes plus an optional human bit. Sequence Jobs (SessionTurn, ProgrammaticJob) are not approvable; they expose a cursor over leaves a present human can answer. The policy program always runs while policyResult is still null. approved is HITL confirmation (a pre-obligation discharge). The host does not derive it. Plexus is the write surface. No approval RPC. No Permission PEW/Proposal.
Unattended: the eval client supplies persisted auto / yolo and does not write approved (../client/shell.md). Ungranted write covers; grant write or use yolo so edits do not hang. A classifier attempt that cannot vote writes judgeFailMessage and retries; it does not flip the session, does not record a human denial, and does not seal interaction_required. An attached TUI may paint the consent prompt so a present human can beat the retry loop.
1. One sentence
Section titled “1. One sentence”Mint is not authorization to effect. registerExecutor(Class, guard, execute) is two functions: a guard (PEP, phase 1) that returns true | false, then execution (phase 2). Completions and the remainder classifier stay PEW. ToolCallJob is-a ApprovableJob. Sequences never approve themselves.
One policy. Three permission modes. One remainder PDP. manual, auto, and yolo all run {kernel}/policy.yaml. They differ on remainder ask and floor deny (consent prompt / classifier / allow). Floor block is deny-side in all three. Floor allow is allow-side in auto. Auto cluster floor ask is allow-side when autoAllow grants that cluster and the call is not hatch. Hatch is a skip-veto on that grant, not a second PDP. Ungranted write ask and deny cover in auto; the write flag is the write classifier (skip ask, classify deny).
approved is not the PEP bit. It is a human write. policyResult / judgeResult / the live permission mode are enough to decide. The decision itself is orchestrationResult.
2. Speech
Section titled “2. Speech”Two languages join here. XACML / Anderson name who decides and who enforces. Selective prediction names when a model may answer, retrieve evidence, or abstain. Code field names stay.
| Speech | Code | Meaning |
|---|---|---|
| PEP | registerExecutor guard; orchestrationResult |
Reference monitor. Named law LICENSE WHEN is the join. |
| PDP | floor policy.yaml; remainder program |
Floor first. Remainder PDP on floor ask or deny (except auto allow-skip and ungranted write). Never on floor allow. Never on floor block. |
| PIP | evidence cuts | Missing-attribute fetch. Round two is not a second vote. |
| PAP | {kernel}/policy.yaml, config.json pointers |
Owning a remainder program is repointing, not a seeded copy. |
| Permit | allow |
Effect is legal. |
| Deny (soft-mandatory) | deny |
Remainder: manual cover, auto classifier (ungranted write covers), yolo allow. |
| Hard-mandatory Deny | block |
Combining cannot lift it. No remainder PDP. Yolo does not rewrite it. Path !. Never a consent prompt. |
| NotApplicable | ask |
No applicable floor rule, or a rule whose effect is defer. |
| Remainder | autoRemainder |
Floor ask | deny (autoResidualOf) except auto allow-skip. Not ISO residual risk. Not block. Not floor-allow. |
| Hatch | hatchShape |
Computed from name + argsJson. Skip-veto on autoAllow (and persist). Floor-allow is not remainder. Not a CRDT field. Inert in manual/yolo. |
| Permission mode | manual | auto | yolo (standingPolicy / live-head currentPolicy) |
How remainder is handled. Closed tri-state. Unknown → manual. CLI always-approve aliases yolo; it is not a stored id. Last-choice for a new session is defaultPolicy (project, else kernel config.json; not a kv key). |
| Mode | SessionRoot.mode |
act / plan. Not this join. |
| Classifier | judgeResult; autoModeJudge |
Remainder PDP. Outcome is Permit or Deny iff judgeResult is still null. Never ask, never fail. |
| Abstain | judgeFailMessage; judgeResult stays null |
Indeterminate. Retry. Not a decision. |
| HITL bit | approved |
Human-only. Host never derives it. |
| Consent prompt | cover / needsHuman |
Remainder ask or floor deny under manual; ungranted write ask and deny in auto; looking auto floor deny (autoCoverDeny, ungranted cluster); or auto remainder with Indeterminate and no judgeResult. Floor block is not coverable. |
| File-read remember | SessionRoot.readAllowlist |
Prepared path the human allowed on a read-cluster remainder. Suffix descendants. Does not lift block. Not a write grant. |
The agent acting as the user is the XACML subject. The tool is the action. Path / payload is the resource. Classifier input is evidence, never “subject.”
Do not say residual (say remainder); judge/quiz (say classifier); fuse (say rejection threshold (g)); license-the-PEP (say authorization; named law LICENSE WHEN stays); processor (say remainder PDP); policy-the-tri-state (say permission mode).
3. Geometry
Section titled “3. Geometry”| Geometry | Meaning |
|---|---|
| ApprovableJob | Leaf the PEP must authorize. Fields: policyResult, judgeResult, judgeFailMessage, approved. Completions and sequence Jobs are not Approvable. |
| Sequence Job | SessionTurn, ProgrammaticJob. Not authorized. Expose approvalQueue / approvalQueueHead. |
| ToolCallJob | The model’s one tool_call. Is-a ApprovableJob: emission and votes on one object. Kinds: SystemToolJob, FileMutateToolJob, MCPToolJob, WebSearchToolJob, WebExtractToolJob, AgentCallJob, AgentCallbackJob, AgentNotifyJob, AgentMessageJob, ContextNeedToolJob. |
SessionTurn sequence SystemToolJob / MCPToolJob / … Approvable leaf ProgrammaticJob sequence (scheme bag) ToolCallJob inner leavesAdmit of a model tool_call mints a ToolCallJob kind. scheme_repl is a bare ProgrammaticJob, not a wrap. Inner scheme-repl admits mint nested ToolCallJobs onto that bag.
There is no TurnJob class (SessionTurn is the turn). There is no ApprovableExpectation. There is no WorkDemand.
4. Durable fields
Section titled “4. Durable fields”Authorization votes live on the leaf. Orchestration and cancel live on Job.
| Field | Type | Writer |
|---|---|---|
policyResult |
allow | deny | ask | block | null |
Daemon, after the policy program. null = not yet evaluated. |
judgeResult |
allow | deny | null |
Daemon, after a finished auto remainder. No fail. No ask. Cancel / abort / crash leave this null. |
judgeFailMessage |
string | null |
Daemon. Non-null after a failed auto judge attempt. Does not skip retry. Does not become a judgeResult. |
approved |
true | false | null |
Human only. Host never derives this. |
approvalComment |
string | null |
Human. Independent of approved. Tape paint reads this on the leaf. The next ReACT mints one TurnApprovalNoteChunk per nonempty comment (role: user after that round’s tool results). Not copied onto the sealed observation. |
orchestrationResult |
true | false | null |
Daemon. The guard’s return. true = run execute. false = skip execute, stamp completed. |
orchestrated |
computed boolean |
orchestrationResult !== null. Do not assign it. |
cancelled |
boolean |
Daemon. Honest cancel flag. false → true. Not a deny, not a human approved. |
completed |
boolean |
Daemon. Cancel stamps cancelled then completed and does not write orchestrationResult or approved. Deny writes orchestrationResult = false, receipt, then completed in the same action. BODY finally stamps completed. |
POLICY ERROR IS AWARENESS. The daemon publishes policyError?: string on the session hub when the operator’s policy.yaml fails to load or throws. It is presence, not a leaf vote. A successful eval of the operator file clears it.
floor policyResult null → allow | deny | ask | blockresidual judgeResult null → allow | denyreport judgeFailMessage null → stringhuman approved null → true | falseguard orchestrationResult null → true | falsecancel cancelled false → truesettle completed false → trueNo reverse edge. A later machine does not rewrite an earlier one. A tool retry is a new Job. A judge retry is the same leaf only because judgeResult is still null.
5. LICENSE WHEN
Section titled “5. LICENSE WHEN”The guard waits until some resolution exists. It does not wait for a consolidated approved. Code: ../packages/harness-sdk/src/models/session/license.ts licenseDecision.
when( approved === true | false || policyResult === block || (readAllowlist hit && policyResult !== null && policyResult !== block) || policyResult === allow || (yolo && policyResult === ask | deny) || (auto && autoAllowSkip) || (policy === auto && autoRemainder && judgeResult === allow | deny))HUMAN FIRST, THEN THE ALGO. Read approved first. Several guards may be true at once. The join is a disjunction — exclusive first-match is not the algebra. ONE PASS, ONE SIDE: the guard selects one side in that order and returns only that boolean, then stops.
allow-side (guard returns true → orchestrationResult = true → execute) approved === true || (readAllowlist hit && policyResult !== null && policyResult !== block) || policyResult === allow || (yolo && policyResult === ask | deny) || (auto && autoAllowSkip) || (auto && autoRemainder && judgeResult === allow)
deny-side (guard returns false → orchestrationResult = false → receipt → completed; no execute) approved === false || policyResult === block || (auto && autoRemainder && judgeResult === deny)Once approved is false, or once orchestrationResult !== null, a later true does not start BODY. Mid-BODY approved = false is a no-op — stop is cancelled.
policyResult block → deny-side // all three modes; no remainder PDPpolicyResult deny + manual → cover // yellow contour; approved stays nullpolicyResult deny + yolo → allow-sidepolicyResult deny + auto → remainder PDP // except ungranted write → coverpolicyResult deny + write + auto + !write grant → coverpolicyResult deny + write + auto + write grant → remainder PDPpolicyResult allow → allow-sidepolicyResult ask + write + auto + !write grant → coverpolicyResult ask + write + auto + write grant → allow-sidepolicyResult ask + auto + autoAllowSkip → allow-sidepolicyResult ask + manual → coverpolicyResult ask + yolo → allow-sidepolicyResult ask + auto → remainder PDP (cover after fail; ungranted write covers)policyResult null → waitNO MANUAL SWITCH. A judge fail message does not write currentPolicy = manual. AUDIT SURVIVES OVERRIDE. A human write of approved does not clear policyResult / judgeResult / judgeFailMessage. LOOK FORWARD ONLY. The guard returns once.
HATCH. Computed hatchShape from name + argsJson (../packages/harness-sdk/src/models/session/hatch.ts). Skip-veto on autoAllow skip and persist. Floor allow is allow-side in auto. Bash: \bnpx\b, \bbunx\b. Not hatch: writes (lockfiles / Makefile / workflows are enable ! deny; .vscode / .claude / .cursor / .git/hooks are write_block; .claude/worktrees is write-ask; AGENTS.md is ordinary write-ask), npm run, cargo run, git commit, in-tree source, git add / fetch, read, MCP.
AUTO ALLOW. Cluster grants { write, bash, mcp } (flag record). Session overlay OR standing (config.json autoAllow). Fires only in auto. Floor ask for a granted cluster is allow-side unless hatchShape. MCP grant is look-ups only (stem floor). Extra-root deny and block are not skip-allow. Write flag off: floor ask and deny cover; classifier does not start (looking and unattended). Write flag on: floor ask is allow-side; floor deny classifies. Other clusters: empty standing remainder ask classifies (looking and unattended); cover after a fail message. autoCoverDeny (default true): interactive floor deny covers instead of classifying, unless that cluster is already granted — then it classifies. Unattended deny still classifies except ungranted write. Edit payload is not a vote. Manual ignores the flags. Yolo already rewrites remainder ask.
6. Queue and head
Section titled “6. Queue and head”Third projection family on sequence Jobs. Not .work. Not collectors. Same children list, different cut.
needsHuman = !completed && !cancelled && approved === null && !readAllowlist hit && ( (policy === manual && policyResult === ask | deny) || (policy === auto && autoRemainder !== null && judgeResult === null && ( judgeFailMessage !== null || !autoClassifierPending )) )policy here is SessionRoot.currentPolicy. Floor block is not coverable. Manual floor deny is coverable. Looking auto remainder ask classifies first; cover only after a fail message. Ungranted write ask and deny cover (no classifier). Looking floor deny covers when autoCoverDeny (default) unless that cluster is already granted — then it classifies. Unattended remainder classifies except ungranted write; cover only after a fail message. In-flight floor / first judge attempt is not in the queue except the looking deny cover and ungranted write. Do not drop a fail-message cover on unattended — a present human may beat retries; the eval client still does not write approved.
Walk: instanceof ApprovableJob / nested SequenceJob. Head is what chrome binds.
7. Host pipeline
Section titled “7. Host pipeline”License is phase 1 of registerExecutor, not PEW honor.
registerExecutor(Class, guard, execute)guard(job) → true | false // phase 1execute(job) → await BODY // phase 2: only if guard returned trueThe starter does not await. It starts the guard while orchestrationResult === null && !completed && !cancelled. When the guard returns: orchestrationResult = that boolean; false → receipt + completed, skip execute; true → await execute. Bootstrap orchestrated && !completed skips the guard and runs execute. A torn deny (orchestrationResult === false && !completed) only stamps completed.
COMM FIRST. AgentCallJob, AgentMessageJob, AgentCallbackJob, AgentNotifyJob, and side-channel tools (update_metadata, context_need) return true without the program. policyResult stays null. Spawn is not a grant — the child’s tools still run the floor.
ONE DENY WRITER. The guard returning false is the only deny settler. Host rejectTool does not write approved. Cancel writes cancelled, not a deny.
POLICY PROGRAM RUNS ONCE. Skip when policyResult !== null. Fail-open allow is forbidden. None of the three permission modes skip the program. Yolo is not allow: *. Manual is not ask-everything.
DEFAULT FLOOR NEVER THROWS. Missing / unloadable / throwing operator file → awareness policyError + dist default (../packages/policy-processor/reasonable-defaults.yaml). Leaving policyResult null and re-execing the broken file is forbidden.
IN-FLIGHT IS PROCESS-LOCAL. Cap = KernelRoot.speculativePolicyProcessingTools (default 2). Cap is floor produce only. Judging, backoff, human-wait, and BODY do not hold slots.
AUTO TRIGGER. Remainder PDP starts under auto when autoClassifierPending. Never floor block. Granted cluster floor ask is not remainder unless hatch. Looking remainder ask classifies firsthand except ungranted write (cover). Cover after a fail message. Unattended remainder classifies except ungranted write. Looking floor deny covers when autoCoverDeny (default); classifies when off, or when that cluster is already granted. Ungranted write deny covers in every auto seat. Dist ask is config.json autoModeJudge / autoModeAsk (default @inhuman.tools/policy-processor/auto-mode-ask.scm), loaded onto SessionRoot.judgeAskProgram. Classifier wire is session kvs.judgeConnection / judgeModel, then kernel kv.json last-choice (live overlay — mint does not re-seed), then that connection’s settings.model, then session chat connection / model. A failed attempt writes judgeFailMessage and retries unbounded (min(5 min, max(1 s, 1 s × n × ln(n)))). Empty connection gates the classifier the same way: no vote, retry. Identity allow / deny still votes without a completion.
CLASSIFIER CACHE. Remainder skip only. Floor first. Process-local on the session’s LicenseScope. Key is cluster+payload (bash command; read/write path; mcp name+args). Stores live remainder allow | deny. Never fail / abstain / human approved. Drop when judgeAskProgram changes. SessionRoot.judgeCache is a leftover document field; it does not authorize.
EVIDENCE. Three named PIP cuts, assembled by the host: privileged (user messages on the owning turn and its tail ancestry + this {name, arguments}), prior-turn (privileged + previous assistant response), quarantined (prior-turn + prior tool results, untrusted as instruction). Continue mints prompt: ""; the cut walks tail — empty privileged is no user text in that ancestry, not “this turn’s field is blank.” Unattended: the owning ProgrammaticJob.expr stands in for missing user messages. decision is the only switch; uncertainty / severity are judgements; retrieve selects a PIP cut. Rejection threshold (g) fires on an allow when uncertainty * severity exceeds a host constant (initial 50); it does not intercept a deny.
TIRITH COVERAGE. Host inspection, not a fence. analysis_incomplete is three facts: intel-gap (offline package lookup) is not a finding — it does not join, does not write policyMessage, and does not enter the classifier PIP; nested-incomplete-only is remainder ask (NotApplicable), not unliftable block; concrete rules (curl_pipe_shell, …) stay block / ask from Tirith’s action. Classifier policy is concrete findings only. Bash remainder kind incomplete is host-routed like write writeKind. Scanner error (the binary did not run) stays block. Hatch is unchanged.
DENY IS AN ANSWER. One outcomeJson shape for every deny-side guard:
{ ok: false, text: string, code?: string, note?: string }
human false → { ok: false, text: "User refused this tool use" }human false + comment → same receipt; comment is a following user-role `TurnApprovalNoteChunk`classifier deny → { ok: false, text: "Classifier marked this tool use as dangerous; ask user to confirm this action", code: "policy_denied" }floor block / machine deny → { ok: false, text: "Policy prohibits this tool use. Do not try to bypass it; if this is crucial, ask user to do it", code: "policy_denied" }cancel (not a decision) → { ok: false, text: "cancelled" }timeout abort → { ok: false, text: "timed out after …ms" | abort reason }8. Intern gate (code ≠ product law)
Section titled “8. Intern gate (code ≠ product law)”Product law: BODY runs after orchestrationResult === true. Floor allow, yolo remainder, classifier allow, and a covering readAllowlist leave approved null.
Current intern: ../packages/harness-sdk/src/models/session/jobs/MCPToolJob.ts returns unless this.approved === true before minting McpToolExpectation. The Job executor does call ensureWork after an allow-side guard, so a licensed MCP leaf with approved === null intern’s nothing. Web-search / web-extract intern after BODY stamps connectionKey and do not check approved. System-tool BODY is the toolkit executor; it does not intern PEW.
Do not paper over this. Do not document ready: approved === true as the PEP — that is the intern bug, not LICENSE WHEN.
9. Permission mode
Section titled “9. Permission mode”| Permission mode | After policy.yaml |
|---|---|
| manual | Follow the program. Remainder ask and floor deny wait for a human. Floor block is deny-side. Floor allow is allow-side (hatch inert). HITL. |
| auto | Follow the program. Granted autoAllow cluster floor ask is allow-side unless hatch. Ungranted write ask and deny cover (no classifier); persist may grant write. Other ungranted remainder ask: classifier (looking and unattended); cover after fail (persist may grant the cluster). Interactive floor deny covers (autoCoverDeny default) unless that cluster is granted — then it classifies. Unattended deny classifies except ungranted write. Floor allow is allow-side. Floor block is deny-side immediately. Indeterminate: stay auto, write judgeFailMessage, consent prompt + retry forever. |
| yolo | Follow the program. Remainder ask and floor deny are allow-side. Hatch inert. block still holds. |
Unattended uses the same guards. evalSpec is admission (auto | yolo only). Observer currentPolicy must not replace it. Unknown interactive tokens are manual. plan / act as permission-mode values: catalog error.
POLICY FOLLOWS HEAD. currentPolicy is a turn overlay; standingPolicy on SessionRoot is pre-head and first-turn attribute. LICENSE WHEN reads SessionRoot.currentPolicy (live-head inherit). Assignment writes standing and, when a head exists, the overlay. Last-choice persist is policy/set onto ProjectRoot.defaultPolicy + config.json defaultPolicy. Ontology: session-job.md.
{kernel}/policy.yaml floor. Always. Dist default on miss / throw.config.json autoModeJudge auto remainder. Never floor block. default: "@inhuman.tools/policy-processor/auto-mode-ask.scm"config.json autoAllow standing cluster flags { write, bash, mcp }config.json autoCoverDeny interactive floor deny covers (default true; persist false to classify). A granted cluster still classifies looking deny.config.json defaultPolicy last-choice permission mode for a new sessionProjectRoot.defaultPolicy project last-choice; empty inherits kernelSessionRoot.autoAllow this-session overlaySessionRoot.standingAutoAllow hydrated from kernelSessionRoot.policyProgram loaded floor bodySessionRoot.judgeAskProgram loaded remainder bodyKernelRoot.speculativePolicyProcessingTools default 2; floor produce capKernelRoot.approvalAskThrottleMs default 2000; chrome, not the PEPKernelRoot.superKey persist-mod; chromeSessionRoot.readAllowlist extra-root read grantThe dist floor and remainder program live in ../packages/policy-processor/docs/README.md. Path jail after an allow-side decision stays the toolkit executor. yolo does not skip it.
10. Extra-root read grant
Section titled “10. Extra-root read grant”The human writes the path. Chrome may offer a parent. The host never infers a grant.
In-workspace read is already silent ("." enable). Extra-root miss is floor deny (remainder). A human allow of a read-cluster remainder stores that prepared path on SessionRoot.readAllowlist (session). Suffix descendants are implied. ! block still holds. Writes are not this list.
| Write | Who | Key stored |
|---|---|---|
| Once | human allow of this leaf (approved === true) |
the leaf’s prepared path |
| Allow this folder | human accept of a suggested parent | that parent |
LICENSE WHEN and needsHuman already honor a covering key. A comment cover does not write the list. There is no Always-this-folder on this list.
OFFER, NEVER INSTALL. The cover may suggest one directory up. Only a human chord writes it. Offer when the current extra-root read shares that parent with another observed extra-root read this session (readAllowlist key or another in-flight read on approvalQueue). A lone file does not offer. Denials are not evidence. Generalization is one directory up from the current ask, not LCA of the whole list — disjoint trees stay two grants, not $HOME.
Fences — fileReadParent returns nothing: . (workspace), /, one-segment (/tmp, /Users), $HOME (/Users/<name>, /home/<name>), volume roots. Relative prepared paths are in-workspace; they do not widen.
Chrome chords: ../client/operate.md. Matchers: ../packages/harness-sdk/src/models/session/read-persist.ts, ../packages/harness-sdk/src/models/session/read-widen.ts. Host rememberFileRead writes the leaf path after human-true; chrome writes a suggested parent then the bit.
11. Execute fence refused
Section titled “11. Execute fence refused”An OS execute fence is possible. The cost is a second product. That cost is not paid.
Shell is /bin/sh -c. Remainder allow is host-uid. Steer is the floor — policy, Tirith, classifier — on the inspectable request, before BODY.
Rejected: named Grant.sandbox; a project-root walker as capture; bundling a native LSM CLI (nono / bwrap / Seatbelt) as a shipping default. Paying for a fence means owning a platform TCB, capture that sees outside the project without a 10k-file walk around every shell, fail-closed that does not refuse ordinary npm test on a machine without the LSM, and a Windows story that is not “unenforced.” Until those four are true, do not name a missing execute fence, do not add a wrapper argv, and do not walk the tree for a receipt. Path policy stays software (../packages/policy-processor/docs/path-policy.md).
12. Package split
Section titled “12. Package split”| Seat | Owns |
|---|---|
plexus-expectations |
Job geometry. registerExecutor(Class, guard, execute). PEW honor has no approve hook. Only Job in the package. |
harness-sdk |
SessionTurn, ToolCallJob, ProgrammaticJob, ApprovableJob. needsHuman / licenseDecision / hatchShape. |
harness-server |
Host guards and execute bodies. Publishes policyError. |
policy-processor |
Match language. Dist never-throwing floor. Dist auto-mode-ask.scm. Not the tri-state. |
13. Rejected
Section titled “13. Rejected”| Alternative | Failure |
|---|---|
| Permission PEW/Proposal or approval RPC | Plexus is the write. Actor does not consider UAC. |
Host derives approved from the two votes |
Invents a second writer on the confirmation bit. |
ready: approved === true as the PEP |
Yolo / floor / judge allow leave approved null. (MCP intern still has this bug — §8.) |
manual / yolo skip the policy program |
Manual becomes ask-everything. Yolo becomes allow: *. block must hold in all three. |
Crash flips currentPolicy to manual |
A network blip changes session policy. Stay auto, retry. |
Crash writes judgeResult = fail |
Bootstrap then skips the classifier. There is no classifier fail state. |
Auto remainder PDP on floor block |
block is the unliftable arm. |
Hatch as a stored vote / rewrite policyResult to ask |
Hatch is computed. Floor allow stays allow. |
Floor-allow remainder via autoHatch |
Auto stricter than manual for dist-approved npm run. Hatch is skip-veto, not a second PDP. |
Named execute fence (Grant.sandbox) |
Remainder allow is host-uid. §11. |
| Auto-widen extra-root at k = 2 siblings | The cover decides. The human does not. |
| LCA of the whole allowlist | $HOME / / the moment two extra-root trees appear. |
| Folder grant ⇒ extra-root write | Read remember is not a write grant. |
Sequence Job is Approvable (SessionTurn.approved) |
Chrome answers the effector leaf. |
License on PEW (ApprovableExpectation) |
Expectation exists before license. Pending asks pollute honor. |
{ pickup: true } / stored orchestrated boolean |
Dual book. Two functions on registerExecutor already split guard from execute. |
Cancel writes approved = false |
approved is human-only. |
/approval auto as a product path |
The tri-state is SessionRoot.currentPolicy. |
| Plan / goal as deny-side guards in this join | Those are session modes. |
| Remainder classifier on every in-tree write | Ungranted write covers. Granted write skip is enable-hit ask only; deny still classifies. Payload is not a vote. |
Write enable polarity approve (like read) |
Manual loses the Diff cover. Bash arg_rest_type: write_path would allow-side rm / tee / mkdir. |
See also
Section titled “See also”| Doc | Role |
|---|---|
../canon/grain.md |
Causal spine; ToolCallJob is-a ApprovableJob |
../canon/proposals.md |
Cancel / Question. Permission is not a Proposal |
session-job.md |
POLICY FOLLOWS HEAD; collectors are not the cursor |
../client/operate.md |
Cover chords, /mcp persist |
../client/shell.md |
Unattended eval; cover shadows draft |
../packages/policy-processor/docs/README.md |
Floor YAML; bash/path match language |