itx: contexts, capabilities, and the one true handle#1407
Merged
Conversation
# Conflicts: # apps/os/src/capnweb/e2e/captnweb-scripts.ts # apps/os/src/capnweb/e2e/captnweb-slack-sdk.e2e.test.ts # apps/os/src/capnweb/e2e/captnweb.e2e.test.ts # apps/os/src/capnweb/node-client.ts # apps/os/src/capnweb/project-capability.ts # apps/os/src/capnweb/projects-capability.ts # apps/os/src/capnweb/root-context-fetch.ts # apps/os/src/entry.workerd.ts
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nt, e2e The first cut of apps/os/docs/itx-spec.md phases 1-3 in one motion: - ContextRegistry embedded in the Project DO (live + worker caps, SQLite state, stream audit events, supervisor dispatch) - Itx handle with typed built-ins + registry fallthrough proxy - ItxEntrypoint (props = sturdy ref) and ProjectEgress (globalOutbound) - /api/itx connect + run endpoints, connectItx Node client - e2e suite incl. the five-step live->durable capability flow Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-step config load - Project DO loses getCapability/getIterateContext/provideCapability/ getConnection and the per-call IterateContextProps machinery; the config worker now gets a project-scoped ItxEntrypoint as env.ITERATE and ProjectEgress as globalOutbound (it previously had NO network access) - /api/captnweb and /__iterate/capnweb are gone; /__itx terminates in the stateless worker per Law 7 - Browser REPL rewired to /api/itx with itx in scope (routes, component, TS autocomplete worker) - oRPC org flows move to domains/projects/project-directory.ts - egress e2e proves both doors (itx.fetch + globalOutbound) with real secret substitution Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
itx.fork() creates a ctx_… context under any project context: own registry + SQLite, parent pointer, audit on the project /itx stream. Cap misses delegate up the chain; shadowing is visible via describe() owner provenance. Connectable by id at /api/itx/ctx_… (access = the owning project's). E2e covers shadow/sibling/reconnect and child worker caps writing through their own project-scoped itx. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- facet-kind caps: stored source exporting a NAMED DurableObject class,
instantiated via ctx.facets as a child of the hosting context node
with its own private SQLite database (named exports required — D12)
- {cap}--{project}.{base} hostnames route to a cap's fetch surface via
ItxCapIngress: 404 unless meta.http.expose, admin-gated by default,
meta.http.public opt-in, or HMAC-signed expiring share URLs
(itx.caps.shareUrl — 'let me show you something real quick')
- supervisor-side invoke failure logging (D14)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A Chromium tab holds an itx over Cap'n Web: runs the shared scripts, the REPL snippets, and PROVIDES live capabilities (members + path-call) back to the project. Skips on plain-http targets where the SameSite cookie physically cannot cross origins; all four tests pass against the https preview deployment. Also: D15 documents that the agents slack-codemode e2e timeout reproduces identically on origin/main (pre-existing, not this branch). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Security (review round 1): global context & global /api/itx/run are admin-only (closes ambient egress for non-admins); replayPathCall filters reserved/prototype segments server-side (itxInvoke is a public method); one shared RESERVED_PATH_SEGMENTS set feeds proxy + replay + cap-name validation; path proxy never falls through to Function.prototype; worker/ facet entrypoint stubs disposed after each invoke; cap HTTP routing matches case-insensitively. Auto-proxying (owner request): itx.project returns the full Project DO stub so any public method/getter is instantly callable (D17); itx.worker replays the full path against the config worker entrypoint (any depth), matching how worker/facet caps already proxy via members mode. New e2e: deep-path member proxying (getter→nested RpcTarget→method) and worker→worker proxying (one dynamic worker cap calling another's surface through env.ITERATE.context). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…xy dispose
- itx.workspace returns the WorkspaceCapability entrypoint directly (like
itx.repos) — deletes ItxWorkspace/ItxWorkspaceGit/WorkspaceClient (~70
lines) since the domain entrypoint already exposes readFile/writeFile and
git.{add,clone,commit,push,status}
- PathProxyRpcTarget loses its unused dispose option/plumbing (no caller
ever passed one)
- inline the single-use RESERVED_PROTOCOL_NAMES intermediate
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t client - 12 documented REPL examples (browser-repl.ts), each mirroring an e2e scenario: list/describe, streams, live cap, path-call SDK, durable worker cap, cap-with-own-itx, deep auto-proxy, worker->worker, stateful facet, fork, egress with secret substitution, HTTP cap + share URL. Each has a title/description and heavily-commented code; a unit test compiles them all. - Fix a REPL compiler bug surfaced by those examples: a snippet ending in a line comment swallowed the appended '; return' -> 'Unexpected end of input'. Newlines around the user source now close the comment. +regression. - fork() narrows child access to [projectId] (was inheriting parent's, e.g. 'all') so a forked session cannot reach sibling projects via itx.projects -- addresses both Cursor Bugbot findings. +e2e proving confinement. - connectItx gains a 15s WebSocket handshake timeout; e2e timeouts cut 120s->45s so a dead server fails fast instead of hanging for minutes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # apps/os/src/capnweb/iterate-context-capability.ts # apps/os/src/domains/projects/project-directory.ts # apps/os/src/orpc/project-access.ts # apps/os/src/orpc/routers/projects.ts
- ContextDO.initialize only emits the contextForked audit when a row was
actually inserted (ON CONFLICT DO NOTHING re-init no longer double-audits)
- cap-host parser requires exactly {cap}--{project}; the reserved
{cap}--{ctxId}--{project} child form is unbuilt and now fails closed (404)
instead of mis-parsing ctxId--project as the project id
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 05e0f9e. Configure here.
invoke() now works on a disposable BORROW for all three kinds — a .dup() of the stored stub for live caps, the per-call entrypoint/facet stub otherwise — disposed in finally. The registered live target is never shared across calls or closed by a borrow's disposal, restoring the original capnweb dup-on-borrow discipline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s a no-op) The /itx-repl page connects to bare /api/itx (global context). The round-1 hardening made the global CONNECT handle admin-only, so a normal logged-in user got 403 → the browser WebSocket never opened → itx stayed null → the Run button was disabled / did nothing. A global handle is safe for any authenticated principal: its access is exactly what they can reach (admins 'all', users their project ids) and every built-in narrows through that access check. The real risk — a global /api/itx/run inheriting the platform's own egress — stays admin-gated in handleItxRun. Only the connect-handle over-restriction is lifted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The default connectSession was an inline arrow, so it got a fresh identity
every render; the connect effect depends on it, so the Cap'n Web session was
torn down and rebuilt on EVERY render — including the re-render from clicking
Run — disposing the itx stub mid-call ('RPC stub used after disposed'). Use a
stable function reference (createBrowserReplSession with no arg = global;
project repls already pass a memoized factory). Also fix stale ctx→itx help text.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jonastemplestein
added a commit
that referenced
this pull request
Jun 10, 2026
main's itx work (#1407) landed importing the StreamProcessorRunner type this branch deletes; the import was unused. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jonastemplestein
added a commit
that referenced
this pull request
Jun 10, 2026
…scriptions, legacy model deleted (#1402) ## Summary Complete rewrite of the original spike: every stream processor apps/os hosts now runs on the class-based `StreamProcessor` model (#1401), owned directly by its domain Durable Object, and the Stream DO reaches subscribers through the `packages/shared` Callable abstraction. All legacy processor-model code is deleted (−17k lines net). Rebased onto main including the itx capabilities work (#1407). Full design rationale and issue log: `apps/os/tasks/stream-processor-class-migration-log.md` (D1–D12, I1–I6 + deletion inventory), plus per-domain notes under `apps/os/tasks/migration-notes/`. ### Hosting model A Durable Object hosts processors as plain class fields: ```ts export class AgentDurableObject extends DurableObject<Env> { host = createStreamProcessorHost(this.ctx); agent = this.host.add("agent", (deps) => new AgentProcessor({ ...deps, ... })); chat = this.host.add("agent-chat", (deps) => new AgentChatProcessor(deps)); async requestStreamSubscription(args: RequestStreamSubscriptionArgs) { await this.ensureStartedOrInitializeFromRuntimeName(); return await this.host.requestStreamSubscription(args); } } ``` `createStreamProcessorHost` provides checkpoint storage in DO KV (keyed by processor name), a late-bound stream context, per-subscription side-effect anchoring, and `processor-registered` announcements. Any number of named processors per DO. ### Subscriber delivery via Callable `stream/subscription-configured` payloads carry `{ type: "callable", callable: Callable }`; the Stream DO dispatches the callable with the subscription handshake (`dispatchCallable` — same Workers RPC transport, so the live stream stub passes through). The hardcoded `STREAM_PROCESSOR_RUNNER` dialing is gone; `packages/streams` now depends on `packages/shared`. Legacy `built-in` subscriber shapes reduce harmlessly but are no longer dialed; OS re-appends callable subscriptions through the existing ensure-on-access paths with new idempotency keys. No subscriber authorization yet (explicitly out of scope). ### Contract-driven event filtering `subscribe`/`subscribeOutbound` accept `eventTypes`; the pump filters post-read while its cursor advances past non-matching events. Hosts always pass `contract.consumes` — the contract is the filter (`"*"` = unfiltered). Catch-up helpers wait on consumed-event targets instead of the raw stream head. ### Side-effect anchor `StreamProcessor` gains `sideEffectsAfterOffset`: events at or below the anchor (persisted at first subscription handshake) reduce into state but skip side effects — attaching to an existing stream rebuilds state without re-firing historical effects (e.g. old LLM requests). Replaces the legacy dual-cursor + first-attach lookback machinery. ### Migrated processors (all wire-format-identical) | Host DO | Processors | |---|---| | `AgentDurableObject` | agent, agent-chat, openai-ws, cloudflare-ai, jsonata-reactor, agent-host | | `ProjectDurableObject` | project-lifecycle | | `RepoDurableObject` | repo | | `SlackIntegrationDurableObject` / `SlackAgentDurableObject` | slack / slack-agent | | `CodemodeSession` | codemode | | streams staging runner | echo-example, circuit-breaker | openai-ws keeps its socket as processor instance state (the DO is the connection scope). `scheduling`, `jsonata-transformer`, `dynamic-worker` had no live subscription path and were deleted, not migrated. ### LLM requests are background work (D12) The LLM providers do NOT hold the processor's batch queue while a request is in flight. Executing under `blockProcessorWhile` would mean a cancellation or superseding event physically cannot be reduced until the request it should affect has finished — defeating the staleness check (`isAgentLlmRequestStillCurrent`) both providers run before appending agent-visible output. Instead: - `agent/llm-request-requested` executes via `runInBackground` (keep-alive-backed through the host's `ctx.waitUntil`), so subsequent events keep flowing while requests run; stale requests complete silently. - Crash recovery no longer rides on checkpoint-held redelivery (the checkpoint now passes the request immediately). Each provider tracks in-flight request ids in instance state; a `started` entry in reduced state with no in-flight execution marks a request a previous incarnation abandoned, and the next delivered batch re-executes it from stream history — still guarded by the staleness check. ### Deleted The legacy OS `StreamProcessorRunner` DO (+ binding; alchemy computes `deleted_classes` automatically), `packages/shared/src/stream-processors/**` (~47 files), the shared DO mixins, and the legacy runner model in `packages/streams` (`processor.ts`, `processor-runner.ts`, `standard-processor-behavior.ts`, ~14 legacy-only helpers). ~35 importers repointed. ### Issues found and fixed during validation & review - **I5** — the agent wake hook awaited processor catch-up inside `blockConcurrencyWhile`; with processors co-hosted on the same DO this deadlocks against the input gate (the handshake/delivery it waits for queues behind it). Rule recorded in the log: never await processor catch-up inside a lifecycle gate on a co-hosting DO. - **I6** — the long-flaky streams-e2e tail spec, root-caused from a CI trace: TanStack Virtual's end target sits ~12px short of the true bottom on Linux font metrics (≤2px on macOS), so the tail-pin's scroll-delta "user left" heuristic fired on the virtualizer's own reconcile writes. The pin now releases only on real user-input signals; CI uploads playwright traces on failure. - Stream child-path announcements and dials to uninitialized codemode sessions on routed streams (found via live-preview e2e). - Review (Bugbot): Cloudflare AI now retries requests a crashed incarnation left in `started` (skip only `completed`), with regression tests; cold `AgentDurableObject` instances initialize their lifecycle before accepting subscription handshakes; `agent-host` wakes its agent once per incarnation on any delivered event instead of an anchor-skipped historical `stream/created`. ## Validation - `apps/os`: typecheck 0 errors, 167 unit tests; workerd suites `test:project-ingress` 6/6 and `test:codemode-session` 18/18 (cover the callable handshake, routed streams, and itx capabilities end-to-end) - `packages/streams` (58), `packages/shared` (64), `packages/ui`, example-app: typecheck + tests green - `pnpm lint` / `pnpm format` clean - **Live preview acceptance** (`os.iterate-preview-4.com`): full OS e2e suite against the deployment — 27 passed / 1 todo / 0 failures, including real OpenAI conversations on freshly created projects, Cloudflare AI Gateway, codemode script execution, and routed Slack webhook → bang-command replies against the real Slack API 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CLOUDFLARE_PREVIEW --> ## Environment Config Lease <!-- CLOUDFLARE_PREVIEW_STATE --> <!-- { "apps": { "os": { "appDisplayName": "OS", "appSlug": "os", "status": "awaiting-tests", "updatedAt": "2026-06-10T05:56:44.340Z", "headSha": "4d30dbdc5812efb7f0a446efa1b3bf7dd45bee1f", "publicUrl": "https://os.iterate-preview-2.com", "runUrl": "https://github.com/iterate/iterate/actions/runs/27256373612", "shortSha": "4d30dbd" }, "semaphore": { "appDisplayName": "Semaphore", "appSlug": "semaphore", "status": "awaiting-tests", "updatedAt": "2026-06-10T05:56:44.300Z", "headSha": "4d30dbdc5812efb7f0a446efa1b3bf7dd45bee1f", "publicUrl": "https://semaphore.iterate-preview-2.com", "runUrl": "https://github.com/iterate/iterate/actions/runs/27256373612", "shortSha": "4d30dbd" } }, "environmentConfigLease": { "dopplerConfig": "preview_2", "leasedUntil": 1781074601972, "leaseId": "50d5671f-2310-495f-b6cd-9ed9239b9028", "slug": "preview-2", "type": "environment-config-lease" } } --> <!-- /CLOUDFLARE_PREVIEW_STATE --> Lease: `preview-2` Doppler config: `preview_2` Type: `environment-config-lease` Leased until: 2026-06-10T06:56:41.972Z ### OS Status: awaiting tests Commit: `4d30dbd` Preview: https://os.iterate-preview-2.com [Workflow run](https://github.com/iterate/iterate/actions/runs/27256373612) Updated: 2026-06-10T05:56:44.340Z ### Semaphore Status: awaiting tests Commit: `4d30dbd` Preview: https://semaphore.iterate-preview-2.com [Workflow run](https://github.com/iterate/iterate/actions/runs/27256373612) Updated: 2026-06-10T05:56:44.300Z <!-- /CLOUDFLARE_PREVIEW --> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
jonastemplestein
added a commit
that referenced
this pull request
Jun 10, 2026
…rk (#1411) # Golden-path `apps/os`: TanStack Start + oRPC + Cloudflare Workers Relentlessly simplifies `apps/os` onto the golden path, fixes the phantom `/projects/new` route the sidebar linked to, and — found during review — **closes a live production secret leak**. Full decision log: `apps/os/docs/simplification-decisions.md`. --- ## 1. The bug that started this: a sidebar link to a 404 The sidebar linked to `/projects/new`, which 404'd. The route file was: ```tsx // routes/_app/projects/[_]new.tsx (the [_] escapes a leading underscore) export const Route = createFileRoute("/_app/projects/_new")({ ... }); ``` In `routeTree.gen.ts` this produced a route with **`path: ''`** — a pathless route that the type system still advertised as the linkable target `/projects/new`, so `<Link to="/projects/new">` typechecked but **never matched at runtime**. It hit an upstream router-generator bug with escaped underscores under a pathless layout (TanStack/router#7408, #7453), and the version we were pinned to (`@tanstack/react-start@1.167.5` → bundled `router-generator@1.166.17`) predated the fix. **Why it was bad:** typed-but-unmatchable routes are invisible to CI — `tsc` passes against a lie. The only way to catch it is to keep the generated route tree honest. **Fix:** - Renamed to a top-level `routes/_app/new-project.tsx` → **`/new-project`** (also can't ever collide with a `$projectSlug`). - Bumped the TanStack pair to the version with the generator fix (`react-start 1.168.25` / `react-router 1.170.15`, bumped together because `react-start` hard-pins `react-router`). - Added `scripts/generate-route-tree.ts` + `routes:check`, wired into `typecheck`, so **CI now fails on a stale/phantom route tree**: ```ts // regenerates routeTree.gen.ts with the SAME generator + config the vite build // uses; --check restores the file and exits 1 if it drifted. const before = readFileSync(routeTreePath, "utf8"); try { await new Generator({ config, root }).run(); after = readFileSync(routeTreePath, "utf8"); } finally { if (checkOnly) writeFileSync(routeTreePath, before); } if (before !== after && checkOnly) { console.error("routeTree.gen.ts is stale…"); process.exit(1); } ``` --- ## 2. Removed the "apps framework" over-abstraction `apps/os` predated the decision to make OS the only product app. It still paid for generality it no longer needs. ### 2a. `app.ts` (manifest + config) → `config.ts`; manifest deleted A manifest object existed only to parameterize shared helpers by app identity. With one product app, every consumer can just say `"os"`: ```ts // before — alchemy.run.ts const ctx = await initAlchemy(manifest, AppConfig, env); // after const ctx = await initAlchemy("os", AppConfig, env); ``` ```ts // before — withEvlog took the whole AppManifest withEvlog({ request, manifest, config, executionCtx }, …) // after — a plain { name, slug }; log field names kept stable for dashboards withEvlog({ request, app: { name: "@iterate-com/os", slug: "os" }, config, executionCtx: ctx }, …) ``` Config utilities (`redacted`/`publicValue`/`parseAppConfigFromEnv`/ `extractPublicConfigSchema`) and request logging moved out of the `apps/*` namespace to `@iterate-com/shared/config` and `@iterate-com/shared/evlog`. The old `@iterate-com/shared/apps/*` paths are now one-line re-export shims, so **apps/semaphore needs zero changes in this PR**. ### 2b. `AppContext` (hand-threaded) → `RequestContext` (TanStack Start's own) The old `AppContext` carried **every worker binding** as an optional field, threaded around next to — not through — TanStack Start's request context. That forced a defensive guard at every use site for bindings that are *always* bound: ```ts // before — in ~6 routers/capabilities if (!context.stream) { throw new ORPCError("INTERNAL_SERVER_ERROR", { message: "STREAM … not configured." }); } const ns = context.stream as unknown as StreamDurableObjectNamespace; ``` **Why it was bad:** a dozen impossible-error branches, an `AppContext` type listing 14 optional bindings, and two parallel notions of "context". The golden-path answer is to read bindings where you use them: ```ts // after — env is the documented module-level binding accessor import { env } from "cloudflare:workers"; const ns = env.STREAM as unknown as StreamDurableObjectNamespace; ``` `RequestContext` (`src/request-context.ts`) is now *just* request-scoped state (config, db, log, auth principal/session, `waitUntil`, `ctx.exports`, project scope) **and is the actual TanStack Start request context** — the `Register` augmentation lives next to the type. `ctx.exports` is the one binding kept on the context, because Cloudflare only exposes it on `ExecutionContext`, not as a module import. Net effect on the routers: agents `+10/−44`, projects `+22/−52`, codemode `+3/−21`, streams `+5/−14` — they all got smaller. ### 2c. `entry.workerd.ts` (604 lines) → `worker.ts` (197 lines) + focused modules The entrypoint was a 600-line grab-bag. It's now a short, linear dispatcher a Cloudflare engineer would recognize at a glance — infra routes → evlog → project ingress → stream RPC / capnweb → TanStack Start handler: ```ts export default { async fetch(request, env, ctx) { const config = parseConfig(env); // per-request, not module scope — see §3 note const early = (await handleCaptunTunnelFetch(request, env, config)) ?? (await handleDebugRoutes({ request, env, config })); if (early) return early; return withEvlog({ request, app: { name: "@iterate-com/os", slug: "os" }, config, executionCtx: ctx }, async ({ log }) => { // … ingress → stream RPC → capnweb → handler.fetch(request, { context }) … }); }, }; ``` The debug endpoints, project-stream RPC, and ingress lookup moved into `src/debug-routes.ts`, `src/domains/streams/project-stream-rpc.ts`, and `src/ingress/lookup.ts`. `IterateApp` gained a `main` option (default unchanged, so semaphore is untouched). The `__internal` oRPC namespace and the OpenAPI reference plugin are now declared inline in `orpc/root.ts` / `orpc/handler.ts` instead of via a shared factory. --- ## 3. 🔒 Security: closed an unauthenticated secret leak (rotate secrets) Review turned up that **`GET /api/__internal/debug` was unauthenticated and returned `process.env`** — which, under `nodejs_compat` (always on for our workers), contains the raw `APP_CONFIG` secret blob. **Confirmed live on `os.iterate.com` and `semaphore.iterate.com`.** ```ts // before — packages/shared/src/apps/internal-router.ts export function createInternalDebugOutput() { if (typeof process === "undefined") return { runtime: "workerd" }; return { runtime: "node", pid: process.pid, …, env: Object.fromEntries(Object.entries(process.env)…), // ← the whole secret blob, to anyone memoryUsage: process.memoryUsage() }; } // after export function createInternalDebugOutput() { // SECURITY: this route is UNAUTHENTICATED. Never return secrets here. return { runtime: "workerd" as const }; } ``` Gutted at the shared source so semaphore (still on the shared router) is fixed too; OS's inline `__internal` router does the same. **Access check (Cloudflare Workers Observability, queried 2026-06-10):** a path filter `url.path eq /api/__internal/debug` returned **0 events**; the only `__internal` traffic was `trpc-cli-procedures` from our own `node` CLI. That's reassuring but **not proof** — effective visibility for rare requests was only ~the last 24–35h (far shorter than the exposure window) and the dataset is ABR-sampled. **Action required: rotate the Cloudflare API token, OpenAI/xAI/ Gemini keys, admin API secret, and Slack/Google OAuth secrets on both apps, then redeploy.** > Note (also from review): config is now parsed **per request** in `worker.ts`, > not at module scope. Module-scope derivatives of secrets can go stale across > binding-only deploys (Cloudflare reuses isolates); a zod parse per request is > trivially cheap and always honors the current secret. --- ## 4. Review & verification - **Two adversarial reviews** against re-read first-party docs (Cloudflare, TanStack Start/Router/Query, oRPC). They caught, among other things, a **build blocker** — `@tanstack/devtools-vite@0.7.0`'s `removeDevtools` transform rewrites a parenthesized JSX `return` into `return ( );` (a syntax error), which only `vite build` (deploy/preview) surfaces, not PR CI. Pinned back to `0.6.0`. - Monorepo `typecheck` / `lint` (0 warnings) / `format` / tests / a real `vite build` all pass; **Cursor Bugbot clean**; Preview deploy + e2e green. - **Headless preview smoke test** (`apps/os/docs/preview-agent-browser-smoke.md`): superadmin sign-in → create a project via `/new-project` → **a real agent conversation** (typed a question in the browser, the agent DO + LLM replied). Two honest caveats are documented, not hidden: a transient `Project not found` I chased was an **expired short-lived OS session JWT**, not a bug; and live stream display needs a WebSocket that 500s on preview hosts (the conversation completes server-side; the WS-upgrade code is byte-identical to main → flagged as preview-infra follow-up, not claimed fixed). --- ## 5. "Wait, a simplification PR that's net +~930 lines?" Correct, and worth unpacking — because **~74% of the net isn't application code at all**, and the code that *is* application logic mostly shrank. `git diff origin/main…HEAD`: **91 files, +3709 / −2777 = net +932.** By area: | Area | net | what it is | |---|---:|---| | `pnpm-lock.yaml` | **+423** | dependency-graph churn from the TanStack version bump. Zero hand-written lines. | | `docs/` | **+267** | the decisions log + headless smoke-test guide you asked for. Prose, not code. | | `apps/os/src` | +123 | see below — dominated by *new* files that replace deleted ones | | `scripts/` | +61 | the new `generate-route-tree.ts` freshness check (pure addition, didn't exist) | | `packages/shared` | +57 | back-compat shims so semaphore is untouched + the moved modules | | generated `routeTree.gen.ts` | 0 | | | tests / other | +1 | | `pnpm-lock.yaml` + `docs` alone are **+690 of the +932**. Strip those and the real source delta is **~+240**, almost all of it *deliberate new infrastructure*, not retained complexity: - `generate-route-tree.ts` (+61) — new CI guard against the class of bug that started this PR. - `request-context.ts` (+96) + `router-context.ts` (+18) — replace the deleted `context.ts` (−51) with a smaller, *correct* request context plus the typed accessors that work around an upstream `getGlobalStartContext` type bug. - back-compat shims in `packages/shared/src/apps/*` — a few lines each so **semaphore changes by zero lines** instead of forcing a parallel refactor into this PR. The actual product logic got **smaller and flatter**: - `entry.workerd.ts` (−604) → `worker.ts` (+197) plus three focused extracted modules. The headline file shrank ~3×. - the oRPC routers lost net ~120 lines of impossible-error binding guards. - `context.ts` (−51) and the 14-field `AppContext` are gone. The line counters also **double-count pure moves**: `config.ts` shows as `+501` (new path) and `−501` (old path → 4-line shim) — net ~0 churn that inflates *both* columns. Same for the evlog modules. Git doesn't detect these as renames because the old path still exists as a shim. So: net +~930, but it's lockfile (+423) + requested docs (+267) + new guard rails and a security fix (+~240), against genuinely *less* and simpler runtime code. If you want the "pure simplification" number, it's the `apps/os/src` runtime logic, which is net-negative once you exclude the new context/worker scaffolding that replaced larger deleted files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- ## 6. Update: merged `main` (itx) + semaphore migration Since this branch opened, `main` landed **#1407 (itx)** which rewrote the worker entrypoint and replaced `src/capnweb/` with `src/itx/`. Rather than rebase through it, I **merged `main`** and reconciled: - `worker.ts` now wires the itx handlers (`handleItxFetch`, `handleProjectHostItxFetch`, `getItxCapHostIngressRule`, itx entrypoint exports) instead of the removed capnweb ones — keeping the clean split (`debug-routes.ts`, `project-stream-rpc.ts`, `ingress/lookup.ts`). - The whole `src/itx/` subsystem was migrated onto the new `config.ts` / `request-context.ts` (it was built on the now-deleted `app.ts`/`context.ts`). - Honored main's new **`no-raw-durable-object-binding-access`** guardrail: ingress code (projects router, integration-api) mints Project/Slack DO stubs through helpers in the trusted `*-durable-object.ts` domain files (`getProjectDurableObjectStub`, `getSlackIntegrationStub`) rather than raw `env.X.getByName`. Updated the rule's allowlist for the `worker.ts` rename. **Also migrated `apps/semaphore`** off the same `@iterate-com/shared/apps/*` framework (its own `app.ts`→`config.ts`, `context.ts`→`request-context.ts`, `entry.workerd.ts`→`worker.ts`, inline `__internal`), which let me **delete the shared modules entirely** (the `apps/config`, `apps/logging/*`, `apps/internal-router`, `apps/orpc` shims) instead of leaving them as back-compat shims. Monorepo typecheck, lint (0 warnings), and tests are green. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **High Risk** > Removes the shared apps layer and rewrites the worker entry, request context, and oRPC wiring across most of OS; also fixes a live unauthenticated debug endpoint that exposed secrets—rotate affected credentials after deploy. > > **Overview** > Refactors **`apps/os`** off the shared “apps framework” onto a single-app golden path: **`app.ts` → `config.ts`**, **`AppContext` → `RequestContext`** (TanStack Start’s registered request context), and **`entry.workerd.ts` → `worker.ts`** with logic split into **`debug-routes.ts`**, **`ingress/lookup.ts`**, and **`project-stream-rpc.ts`**. oRPC routers and integrations now read **Cloudflare bindings via `import { env } from "cloudflare:workers"`** instead of optional fields on context, with trusted **DO stub helpers** for lint compliance. > > Fixes the phantom **`/projects/new`** route by moving project creation to **`/new-project`**, bumps the TanStack pair, and adds **`routes:check`** / **`generate-route-tree.ts`** so CI fails on a stale **`routeTree.gen.ts`**. > > **Security:** stops **`GET /api/__internal/debug`** from exposing secrets — OS inlines a safe **`__internal`** router (static `{ runtime: "workerd" }` on debug) and hardens the shared debug helper so other apps don’t leak **`process.env`**. > > Docs add the simplification decision log and expanded headless preview smoke procedures; alchemy deploy points **`IterateApp`** at **`./src/worker.ts`** and passes **`"os"`** to **`initAlchemy`** instead of a manifest. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ecbd6d7. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- CLOUDFLARE_PREVIEW --> ## Environment Config Lease <!-- CLOUDFLARE_PREVIEW_STATE --> <!-- { "apps": { "os": { "appDisplayName": "OS", "appSlug": "os", "status": "deployed", "updatedAt": "2026-06-10T07:35:52.639Z", "headSha": "ecbd6d7c21ea49d04a88d0962f6e7c38330b08ab", "message": null, "publicUrl": "https://os.iterate-preview-2.com", "runUrl": "https://github.com/iterate/iterate/actions/runs/27260626648", "shortSha": "ecbd6d7" }, "semaphore": { "appDisplayName": "Semaphore", "appSlug": "semaphore", "status": "deployed", "updatedAt": "2026-06-10T07:35:43.130Z", "headSha": "ecbd6d7c21ea49d04a88d0962f6e7c38330b08ab", "message": null, "publicUrl": "https://semaphore.iterate-preview-2.com", "runUrl": "https://github.com/iterate/iterate/actions/runs/27260626648", "shortSha": "ecbd6d7" } }, "environmentConfigLease": { "dopplerConfig": "preview_2", "leasedUntil": 1781080419272, "leaseId": "4779be3e-b0a6-4e72-851c-1efa4aae3514", "slug": "preview-2", "type": "environment-config-lease" } } --> <!-- /CLOUDFLARE_PREVIEW_STATE --> Lease: `preview-2` Doppler config: `preview_2` Type: `environment-config-lease` Leased until: 2026-06-10T08:33:39.272Z ### OS Status: deployed Commit: `ecbd6d7` Preview: https://os.iterate-preview-2.com [Workflow run](https://github.com/iterate/iterate/actions/runs/27260626648) Updated: 2026-06-10T07:35:52.639Z ### Semaphore Status: deployed Commit: `ecbd6d7` Preview: https://semaphore.iterate-preview-2.com [Workflow run](https://github.com/iterate/iterate/actions/runs/27260626648) Updated: 2026-06-10T07:35:43.130Z <!-- /CLOUDFLARE_PREVIEW --> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jun 10, 2026
jonastemplestein
added a commit
that referenced
this pull request
Jun 10, 2026
## What Two design artifacts from the post-#1407 itx review sessions — docs/types only, **no runtime changes**. ### `apps/os/docs/itx-next.md` — the working roadmap The running list of what we want to fix or build in the itx layer, with positions, a decisions log, and open questions. Highlights: - **CapTarget**: a capability is a name + a target; the target is one discriminated union of three kinds — `live` (inbound: a connected provider's stub, the one non-serializable kind), `rpc` (outbound: an RPC target in some worker, with `WorkerRef` naming where that worker lives — binding / loopback / project-worker / durable-object / stored source), and `url` (a Cap'n Web server across the internet, headers pass through egress secret substitution). Inbound/outbound is derived, never spelled. - **MCP/OpenAPI are not transports** — they're client implementations, i.e. ordinary RPC targets. Litmus test: the first-party `McpClient` (loopback) and a user-space `OpenApiClient` (exported from your own project worker) must be the same shape. - **One `caps.define` verb**; `kind: "facet"` dies (statefulness is `source.exportType`). - **Platform bindings as caps**: thin policy wrappers (gateway selection, attribution) via loopback entrypoints — the ProjectEgress pattern applied to bindings. - **A real global context** + the ref-addressing model (refs are unauthenticated sturdy refs; resolution checks the principal; absolute forms are sugar over narrowing). - **Codemode drop plan**: a session = a forked child context; tool providers = caps; execution = two events (`itx/execution-requested|completed`), record-only first. - Streams convergence direction, sturdy-refs receipts (Cap'n Proto `persistent.capnp`), resolved-decisions log, open questions. ### `apps/os/src/itx/types.ts` — the design of record Handwritten, import-free. Narrative header (the three concepts + a thirty-second worked example), then `Itx`/`ItxBuiltins`, the `KnownCaps` declaration-merge point, `CapTarget`/`WorkerRef`, `CapSource` (with `cacheKey`, formerly `codeId`), `ItxPrincipal` hand-mirrored from `~/auth/principal.ts` (no invented access model), `StreamRef` addressing forms, and `CapMeta` as arbitrary metadata with the `instructions` convention. The implementation conforms to this file in follow-up PRs; it also doubles as the REPL completion source. ## Why We want the design pinned down and reviewable before the kernel (`protocol.ts`/`registry.ts`) migrates to it. Next slices build on this: CapTarget in the registry (`itx.ai` via a binding ref as the proof), then the MCP/OpenAPI litmus pair, then the codemode drop. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Documentation and type sketches only; no production code paths, auth, or data handling change in this PR. > > **Overview** > Adds **documentation-only** design artifacts for the itx layer—no runtime or kernel changes. > > **`apps/os/docs/itx-next.md`** is a working roadmap: **CapTarget** as `live | rpc | url` with **WorkerRef** (replacing today’s `live | worker | facet`); a single **`caps.define`** verb; MCP/OpenAPI as ordinary RPC clients, not protocol kinds; platform bindings as thin policy caps; a real **global** context and namespace-based stream addressing; a **codemode → fork + caps + execution events** migration plan; and a resolved/open-questions log. > > **`apps/os/src/itx/types.ts`** is the import-free **design of record**: narrative plus types for `Itx`, `CapTarget`/`WorkerRef`/`CapSource` (`cacheKey` vs `codeId`), `ItxPrincipal` mirrored from auth, `StreamRef`, `KnownCaps`, and related wire types—intended for future implementation conformance, REPL completion, and agent-facing docs. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ee52a30. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- CLOUDFLARE_PREVIEW --> ## Environment Config Lease <!-- CLOUDFLARE_PREVIEW_STATE --> <!-- { "apps": { "os": { "appDisplayName": "OS", "appSlug": "os", "status": "deployed", "updatedAt": "2026-06-10T11:42:47.344Z", "headSha": "ee52a30ecdead15fca064e1fac18765ced96a790", "message": null, "publicUrl": "https://os.iterate-preview-2.com", "runUrl": "https://github.com/iterate/iterate/actions/runs/27273577632", "shortSha": "ee52a30" } }, "environmentConfigLease": { "dopplerConfig": "preview_2", "leasedUntil": 1781095199909, "leaseId": "2aea9595-c753-4464-8a8e-5d0a529bbe70", "slug": "preview-2", "type": "environment-config-lease" } } --> <!-- /CLOUDFLARE_PREVIEW_STATE --> Lease: `preview-2` Doppler config: `preview_2` Type: `environment-config-lease` Leased until: 2026-06-10T12:39:59.909Z ### OS Status: deployed Commit: `ee52a30` Preview: https://os.iterate-preview-2.com [Workflow run](https://github.com/iterate/iterate/actions/runs/27273577632) Updated: 2026-06-10T11:42:47.344Z <!-- /CLOUDFLARE_PREVIEW --> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
jonastemplestein
added a commit
that referenced
this pull request
Jun 10, 2026
…capnweb pointers, fix task states
- README.md/AGENTS.md: real Important Files (src/worker.ts, src/config.ts),
real e2e lanes (pnpm e2e, pnpm e2e:itx), correct cf:deploy vs pnpm deploy
semantics, drop nonexistent /org/:organizationSlug route
- CONTEXT.md: fix the /org/:organizationSlug claim in the example dialogue
- architecture-and-operations.md: full rewrite against current code — Iterate
Auth (no Clerk), real route map, canonical MCP endpoint via
APP_CONFIG_MCP__BASE_URL, ProjectMcpServerEntrypoint 410 tombstone, real
redirects, /__durable-objects debug proxy, sync-auth-clients.ts, itx
- headless-local-debugging.md: /projects/new -> /new-project
- iterate-context{,-learnings}.md: tombstones pointing at src/itx/ successors
- capability-system-research / rpc-target-constructor-shape research notes:
historical status headers
- src/itx/README.md + handle.ts comment: replace the nonexistent ProjectCaps
declaration-merging pattern with the real Stubify cast
- itx-spec.md: PR #1407 is merged to main; mark the unbuilt client reconnect
loop (connectItx is one-shot) as a known divergence
- tasks: delete shipped simplify-context-cloudflare-native and
project-egress-secrets-mvp (verified in code); honest status notes on
codemode-session-vertical-slice and codemode-session-night-plan
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jonastemplestein
added a commit
that referenced
this pull request
Jun 10, 2026
…capnweb pointers, fix task states (#1432) Documentation sweep over `apps/os`. Every statement written into a doc was verified against the code on this branch. ## Changes **`apps/os/README.md` (= `AGENTS.md`)** - Important Files: `src/app.ts` / `src/entry.workerd.ts` do not exist — replaced with `src/worker.ts` (Worker entrypoint) and `src/config.ts` (`AppConfig` schema). All other listed files verified to exist. - Real-worker tests: the documented vitest configs (`src/capnweb/e2e/vitest.config.ts`, `src/domains/capability-prototype/e2e.vitest.config.ts`) are gone — replaced with the real lanes `pnpm e2e` (`e2e/vitest.config.ts`) and `pnpm e2e:itx` (`src/itx/e2e/vitest.config.ts`), verified against `apps/os/package.json`. - `pnpm cf:deploy # production deploy` was wrong and dangerous: `cf:deploy` deploys to whatever Doppler/Alchemy stage is ambient. Now documents both `cf:deploy` (ambient stage) and `pnpm deploy` (the `doppler --config prd` wrapper). - Removed the nonexistent `/org/:organizationSlug` route; remaining routes verified against `src/routes/`; added `/new-project`. **`apps/os/CONTEXT.md`** — fixed the example-dialogue claim that organization UI lives under `/org/:organizationSlug` (no such route; orgs live in the auth worker). **`apps/os/docs/architecture-and-operations.md`** — rewritten. The old doc described the pre-migration world: Clerk auth (whole `## Clerk` section, `sync-clerk-apps.ts`, `APP_CONFIG_CLERK__*`), `/orgs/:organizationSlug` route maps, inbound MCP via `ProjectMcpServerEntrypoint` (now a hardcoded 410 tombstone), wrong redirect claims, and an unprefixed `/durable-objects/stream` debug route. The new doc describes current reality: `src/worker.ts` dispatch pipeline, Iterate Auth middleware, real route map and root-redirect behavior (`/` → `/projects/$projectSlug` or `/projects`; project root renders `ProjectHomePage`), canonical MCP endpoint from `APP_CONFIG_MCP__BASE_URL` with Iterate Auth protected-resource metadata, `/__durable-objects/<kind>/<name>/<path>` debug proxy (kinds verified), itx endpoints, `scripts/sync-auth-clients.ts`, current codemode default/example providers, and current smoke-test env vars (verified in the e2e test files). **`apps/os/docs/headless-local-debugging.md`** — `/projects/new` → the real route `/new-project`. **`apps/os/docs/iterate-context.md`, `iterate-context-learnings.md`** — both pointed at the deleted `src/capnweb/` tree as "the current design"; now short tombstones pointing at the successor (`src/itx/` README + DECISIONS, `docs/itx-spec.md`). **`apps/os/docs/capability-system-research-and-design-notes.md`, `rpc-target-constructor-shape-research.md`** — added status headers marking them historical research notes superseded by itx; bodies untouched. **`apps/os/src/itx/README.md` + `src/itx/handle.ts`** — the "Typed caps" `ProjectCaps` declaration-merging pattern does not exist in code (no `ProjectCaps` interface anywhere). Rewrote the README section to the thing that actually works: casting `itx.cap("name")` through the exported `Stubify<T>` type. Also fixed the same false claim in the `Stubify` doc comment in `handle.ts` (comment-only change). **`apps/os/docs/itx-spec.md`** — status header said "IMPLEMENTED on the `itx-implementation` branch"; PR #1407 is merged to main (verified in git history). Marked the one known divergence honestly: the §6.3 client reconnect loop was never built — `connectItx` (`src/itx/client.ts`) is one-shot, and there is no `itx.cap.disconnected` event. Corrected §6.3 and the related §4 caveat. **`apps/os/tasks/`** - Deleted `simplify-context-cloudflare-native.md` (state: todo, but shipped — `src/worker.ts` imports `env` from `cloudflare:workers` directly, `RequestContext` is the narrow request-scoped shape the task specified, auth lives in Start request middleware, the manifest/`src/app.ts` is gone). - Deleted `project-egress-secrets-mvp.md` (state: todo, but shipped — `ProjectEgress` entrypoint, `ProjectDurableObject.egressFetch` with `substituteProjectEgressSecretHeaders`, D1-backed `SecretsCapability.getSecret`, and the `/api/itx/egress-echo` echo proof covered by `src/itx/e2e/itx-egress.e2e.test.ts`). - Grooming rules (`docs/tasks-grooming.md`) say "Delete when done", so deletion rather than state edits. - Added brief status notes (no rewrite) to `codemode-session-vertical-slice.md` (checked-off "tiny worker" box diverged: `CodemodeSession` lives in the main OS worker) and `codemode-session-night-plan.md` (plan superseded by itx). ## Skipped - Nothing skipped; all nine items verified and addressed. ## Flags for reviewers - `src/itx/handle.ts` got a comment-only edit (the `Stubify` doc comment made the same false declaration-merging claim as the README). No runtime change; typecheck/lint/tests pass. - The two deleted task files: please sanity-check the "shipped" verdicts above if you have more context on intended remaining scope. - Carve-outs respected: no changes to the streams type systems or to how the os-streams worker is deployed. ## Checks - `pnpm install`, `pnpm format` (oxfmt), `pnpm typecheck`, `pnpm lint`, `pnpm test` — all pass. ## Task-file audit A follow-up commit deletes 22 task files whose work was verified as shipped, obsolete, or purely historical. (Two more from the audit — `apps/os/tasks/project-egress-secrets-mvp.md` and `apps/os/tasks/simplify-context-cloudflare-native.md` — were already deleted by earlier commits on this branch, see above.) ### Deleted: completed - `tasks/cf-prd-orphaned-resources-cleanup.md` — live Cloudflare API check of the prd account (2026-06-10) shows 14 worker scripts (was 1026 at the task's 2026-05-18 sweep) and 6 D1 databases; cleanup is done. - `tasks/complete/2026-05-22-os-captun-worker-test-tunnel.md` — shipped via merged PR #1361 ("codemode++ e2e++"); all described artifacts exist on main and survived the golden-path rebuild (#1411). - `tasks/dead-code-and-docs-cleanup-audit.md` — high-confidence items all shipped; `pnpm-workspace.yaml` no longer lists the dead packages and now uses `apps/*`/`packages/*` globs. - `tasks/os-auth-spurious-logout-refresh.md` — commit ad6da76 "Fix 5-min logout, deploy-time JWKS, and stream append skeleton flash (#1410)" (merged 2026-06-10) shipped exactly this work. - `tasks/os-codemode-router.md` — task file was added in the very PR that implemented it (commit 98ee148, #1294). - `tasks/os-domain-capability-orpc-refactor-design.md` — every major pillar of the design (domains layout, capabilities, oRPC structure) exists on main. - `tasks/os-domain-capability-orpc-refactor-prd.md` — shipped in PR #1305 "Make codemode function calls event-driven" (squash commit 284193e, merged 2026-05-08). - `tasks/semaphore-lease-renewal.md` — the described lease-renewal feature exists on main as `resources.renew` (named "renew" rather than the proposed "extend") in `apps/semaphore`. - `tasks/signup-slug-uniqueness.md` — shipped with the auth worker (PR #1273); `packages/shared/src/slug.ts` implements `resolveUniqueSlug`/`slugifyWithSuffix`. - `apps/os/tasks/codemode-session-night-plan.md` — planned outcomes verifiably shipped on main, in evolved form (codemode session browser UI and follow-ons). - `apps/os/tasks/codemode-session-vertical-slice.md` — all 11 ticked checklist items shipped via PRs #1294/#1305 and follow-ups. - `apps/os/tasks/refactor-lifecycle-init-params-as-structured-name.md` — every acceptance criterion implemented in the `with-lifecycle-hooks.ts` mixin on main. - `apps/os/tasks/repos-vertical-slice.md` — frontmatter already says `state: done` and the described slice verifiably exists on main. - `apps/os/tasks/slack-processor-unwind.md` — all target-shape items exist on main (`/integrations/slack` stream path; no `/integrations/slack/webhooks` references). ### Deleted: obsolete / nonsense - `tasks/github-oauth-use-repo-id.md` — all referenced code is gone: `linkExternalIdToGroups` / `repoId` / `repository.id` return zero hits repo-wide. - `tasks/ignoreme-email-security.md` — every code path the task targets was deleted with the legacy OS1 stack (commit 545854d, #1341). - `tasks/os-stream-runtime-big-refactors.md` — os2-era brainstorm list largely superseded or done differently; item 2 shipped via PR #1394. - `tasks/realtime-pusher-efficiency.md` — targets the legacy OS1 realtime pusher, which no longer exists. - `tasks/stream-processor-ergonomics.md` — targets the legacy hook-style processor API, replaced by the class-based StreamProcessor model. ### Deleted: historical logs - `apps/os/tasks/slack-google-auth-poc-implementation.md` — explicitly an "Implementation Log" (`state: done`), not actionable work; shipped in merged PR #1317. - `apps/os/tasks/stream-processor-class-design-notes.md` — design notes written alongside the class-based StreamProcessor migration, not a task. - `apps/os/tasks/workspace-codemode-implementation-log.md` — `state: done`, all 9 checkpoints ticked; the described work verifiably shipped on main. ### Kept but flagged for maintainer judgment - `tasks/cf-prd-orphaned-resources-cleanup.md`: Explicit not-in-scope follow-ups (preview account 376ef7ed cleanup, Doppler os-legacy-backup pruning) were never broken out into their own tasks; spin them out only if still wanted. - `tasks/codemode-capability-policy.md`: Still-unshipped, still-wanted design work, but duplicates `apps/os/tasks/codemode-capability-access-policy.md` and overlaps the active itx capability-system design notes — maintainer should consolidate into a single task. - `tasks/complete/2026-05-22-os-captun-worker-test-tunnel.md`: apps/os still depends on the unpublished pkg.pr.new/captun@14 build (the task's stated stopgap); a published captun/worker release would be a separate follow-up, not a reason to keep this file. - `tasks/dead-code-and-docs-cleanup-audit.md`: Residual from this audit: packages/iterate is still excluded from root build/typecheck/test (`--filter '!iterate'`); if that CI gap matters, open a fresh small task rather than keeping this stale inventory. - `tasks/doppler-shared-and-os-secrets-audit.md`: Audit still unrun and wanted, but needs a rewrite first: replace Clerk-key expectations with iterateAuth, point AppConfig refs at `apps/os/src/config.ts` (`app.ts` and `packages/shared/src/apps/config.ts` were deleted in PR #1411), and refresh the 2026-05-18 baseline. - `tasks/ignoreme-email-security.md`: If outbound email via Resend is ever reintroduced in the rebuilt apps/os, recipient allowlisting should be designed fresh against the itx/egress-secret-substitution layer, not this OS1-era plan. - `tasks/iterate-cli-distribution.md`: Live but ~90% of the file is OpenCode architecture research notes, not actionable steps; npm distribution already exists, so the remaining work (bun binary, brew, install script) should be restated as concrete tasks or the research trimmed. - `tasks/os-auth-spurious-logout-refresh.md`: PR #1410 left one open thread: a manual end-to-end "wait 5 minutes in prod" verification was never done, and the claims-staleness force-refresh was consciously skipped (≤30m propagation accepted) — file a new narrow task only if either still matters. - `tasks/os-deploy-time-jwks-fetch.md`: Code shipped in PR #1410; only remaining action is deleting `ITERATE_AUTH_JWKS` from Doppler os prd/preview (still present and shadowing the deploy-time fetch) — after that, delete this task. - `tasks/os-domain-capability-orpc-refactor-prd.md`: Sibling task `os-domain-capability-orpc-refactor-design.md` (its dependsOn target) is likely also completed and should be audited/deleted together. - `tasks/os-project-do-projection-reconciliation.md`: Scope item "rename IterateMcpServer to ProjectMcpServerConnection" is already done and could be ticked off; the rest is unshipped and still relevant. - `tasks/os-project-hostname-base-singular.md`: Scope file paths are stale post-PR #1411 (`app.ts`→`src/config.ts`, `sync-clerk-apps.ts`→`sync-auth-clients.ts`, `entry.workerd.ts` deleted, routing files moved to `src/ingress/`); task itself is still valid. - `tasks/os-project-route-authorization.md`: Still-wanted design work (referenced by live project-ingress-architecture task), but needs rewrite: Clerk OAuth and `ProjectMcpServerEntrypoint` references are dead — MCP moved off project ingress (410 stub) and auth is now apps/auth Principal-based. - `tasks/os-stream-runtime-big-refactors.md`: Only surviving idea: cosmetic no-compat rename of `events.iterate.com/...` event-type names (events app is deleted); re-file as a small standalone task if still wanted. - `apps/os/tasks/codemode-capability-access-policy.md`: Live work, but near-duplicates root-level `tasks/codemode-capability-policy.md` (same PR #1294); keep this copy and consolidate/delete the root one. - `apps/os/tasks/codemode-session-night-plan.md`: Open capability-scope questions from this plan live on in `codemode-capability-access-policy.md`; checkboxes are unticked but the work shipped via PRs #1294/#1305/#1402. - `apps/os/tasks/codemode-session-vertical-slice.md`: Last unchecked box (generalize self-callable bindings) shipped as the loopback-binding pattern used repo-wide; follow-on work lives in `codemode-session-night-plan.md`. - `apps/os/tasks/project-egress-and-secrets-architecture.md`: Design doc whose first vertical slice shipped (egress + secret substitution MVP); remaining secret-DO/policy/approval/OAuth design is still live but needs grooming: drop completed PoC sections, update Clerk-scope terminology, and reconcile with itx DECISIONS.md as the newer design-of-record for egress wiring. - `apps/os/tasks/project-egress-intercept-tunnel-latency.md`: Still-relevant latency work, but file refs are stale (`entry.workerd.ts` → `src/worker.ts`; vendored `apps/os/src/lib/captun` removed for the published captun package in #1361) and the benchmark numbers predate the #1411 worker rebuild — re-benchmark before picking an option. - `apps/os/tasks/project-ingress-architecture.md`: Live, actively-maintained ingress reference (edited today in #1416), but needs a refresh: Clerk auth sections, `Project.checkAccess`, and the streams-upstream proxy model are superseded (auth worker, principal claims, bundled project worker), and the 2026-05-05 status checklist is partly outdated. - `apps/os/tasks/stream-processor-class-migration-log.md`: Migration log (merged today via #1402, which links to it as the canonical rationale) — not an actionable task; contains unique I6-I8 forensics not in the PR body, consider moving to docs/ alongside `tasks/migration-notes/` rather than deleting. - `apps/os/tasks/stream-subscriber-delivery-refactor.md`: Core design shipped differently via the class-model cutover (#1401/#1402/#1394); only live remainder is migrating `codemode.streamEvents`, `StreamsCapability.stream()`, and project-mcp-server-connection off the OS-internal NDJSON shim in `new-stream-runtime.ts` — consider replacing this large draft with a small task for that. - `apps/os/tasks/workspace-codemode-implementation-log.md`: Done implementation log; only marginally unique note is the rationale that plain method objects (not class instances) cross DO RPC, which is now embodied in the shipped workspace DO code. - `apps/os/tasks/migration-notes/`: Historical migration logs (not tasks) committed with and cited by merged PR #1402 one day ago; contain unique per-domain decisions plus the legacy-subscriber gap behind the 2026-06-10 prd Slack outage — maintainer should relocate to docs/ or delete deliberately. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Documentation and task-file deletions only; no application runtime or API behavior changes in the diff. > > **Overview** > **Aligns OS documentation with the current worker, auth, routing, and itx reality**, and **removes a large set of completed or obsolete task files** from `apps/os/tasks/` and `tasks/`. > > The **README / AGENTS** and **`architecture-and-operations.md`** rewrites drop Clerk-era and deleted-entrypoint references (`src/app.ts`, `src/entry.workerd.ts`, `/org/:organizationSlug`) in favor of **`src/worker.ts`**, **Iterate Auth**, **project-scoped routes** (`/projects/...`, `/new-project`), **canonical MCP** (`APP_CONFIG_MCP__BASE_URL`, auth-worker OAuth), **itx** endpoints, and **`sync-auth-clients.ts`**. Deploy docs now distinguish ambient **`pnpm cf:deploy`** from production **`pnpm deploy`**. E2E docs point at **`pnpm e2e`** and **`pnpm e2e:itx`** instead of removed capnweb vitest configs. > > **Cap'n Web tombstones** in `iterate-context*.md` redirect readers to **itx** (`src/itx/`, `itx-spec.md`). Research notes get **historical** headers; **itx-spec** notes merged status on main and documents that **`connectItx` is one-shot** (no §6.3 reconnect loop). **itx README / `Stubify`** docs are corrected: typed caps use **`itx.cap("name") as Stubify<...>`**, not declaration merging. > > **CONTEXT.md** fixes the example that claimed org UI lived under `/org/...`. **headless-local-debugging** uses **`/new-project`**. > > **Task grooming** deletes many markdown tasks whose work is done, superseded (itx, auth worker), or OS1-dead — including codemode vertical-slice plans, domain oRPC refactor design, egress MVP, Slack processor unwind, and similar inventory items. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a4f093f. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- CLOUDFLARE_PREVIEW --> ## Environment Config Lease <!-- CLOUDFLARE_PREVIEW_STATE --> <!-- { "apps": { "os": { "appDisplayName": "OS", "appSlug": "os", "status": "deployed", "updatedAt": "2026-06-10T12:23:34.040Z", "headSha": "a4f093f29684fc65b851dbf53847ccd85ddf8ffc", "message": null, "publicUrl": "https://os.iterate-preview-5.com", "runUrl": "https://github.com/iterate/iterate/actions/runs/27275677688", "shortSha": "a4f093f" } }, "environmentConfigLease": { "dopplerConfig": "preview_5", "leasedUntil": 1781097591555, "leaseId": "36e57584-6cc7-4024-a027-103a3cb0b29b", "slug": "preview-5", "type": "environment-config-lease" } } --> <!-- /CLOUDFLARE_PREVIEW_STATE --> Lease: `preview-5` Doppler config: `preview_5` Type: `environment-config-lease` Leased until: 2026-06-10T13:19:51.555Z ### OS Status: deployed Commit: `a4f093f` Preview: https://os.iterate-preview-5.com [Workflow run](https://github.com/iterate/iterate/actions/runs/27275677688) Updated: 2026-06-10T12:23:34.040Z <!-- /CLOUDFLARE_PREVIEW --> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
mmkal
added a commit
that referenced
this pull request
Jun 10, 2026
Replaces the `merge-to-main-slack` workflow (one Slack message per merged PR — noisy on busy days) with a workflow that maintains **at most one message per day** in `#ci`: a one-line PR dashboard summary, with the full per-PR breakdown in a single threaded reply. Both are created on the first PR event of the day and updated in place after that. Channel message: > **PR dashboard 10th June** — 51 merged · 9 closed without merging · 4 opened · 2 older still open (details in thread) Threaded reply (rendered from real data): > **Merged:** > • [#1410 Fix 5-min logout, deploy-time JWKS, and stream append skeleton flash](#1410) by jonas (ad6da76) > • [#1407 itx: contexts, capabilities, and the one true handle](#1407) by jonas (f256768) > … > **Closed without merging:** > • [#1440 Migrate captun to published npm 0.0.3](#1440) by misha > … > **Opened:** > • [#1448 Replace per-merge Slack messages with a daily PR dashboard](#1448) by misha (draft) > … > Old: [#1349](#1349), [#1355](#1355) How it works: - Content is refetched from the GitHub search API on every run (merged / closed-unmerged / opened-and-still-open today, plus older open PRs), so the message is self-healing — no incremental state to corrupt. - The day's message timestamps live in a repo Actions variable (`SLACK_PR_DASHBOARD_STATE`, `{date, channel, ts, details_ts}`), written with the same `ITERATE_BOT_GITHUB_TOKEN` the nag workflow uses. No new Slack scopes needed: `chat.update` uses the `chat:write` the bot already exercises. - Targets `#ci`, adopting #1452's decision to move merge announcements out of `#building` (that PR edited the workflow this one deletes; the conflict is resolved here by keeping the deletion). - The threaded details go out as chunked mrkdwn section blocks rather than one `text` param: on busy days a single text field hits `chat.update`'s `msg_too_long` (`postMessage` truncates, `update` rejects — found by e2e-testing against today's ~50 merges). - Plain-text author names (no @-mentions) since the messages update many times a day. - Testable two ways: pushing any `*pr-dashboard*` branch runs it for real against `#misha-test` with a separate state variable (create, update-in-place, and threading paths all verified this way — e.g. runs [27280068182](https://github.com/iterate/iterate/actions/runs/27280068182), [27288814028](https://github.com/iterate/iterate/actions/runs/27288814028)), and `node cli.ts github-script pr-dashboard.update_dashboard.update_pr_dashboard --github-token ...` does a local dry run that prints both messages. Task file: `tasks/slack-daily-pr-dashboard.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
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.

Implements apps/os/docs/itx-spec.md — replaces the Cap'n Web mount/scope machinery with three primitives: contexts (durable nodes), caps (registry entries: live/worker/facet), and itx (the one handle).
No backwards compatibility: prd will be destroyed on deploy. Old capnweb mounts, TargetCall, scopes, and the capability prototype are deleted as phases land.
Phases (see spec §10):
🤖 Generated with Claude Code
Environment Config Lease
No active environment config lease.
OS
Status: released
Commit:
98b23d1Preview: https://os.iterate-preview-3.com
Summary: Preview app released.
Workflow run
Updated: 2026-06-10T05:26:27.437Z
Semaphore
Status: released
Commit:
406522bPreview: https://semaphore.iterate-preview-3.com
Summary: Preview app released.
Workflow run
Updated: 2026-06-10T05:26:22.486Z
Note
High Risk
Major auth/capability and RPC architecture change with breaking API renames and deleted capnweb surfaces; touches project ingress, egress, and durable-object access patterns.
Overview
Replaces the Cap'n Web
IterateContextmodel (mounts, scopes,TargetCall) with itx: durable contexts, a cap registry (live/worker/facet), and a singleitxRPC handle used the same way from browser, Node,/api/itx, and loaded workers.Runtime wiring: adds
ITX_CONTEXT/ContextDOfor forked child contexts;pnpm e2e:capnweb→e2e:itx; removes the oldsrc/capnweb/tree (CLI, REPL helpers, e2e scripts, docs). Public surface moves to/api/itxper the spec.Governance & docs: enables
iterate/no-raw-durable-object-binding-accesssoPROJECT/REPO/STREAM/WORKSPACEgetByNamestays in capability/DO-internal paths; expands CONTEXT.md (admin secret, capability scope, project worker) and README real-worker test flows; adds large design artifacts (itx-spec.md, capability hierarchy sketch, research notes).Per the PR description, this is a no backwards-compatibility cutover—legacy capnweb endpoints and the capability prototype are removed as phases land.
Reviewed by Cursor Bugbot for commit 98b23d1. Bugbot is set up for automated code reviews on this repo. Configure here.