Consolidate Workshop Intern against current backend contract - #116
Merged
JoshuaPurtell merged 30 commits intoSep 14, 2026
Conversation
…d build The packaged instance log store reported `binary_missing` because only scripts/build-tier.sh fetched the diagnostics index executable. `npm run build`, desktop-instance CUA bundles and `tauri dev` instances bundled or resolved an unstaged services/victoria-logs directory that held only its .gitignore. The development-checkout fallback could also mask a missing bundled binary on the machine that built the bundle. - tauri.package.json beforeBuildCommand runs package:stage-diagnostics, so every packaged `tauri build` stages the binary before bundling. - desktop-instance.sh stages it for CUA bundles (cua-live-build clears beforeBuildCommand) and, non-fatally, for dev launches. - fetch-victorialogs.sh verifies pinned archive SHA-256s (darwin arm64 and amd64 v1.52.0), refuses a mismatch, supports --if-missing and records the staged version. - A packaged bundle (.../Contents/MacOS/<exe>) resolves only its own Resources, never the compile-time checkout. Tests: packaged and development resolution layouts, non-executable refusal and a packaging-config assertion. The packaged .app itself was not built here (resource limits); the fetch script was run end to end. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DvS6DjNGSe3a3BrHRxhK5K
Qualify the gated scoped-cloud schema, then register it as desktop migration 69. The DDL is idempotent: a lane that collided on version 69 heals every table through heal_missing_tables. CloudStore::open verifies the column shape and refuses a same-named table with another shape. Registration adopts no existing rows. The host runtime stays QualificationRequired until a store is explicitly installed. Mailbox persistence, coded against the manderqueue docs/WORKSHOP_GRANT_CONTRACT.md (sha256 8fc1669a...): - An explicitly selected existing Local session is bound to one thread as the server-derived enrollment principal. Legacy/remote-linked sessions, another account's session, rebinding and silent policy changes refuse. - Server incarnation and grant generation are persisted. A higher generation fences queued writes and open deliveries from the older one; revocation fences everything. - Granted-history pages commit atomically with the cursor. An authorized skip is recorded in cloud_mq_history_gaps; real gaps, foreign orgs and a changed floor refuse. - Delivery ladder: delivered -> observed -> acting -> answered, declined or expired, plus fenced. Native acceptance fences on session, incarnation and grant generation. Handler concurrency, rate, causal depth and deadline are counted from durable rows. - Outbox: original key/correlation/causation/parent are stored immutably. Explicit sign-out and account switch advance a per-account write fence; identity expiry does not. Uncertain sends settle only through matching own history, never by resending. - Grant contract DTO validation, a GrantAuthority trait with a thin HTTP adapter, the granted /history reader and SSE wake reader, and the participant policy/tool gate. Tests: migration clean/existing-v68/failed-then-retried/collided-lane, restart and account isolation, shape refusal, history gaps, native fences, write fences and history reconciliation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DvS6DjNGSe3a3BrHRxhK5K
…ivery
Compose the registered store with fresh identity verification and grant
credentials:
- connect/resume: enroll the device/session (fresh incarnation), bind the
selected session and create or re-read exactly its own grant. Uncertain
or duplicate creates resolve by a GET, never a retried mutation.
- One bounded pass: a validated <=300 s credential (the endpoint must equal
the enrolled one), granted-history catch-up, outbox flush with the
original identities, then history reconciliation of uncertain sends. An
unmatched uncertain send stays explicitly unknown and is never resent.
Contract §7 denials map to revoked/expired/fenced.
- Delivery only at an idle turn boundary. Heartbeats, notices, answers and
status requests are handled with no model call. Work requests reach a
RestrictedExecutor only under the Respond preset, after handler
admission and the session's existing paid-compute authorization. The
executor receives only a ToolGate that refuses out-of-policy
files/artifacts/tools, writes, spend, deploy, invites, access expansion
and spawned work. No production executor is registered, so requests
otherwise wait for an operator answer. Replies are correlated
answer/decline/expiry notices with hop counts.
- A supervisor with SSE wake-then-fetch, backoff and sleep detection. It
stops on explicit sign-out, cancellation or terminal authority loss.
Explicit sign-out now also fences the account's queued MQ writes and
drops cached credentials.
- Eval-driver routes /v1/cloud/mailbox/{activate,connect,resume,pass,
publish,answer,status,disconnect,signout,sleep} (instance builds only).
Tests: in-process backend grant and MQ HTTP fixture journeys covering
binding refusals, no-model handling, restricted-tool refusal, operator and
correlated replies, expiry, handler bounds, uncertain sends, crash
recovery with original IDs, account-switch/sign-out/revocation fencing,
SSE wake and sign-out cancellation of an in-flight request.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DvS6DjNGSe3a3BrHRxhK5K
- Re-vendor the MQ snapshot to claude/workshop-v011-grants-mq 02db5d4. It is the whole-tree `git archive` with per-file SHA-256s, the same method as before; archive sha256 1861aea8..., 73 files; check-mq-vendor.py passes. mq-core/mq-sdk dependencies are unchanged, so Cargo.lock is untouched. - Granted-history catch-up now uses MqClient::read_history and the SDK's HistoryPage/HistorySkip types. It keeps the no-redirect, 30 s deadline and bounded-body protections, plus a page/thread bound check. The SDK still has no SSE reader, so the hardened wake reader stays. - Contract v2 (committed sha256 9a442993...): enrollment `revoked_at`, GrantAuthority revoke_enrollment/get_enrollment, and `enrollment_revoked` mapped to revoked. sign_out_device_with revokes every enrollment of the account (an uncertain revoke is resolved by a GET, never retried), fences queued writes and open deliveries, then signs out locally. The eval-driver `signout` route uses it. - Loopback identity origin: http is accepted only in the backend's exact APP_ENVIRONMENT=local form (127.0.0.1/localhost/[::1], explicit port, no path/query/userinfo), and only when it equals the configured origin. The identity document has no environment field, and the backend emits this form only in local. Tests: device sign-out journey (enrollment and grants revoked, supervisor stopped, queued write fenced, no re-enroll) and local-origin rule cases mirroring the backend's. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DvS6DjNGSe3a3BrHRxhK5K
Accepted Respond-preset work requests can now run through a production RestrictedExecutor that never uses start_turn_inner or the session's normal Codex authority. Each request gets its own ephemeral directory and app-server process. Nothing survives the request. Enforcement, verified against the local Codex 0.145 (app-server protocol v2 schema, --strict-config, `features list`, a model-free confined boot): - A macOS seatbelt (sandbox-exec) around the whole app-server tree. File data and listings under /Users and /Volumes are unreadable except the Codex install and the ephemeral dir. Writes go only to the ephemeral dir. Outbound network reaches only the loopback provider port. Codex compiles in a view_image tool with no config switch, so only OS confinement can bound its reads. - A zero-tool Codex: shell, unified exec, apps, plugins, browser and computer use, image generation, hooks, sub-agents and related features are disabled. Web search is disabled and no MCP servers are configured. approval_policy is "never" with the read-only sandbox, and turn/start pins sandboxPolicy readOnly with networkAccess false. - Every approval, tool or elicitation request from the app-server is declined and recorded as a refused gate decision. - Only gate-authorized files are copied into the ephemeral workspace, SHA-256 hashed and given to the model as quoted untrusted data. The reply carries the input and answer hashes. - Requests it cannot enforce are declined before running, and before any handler budget is consumed (RestrictedExecutor::can_enforce): artifacts, tools other than read_allowed_file, non-loopback providers and non-macOS hosts. Deadline, concurrency and rate bounds come from the handler limits. A loopback provider bills nothing. Production wiring: host::confined_executor exists only with a native Codex binary, macOS seatbelt, the local Laguna provider on loopback and a passing confined boot check. The eval-driver `pass` route attaches it. Tests (model-free): a scripted app-server inside the real seatbelt proves that out-of-allowlist reads, listings and writes are denied, that only the provider port connects (other loopback ports and 1.1.1.1 are denied), that its command-approval request is declined, that the host environment is not inherited and that the allowed file arrives hashed. The real codex binary boots confined with the generated config and reports the restricted effective configuration. Config/profile content, the refusal of unenforceable policies and a host journey declining an unenforceable request without running it are also covered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DvS6DjNGSe3a3BrHRxhK5K
Five renderer commands over the native mailbox: - cloud_mailbox_connections - cloud_mailbox_status (connection/grant state, inbox with requests that wait for an operator, outbox with unresolved unknown outcomes, gaps) - cloud_mailbox_answer - cloud_mailbox_decline - cloud_mailbox_sign_out (device sign-out via enrollment revoke, then the local sign-out; the local sign-out happens even when the backend cannot be reached) Each command checks the host scope first and refuses while it is QualificationRequired, before reading any config, credential or network. The views are specta-typed with JS-safe integers and never carry a credential or grant token. protocol.ts is regenerated and the lockstep count moves from 332 to 337. CloudMailboxPanel, mounted in BackendSettings, renders nothing while the host is gated. Otherwise it shows the connection and grant state, answer/decline for requests awaiting an operator, the outbox with an explicit "outcome unknown - not resent" state, and device sign-out. Tests: one Rust test per command against the in-process fixture journeys, plus a gated-host test that all five refuse. The specta lockstep test passes. Frontend typecheck (tsc --noEmit) reports 0 errors; the worktree borrows the same-base rc1 install through gitignored symlinks. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DvS6DjNGSe3a3BrHRxhK5K
… ready Item 4 of the follow-up asks for a packaged `.app` check. It was not feasible within the disk/resource limits, for the reasons in the report. This tests the resolution fix directly instead. With the pinned VictoriaLogs binary staged by fetch-victorialogs.sh (the new packaging hook), an unpackaged build resolves the checkout binary. The real sidecar then starts on loopback and reports `ready` with a URL, not `binary_missing`, and stops cleanly. The test skips when nothing is staged. A shared lock now serializes the tests that set the process-wide SYNTH_VICTORIALOGS_BIN override, so they cannot race. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DvS6DjNGSe3a3BrHRxhK5K
JoshuaPurtell
merged commit Sep 14, 2026
d08a6c4
into
claude/workshop-v011-rc1-20260911
1 check passed
JoshuaPurtell
deleted the
codex/workshop-intern-contract-consolidated-20260914
branch
September 14, 2026 07:06
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Validation
Branch topology
The default public main branch is a deliberately reduced public export. A raw merge into the internal desktop line causes structural delete/rename conflicts and is not the release integration path. This PR targets the existing internal v0.11 RC lineage.