Skip to content

itx: contexts, capabilities, and the one true handle#1407

Merged
jonastemplestein merged 26 commits into
mainfrom
itx-implementation
Jun 10, 2026
Merged

itx: contexts, capabilities, and the one true handle#1407
jonastemplestein merged 26 commits into
mainfrom
itx-implementation

Conversation

@jonastemplestein

@jonastemplestein jonastemplestein commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

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):

  • Phase 1: ContextRegistry in Project DO
  • Phase 2: itx handle + entrypoint, delete mounts
  • Phase 3: Egress unification (globalOutbound + itx.fetch)
  • Phase 4: Child contexts (ContextDO + fork)
  • Phase 5: Facet caps + HTTP routing
  • Phase 6: Cleanup + rename captnweb→itx

🤖 Generated with Claude Code

Environment Config Lease

No active environment config lease.

OS

Status: released
Commit: 98b23d1
Preview: https://os.iterate-preview-3.com
Summary: Preview app released.
Workflow run
Updated: 2026-06-10T05:26:27.437Z

Semaphore

Status: released
Commit: 406522b
Preview: 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 IterateContext model (mounts, scopes, TargetCall) with itx: durable contexts, a cap registry (live / worker / facet), and a single itx RPC handle used the same way from browser, Node, /api/itx, and loaded workers.

Runtime wiring: adds ITX_CONTEXT / ContextDO for forked child contexts; pnpm e2e:capnwebe2e:itx; removes the old src/capnweb/ tree (CLI, REPL helpers, e2e scripts, docs). Public surface moves to /api/itx per the spec.

Governance & docs: enables iterate/no-raw-durable-object-binding-access so PROJECT/REPO/STREAM/WORKSPACE getByName stays 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.

jonastemplestein and others added 20 commits June 9, 2026 13:08
# 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>
@jonastemplestein jonastemplestein marked this pull request as ready for review June 9, 2026 21:29
Comment thread apps/os/src/itx/handle.ts Outdated
Comment thread apps/os/src/itx/handle.ts
…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>
Comment thread apps/os/src/itx/context-do.ts
Comment thread apps/os/src/itx/http.ts
jonastemplestein and others added 2 commits June 9, 2026 23:08
# 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>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread apps/os/src/itx/registry.ts
jonastemplestein and others added 3 commits June 9, 2026 23:16
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 jonastemplestein merged commit f256768 into main Jun 10, 2026
9 checks passed
@jonastemplestein jonastemplestein deleted the itx-implementation branch June 10, 2026 05:24
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>
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant