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
Commit c7d6f38
Browse filesBrowse the repository at this point in the historyBrowse files
Autonomous tasks: durable runs, and memcode task run end to end
Milestone 2. A task can now actually execute: definition, run record, execution,
persisted outcome, inbox. No triggers, no PR driver, no detection yet.
A Run is immutable history. It freezes everything that decided what an execution
meant — the definition's revision AND its full text, the resolved project, the
expanded authority, the runtime, the limits — before any work starts. Nothing
downstream re-reads the YAML, so editing a task while it runs cannot change what
that run was authorized to do. The definition is stored whole rather than by
reference so a run stays readable after its file is edited or deleted.
Run identity is the idempotency boundary. The row is created before the work
begins, and a unique index on (task, trigger occurrence) means a repeated
dispatch loses at the ledger rather than by hoping two executions do not
overlap. Manual runs are exempt: asking twice by hand is a deliberate act.
Claiming is a conditional update, so two processes racing cannot both win.
Restart semantics are explicit rather than accidental. A run whose process died
is FAILED as interrupted — never silently resumed, never silently re-run. A
partially finished mutating run cannot be safely continued by a process that did
not see what it did, and re-running it automatically is precisely how
at-least-once dispatch becomes duplicate side effects. If the work still needs
doing that is a new run with a new identity, which the ledger shows. Death is
detected by a dead pid on this host, or a stale heartbeat anywhere.
Runs live in their own database, not the gateway's: `task run` has to work where
the daemon has never started, and the ledger is an audit trail that must outlive
operational state that gets pruned.
The authority finding is the important part of this commit. read_only was first
implemented as a stricter permission mode, on the reasoning that a detached run
has no human, so every prompt is a denial. A real run disproved it: a read_only
task told to create a file created it, logging "auto-allowed" in ask mode. The
authorization judge downgrades a prompt to an allow when the request plainly
asked for the action — a good rule interactively, where it catches an agent
freelancing PAST what was wanted, and exactly backwards for a task, whose
instructions always authorize the task's own work.
So authority is enforced as CAPABILITY instead. The child runs with the existing
read-only tool whitelist, reached through a new --read-only on `memcode run` and
jobs.SpawnSpec, because a tool the session was never given cannot be argued for.
Reusing that whitelist rather than hand-listing tools to deny also means it keeps
covering tools added later. Note it is currently STRICTER than the tier's stated
grants — a read_only task cannot run `go test` either — and honouring
process.execute_readonly needs per-grant command gating in milestone 4. Too
strict is the right direction to be wrong in.
Outcome classification is the weak part and is knowingly provisional. It reads
the agent's prose, and the same denial was reported three different ways across
three runs ("was denied", "is being denied", "the run was blocked"), so the
patterns err toward surfacing rather than toward calling things fine. Verify
commands and a structured verdict replace this in milestone 4.
Inbox state is unseen/seen/acknowledged, never a boolean: being shown a banner
is not the same as having dealt with something, which matters most for exactly
the needs_attention runs.
runCmd.Flags().String("deny-tools", "", "internal: comma-separated toolset/tool deny-list for a delegated job (deny wins)")
364
371
_=runCmd.Flags().MarkHidden("deny-tools")
372
+
runCmd.Flags().Bool("read-only", false, "internal: explorer mode — read-only tools only, no edits and no bash")
373
+
_=runCmd.Flags().MarkHidden("read-only")
365
374
runCmd.Flags().String("model", "", "model for this run (a catalog label like sonnet or opus); overrides the remembered pin without changing it")
366
375
runCmd.Flags().String("browser-session", "", "internal: \"existing_chrome\" attaches this run to the user's own already-running Chrome via the gateway browser broker (fails closed, never falls back to ephemeral)")
0 commit comments