Files
stack/docs/plans/foundation-v1-candidate/RUNTIME.md
T

320 lines
24 KiB
Markdown

# Mediated runtime contract — phase-2 review candidate
Status: proposed technical contract after Jason's **Q28 A** ruling. No component
below has been installed. Pi remains the engine. A Mosaic-controlled terminal
is acceptable; native Pi screen/shortcut parity is not an initial requirement.
The same authorized operations are intended for later desktop/web clients.
This file completes the operation and evidence rules alongside [the package
rules](README.md) and [runtime shapes](runtime.schema.json). Closed JSON shapes
are necessary, not sufficient. `check.py` checks synthetic examples, not a live
permission system. Fixtures are independent shape cases, not one valid run graph.
## 1. Components and authority
| Component | Owns | Must not do |
|---|---|---|
| Mosaic client | Scope display, user interaction, authenticated requests, authorized rendering | Hold client-private task truth, set trusted actor/producer, bypass the broker |
| Control broker | Authentication binding, policy intersection, current claims/controllers, admission, routing | Treat agent text or engine events as approval, fall back to another scope/account |
| Trusted publisher | Serialized immutable records, artifact index, receipts, recoverable projections | Accept a worker's producer label as proof, acknowledge an undurable commit |
| Engine adapter | Exact Pi instance/session, pinned configuration, reviewed tool bridge | Expose raw engine control channels, discover ambient extensions/context |
| Command gateway and supervisor | Enforced command limits, process cohort identity, observations and stopping | Mount credentials, host control endpoints, private engine home, or shared evidence into command jobs |
The broker/publisher/supervisor are runtime infrastructure, not a new autonomous
conductor agent. Git, credentials and shared policy retain conductor/owner
authority. This design does not add a background goal scheduler.
Initial ingress proposal: a local owner client over a private, ownership-checked
local channel. Bind owner identity through OS credentials and reviewed bootstrap
policy, not a JSON principal ID. Sandboxed engines/jobs must not reach that owner
control endpoint. Agent/service ingress uses a distinct, execution/delegation-bound
channel with narrower operations. Provider API credentials are never control-plane
credentials. Remote ingress is refused until an authenticated mapping is approved
and tested; a future web interface is not permission to trust HTTP actor headers.
A compromised host owner/root is outside the sandbox boundary, not something this
contract claims to contain.
## 2. Wire, operations, and receipts
The proposed control transport uses bounded UTF-8 JSON frames. Reject duplicate
keys, non-finite numbers, unknown fields, bad encodings and metadata frames over
1 MiB before admission. No shell expansion, URL fetching or host-path resolution
is implied by an argument. Sidecar body ingestion is authenticated, private,
bounded by the selected operation/profile, and correlated to the request. It
cannot publish work or grant authority independently. Validate content and digest
before replacing a staging handle with a trusted artifact reference.
`controlRequest` defines request ID, target scope, expected record references,
optional assignment reference, exact operation and closed parameters. Actor,
producer, authorization and outcome come from the service. Public ingress validates
only its request sub-schema; a valid runtime-record or response object is never
accepted as a client request. `command.execute`
retains its separate command request shape. `inputRequest` is exclusively for
prompt, stop and approved dialog replies, with execution/connection generations.
All 29 permission names have a request contract: 28 control operations and the
separate command request. Descriptive CLI commands are wrappers, never another
permission system. JSON stdin/file forms submit the same request; they do not
allow arbitrary engine JSON. No new command described here exists today.
| Family | Required interpretation beyond shape |
|---|---|
| Project/workspace create/change | Proposed ID/payload is not a published envelope. Validate exact record-kind payload, owning scope, lifecycle and reserved/delegated target IDs; publisher supplies envelope/authorization. Workspace creation needs an existing exact project. |
| Mission/task create/change | Preserve single-parent ownership, mission policy intersection, dependency acyclicity and current approved intent. A proposed accepted status cannot bypass acceptance. |
| Assignment change | Null current reference explicitly requests creation; otherwise exact current ID/revision must match. New assignment does not abandon existing assignments. |
| Registration manage | Explicit creation/update; grantee identity, parent ceilings, allowed role and delegation must resolve. Never infer membership from creation or dependency. |
| Work read | `records` returns authorized work records; `execution-status` returns safe execution metadata; `configuration` runs the shared hash check. No transcript, account secret or private launch-input dump. |
| File read/change | Only the bound workspace, permitted path components and current limits. Contents/entries are distinct views. Put with null expected digest requires absence; delete requires an existing matching digest. No root deletion. |
| Work propose/result submit | A proposal is not approved work; a submitted result is not acceptance. File/work changes require a recorded assignment. |
| Task/mission accept | Exact subject/criteria revisions, independently authorized reviewer, verified evidence and current approval authority. |
| Decision issue | Validate decision-kind basis and issuing authority. Creation uses a new ID; revision/revocation requires the exact current decision in expectedRefs and the same immutable identity/scope. A model cannot issue owner approval because it can write prose or name the owner. |
| Conversation read/observe | Check separate conversation grants on each page/stream. Record visibility never auto-loads a transcript. |
| Execution launch/control/transfer | Apply sections 3-4, not native engine launch/attach semantics. |
| Workspace retire/reopen | Stop admission immediately; finalize retirement only after safe stop/reconciliation. Retain files/history. Reopen does not restore revoked grants or restart work. |
| Message send | Apply section 5 at both send and delivery. No automatic work assignment or acceptance. |
| Audit read | Resolve classified artifacts with the caller's own rights; knowing a reference is not permission. |
| Session adopt | Explicit reviewed decision plus preserved legacy snapshot; no filename-derived membership or in-place historical conversion. |
New creation/change/acceptance/adoption operation names are **separate explicit
grants**, not implicit additions to Reader/Contributor/Reviewer/Coordinator.
In particular `mission.accept` does not follow automatically from `task.accept`.
System-scoped registration delegation requires reviewed ceiling authority and
an approved `delegation-basis`: grantee, allowed operations, existing scopes,
preallocated new target IDs, parent decision, expiry and finite use limit. The
parent's limits still intersect it. A scope role cannot redefine shared policy.
Bootstrap creation uses explicit owner authority and an auditable bootstrap
assignment/bundle; ordinary agents cannot select a bootstrap bypass. Once work
exists, changing actions bind to their recorded assignment. Taskless discussion
and inspection never silently become file changes.
`controlResponse` distinguishes succeeded, accepted, already-active, refused and
unknown. Accepted means durable admission only. Success/acceptance needs a receipt.
An already-active response exposes identity only after authorization. Pre-admission
invalid/unauthorized requests may receive an unreceipted refusal; an audit outage
or uncertain action is never represented as success. Unknown effects require
recovery, not a retry under a new ID. Outcome data is a classified reference.
Reason codes are closed: allowed, invalid-request, not-authorized, stale-revision,
missing-state, already-active, control-conflict, retired, unsupported-capability,
audit-unavailable, unknown-effects, unsafe-replacement, request-id-conflict,
expired. Client copy must distinguish these without leaking private state.
Request identity is bound to authenticated principal, instance/epoch and canonical
request digest, including referenced input digests. A matching retry returns its
existing receipt/state. Different content under the same ID refuses. Private
bodies are not copied into a public request log. After a reset/lost index, old
channels/claims cannot authorize replay: recover the authoritative namespace or
require explicit reinitialization and new intent. Do not silently recreate it.
## 3. Launch, claims, sessions, and configuration
1. Authenticate, resolve exact identities/revisions and current policy. Closed or
ambiguous scope, bad config, missing evidence or unsupported adapter refuses.
2. Resolve work selection. Continue is the default; select/abandon name exact
assignments. Abandon changes only those assignments after authorization.
None permits conversation/inspection, not arbitrary changes.
3. Reserve the single `(agent, project, workspace)` claim in a serialized durable
transaction. Concurrency initially remains one per tuple; unsupported higher
tuning refuses rather than being silently ignored.
4. Resume selects the recorded current session or an explicit same-scope session.
Genuine first use creates and announces an initial conversation. Once history
or initialization is recorded, missing state is an error, not new first use.
Resume pins an immutable pre-load snapshot and selected leaf (null only for a
verified empty history). Verify the engine loads that history/branch; unsupported
selection refuses. These private hashes are not part of the comparable base.
5. Fresh creates a new conversation with no old chat/automatic summary. Metadata
may retain predecessor references; they are not transcript loading authority.
If an execution is active, an explicit replacement binding/generation and
authority are required. Stop admission, verify old engine and command cohorts
stopped, reconcile effects, then release its claim. Idle/abort/EOF is not proof.
6. Bootstrap the selected pinned engine in a private instance with inference and
tool admission closed. Obtain/verify native session identity and effective
settings. If the adapter cannot establish this before effects, it is unsupported.
Do not guess the latest file or invent an engine session header.
7. Publish the session binding and full launch manifest, verify snapshot digests,
then open only the approved capabilities. Record started state and actual
process identity. Incomplete bootstrap enters recovery, not automatic replay.
A manifest includes exact agent/session/settings/capability references, image
identity, comparable base projection, context snapshots, policy references and
selected assignments. `agentConfigHash` hashes the normalized base projection;
`launchManifestHash` hashes normalized `inputs`, not its own hash or timestamps.
Preserve ordered inputs. Normalize the skill set before JCS serialization.
Credentials and credential hashes are absent. Account selection is non-secret
identity metadata, resolved through #50 without fallback or in-session swapping.
Private/personal/project/work context appears only in authorized launch inputs.
Designated shared general preferences need their classification record; when they
serve as shared behavior instructions, include their content digest in the base
instruction projection. Do not omit a shared instruction merely because its
source is named USER. Context selection verifies source kind, classification,
audience and scope; a supplied category is not proof. Work context is a structured
projection of R6's approved records, preserving proposed/unverified labels.
Canonical approved configuration publication triggers a comparison for affected
executions. Also compare on client connection/reconnection and on demand. Publish
matching/changed/unknown and checked time. Missing current sources yields unknown,
not matching. Deduplicate notices by execution/old-hash/new-hash/status, retain the
current status badge, and deliver on reconnect using the recorded cursor. A local
UI toast acknowledgement changes no canonical state. No periodic model polling
or silent reload is required. These are event-triggered comparisons, not proof
of continuous live memory equality. Permission revocation acts immediately even
though ordinary configuration drift is a non-blocking Fresh recommendation.
## 4. Control and the Pi adapter boundary
A connection is observe or control. Both need their own visibility grants; control
is not implicit transcript access. There is one current controlling connection
and generation per execution. Check both execution and controller generations on
admission. Transfer is explicit, targets an authorized connection, and invalidates
old queued input before admitting new-controller input. Previously admitted work
keeps its true causal actor; transfer does not retroactively rename it.
A client disconnect does not release an execution claim or prove work stopped.
Recovery replacement without the current controller requires an explicit
qualified owner/delegation decision; knowing the current generation is not a grant.
Revocation closes affected ingress/streams and fences new unsafe actions. It also
stops affected running work, recording uncertainty until actual stopping is proven.
Unrelated independently authorized/recordable work can continue.
Proposed Pi integration: a broker-owned RPC process with a reviewed, pinned tool
bridge, no builtin tool escape, no ambient packages/extensions/templates/context,
and no raw RPC endpoint exposed to clients. The bridge requests gateway actions;
it does not execute arbitrary project commands inside the credential-bearing
engine process. Engine events report observations, never permission decisions.
This architecture is proposed, not a claim that the current adapter does it.
Initial prompt body is bounded text. Stop means fence admission and request safe
cancellation; only supervisor proof closes the execution. Dialog replies match an
outstanding approved dialog, connection, execution and generation. Unsupported
engine commands/dialogs fail visibly; no pass-through of bash, session switching,
model changes, login, package installation or reload. Additional reviewed mappings
must preserve the same contracts. Display engine text as untrusted data: no terminal
control/clipboard escape execution or web markup privileges.
## 5. Messages, queues, and late results
A message has sender, source scope, recipient agent/scope, routing mode, class,
private body reference, subject references, expiry, reply correlation and trusted
authorization. Sender identity comes from the channel. Record admission before
publishing to the queue. Delivery rechecks current sender delegation, receiver
membership, body visibility, expiry and exact target binding.
- Current-scoped-session is for permitted ordinary notices. Resolve at delivery
within that exact agent/workspace, never a global newest session. Missing or
inactive recipient can remain queued until expiry; it does not auto-launch.
- Exact-session never follows Fresh. Exact-execution requires both session and
execution identity. Results/interactive approvals use exact-execution routing.
- An approval message only references a separately valid decision. Match pending
request ID/digest, intent revision, scope and execution; text saying approved
confers nothing. Late results remain evidence for their original execution.
- Delivered means an attributable receiver acknowledgement, not task success or
acceptance. Lost acknowledgement gives unknown, not safe permission to resend.
- Retries reuse identity/content. Receiver deduplication precedes processing.
Do not claim exactly-once delivery across an unproved crash boundary.
Expiry is explicit and checked against trusted service time and policy bounds;
there is no hidden default TTL or automatic approval on timeout. Current-schema
calendar validation does not establish clock correctness.
## 6. Command limits and concrete evidence
`command-spec` contains reviewed command-profile/parameter-schema identities,
classified parameter/input references and workspace-relative cwd (null explicitly
means workspace root). No raw argv/environment appears in public metadata. The
reviewed profile fixes executable identity and parameter grammar; it is not an
agent-supplied shell wrapper. The parameter-schema reference must equal the
profile's registered schema; callers cannot substitute a weaker schema. Profile/schema bodies are immutable reviewed contract
assets identified by commit/content, not per-workspace copies of authority.
`sandbox-limits` records the selected sandbox profile, workspace grants, permitted
endpoint identities, wall-time/memory/process ceilings, and the explicit absence
of credential/host-control access. Workspace paths supplement the reviewed image,
scratch and system-filesystem rules in the profile; they do not pretend to list
all internal reads. Resolve current policy and take the least-privilege limits.
The profile must actually enforce filesystem and network boundaries, including
symlinks, mounts, /proc access, endpoint redirects/DNS and child processes.
A declaration, Pi tool allowlist or --offline flag alone is insufficient.
Initial file-effect concurrency proposal: a workspace read/write lease. Writers
are exclusive; read-only commands may share a read lease. Hold a write lease until
its cohort ends and effects are accounted for. An uncertain writer blocks affected
access, not unrelated workspaces or control-plane recovery. Finer concurrency is
a later reviewed optimization. Metadata publication uses its separate serialized
revision guard. File put uses verified content/destination and durable atomic
publication; crashes never turn absence of a result into permission to replay.
Evidence bodies now include process observations, termination proof, decision
basis and delegation basis. The publisher verifies their producer and meaning:
| Body | Required additional truth check |
|---|---|
| Process observation | Exact backend object/cohort from admitted launch, pinned image, observation attributable to supervisor; PID/name alone is insufficient |
| Termination proof | Admission is fenced and original cohort/descendants are gone. Never-started proof also establishes no start was admitted and no old producer can start it later. Absence or idle alone supplies neither proof. |
| Decision basis | Subject/criteria/evidence revisions resolve; authors derived from delivery provenance; reviewer is authorized and not an author; request-bound approvals match digest/binding |
| Delegation basis | Issuer/parent authority, target allocations, operations, expiry/use limit and ceilings all hold at use time |
| Command result | Resolve outcomeEventId to the actual command finished/recovered event with matching command/binding. There is no second outcome/signal vocabulary. Classified stdout/stderr/output references and truncation remain distinct from work acceptance. |
Artifact indexes disclose kind, scope, digest, byte length, media type, visibility,
publisher and authorization, not absolute storage paths. Index and referenced
body must agree. Metadata reads do not authorize body reads. Output capture is
bounded/classified by profile; truncation must be explicit in the result evidence,
never silently presented as full output. Credentials are allowed in neither
metadata nor evidence; do not rely on a generic secret-pattern regex as proof.
The trusted journal publishes intent before effects, started after attributable
creation, and outcome only after observed completion. Failure after effects blocks
that execution and preserves unknown state. Recovery first proves stopping and
reconciles actual files/results, then appends a new observation; it never edits an
old receipt or blindly repeats the invocation.
Launch authorization must preauthorize the supervisor's specific fail-safe fencing
and stopping of its own bound cohort when required recording fails. Attempt that
stop even if a new stop event cannot be written; do not start exploratory recovery
commands through an unaudited fallback. Signal the outage without sensitive content
and retain unknown state until recording recovers and stopping/effects are verified.
A later observed-at time records the later observation, not a fabricated stop time.
## 7. Storage, retention, and implementation admission
Use the storage ownership/publication rules in README sections 2 and 6. Mutable
indexes are rebuildable projections, not the authority. The recorded runtime
namespace/epoch and exact scope must survive restart or be declared unavailable.
No metadata search may infer scope from an engine filename or host working path.
Legacy sessions remain separate until reviewed adoption preserves the original.
Adoption rollback before activation discards only unpublished staging and appends
its failure receipt. After activation, safely stop affected work, explicitly
revise/revoke the adoption approval, and preserve all original/new history and
work effects. A now-invalid current mapping causes refusal; it does not silently
select another conversation or become first use. A separately authorized explicit
Resume/Fresh can establish the next valid mapping. Reversing work effects requires
its own reconciliation/assignment, never an automatic historical rewrite.
No new retention duration is implied: retain records and history by default.
Workspace retirement is not deletion. Evidence needed for current state, active
claims, unresolved effects, live references or required recovery cannot be pruned
without satisfying its protection/receipt rules. A pruned reference is explicitly
pruned, not current verifiable evidence. Existing reset/prune scripts have not
acquired these protections; reconcile and test them before enabling this mode.
The first mediated adapter is admitted only after an implementation charter and
proof of: complete mediation; narrow mounts/egress/credential separation; exact
session/config binding; crash-safe writer/idempotency; cohort stopping; revocation
and stale-controller fencing; context/output privacy; protected retention; closure of legacy broad-mount/unmediated entrypoints into
the protected namespace; and #50 account/materialization compatibility. Unsupported capability fails closed.
Pinned Pi documentation is not a passing admission test.
| Capability | Evidence available now | Still required before managed execution |
|---|---|---|
| Native/print launch | Current source inspected; earlier owner launch/resume example | New mediated launch implementation and negative tests |
| Exact selection and RPC events | Pinned 0.84.4 documentation read | Verify exact bootstrap/resume identity and cancelled/error cases |
| Pre-action mediation | Architecture and typed requests only | Reviewed bridge/gateway and bypass/failure tests |
| Sandbox | Pinned Pi documentation says no builtin sandbox; current Compose broad mount inspected | Actual filesystem/network/credential boundary tests |
| Stop/replace/control | Typed records and synthetic traces | Real process-cohort, crash, concurrent-client and revocation tests |
| Audit/publication/recovery | Existing writeOnce source plus candidate rules/models | Durable failure-injection and trusted-producer tests |
| Context/config/privacy | Source risks identified; hashes and candidate classifications | Admission/loading/output tests on actual adapter |
| OAuth refresh | #50 boundary read; gate 7 still open | Separately authorized provider/materializer proof |
These are explicit implementation acceptance gates, not missing permissions that
an agent may improvise around. No runtime feature claim follows from this draft.