From c76b240c13b1c0a3269adb626becaa2f011134b1 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Tue, 20 Oct 2026 20:22:28 -0700 Subject: [PATCH] docs(repo): prune stale micro-docs and align docs with current layout --- ARCHITECTURE.md | 30 +++++++++++----- README.md | 1 - apps/web/src/auth/dither-background.tsx | 5 +-- apps/web/src/chat/threads-api.ts | 6 ++-- apps/web/src/chat/workbench-roster.ts | 6 ++-- apps/web/src/needs-converge.ts | 3 +- apps/web/src/pages/workbench-page.tsx | 4 +-- apps/web/src/routines-api.ts | 5 ++- apps/web/src/settings/section-registry.tsx | 2 -- docs/GLOSSARY.md | 8 ++--- docs/auth-dither-background.md | 14 -------- docs/chat-mail-threading.md | 42 ---------------------- docs/routines-scheduling.md | 36 ------------------- docs/settings-sections.md | 17 --------- 14 files changed, 40 insertions(+), 139 deletions(-) delete mode 100644 docs/auth-dither-background.md delete mode 100644 docs/chat-mail-threading.md delete mode 100644 docs/routines-scheduling.md delete mode 100644 docs/settings-sections.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 7fea8a8e6f..36cda894a0 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -27,19 +27,24 @@ handful of domain packages built on top. ## Tenancy A workbench is a plain Interchange tenant — there is no separate -"workbench" tenancy layer. Every user gets a tenant on sign-up; a +"workbench" tenancy layer. Every user gets a root workspace tenant on +sign-up, and creating a workbench creates a child tenant underneath it; a principal is a person or an agent that can act inside one. ## Workbench mail -There is no chat-specific data model. A conversation is a thread in -`@corbits/mailbox`: `GET /me/inbox/threads` lists threads, `POST -/me/inbox/send` sends a message (as a person or, addressed as an agent -principal, as that agent). Fan-out, read state, and threading are the -library's; the hub only resolves the caller's tenant/principal and mailbox -address. +There is no chat-specific data model. A workbench's conversation is its own +child tenant's mailbox in `@corbits/mailbox` (`/api/tenants/:id/mailbox/me/inbox/...`). +`sendToWorkbench` fans out to every live agent in the workbench unless an +`@Name` mention narrows the recipient list, and appends a trailing +`Participants:` roster (stripped when rendering a person's echoed-back +message) so agents know each participant's run address and copy the person on +handoffs. A person's address (`@`) is lowercased on both client +and hub so small models that lowercase addresses on reply still match the +thread's participant; mailbox delivery matches the local part +case-insensitively. -## Agents and workflows +## Agents, workflows, and scheduling `packages/worker` is the one harness a bench's worker, and any agent Workbench creates, deploys on. Its tools are `@intx/tools-mail` over the @@ -51,6 +56,15 @@ requires its use on the deployer's authority, and the sidecar hands the tool an origin-pinned mediated fetch. The worker writes further workflows and agents as code; the person deploys them through the stock deploy route. +Scheduled workflows use `@corbits/cron` (`/api/tenants/:id/cron`), targeting a +workflow by its source asset's definition name so schedules survive redeploys. +At fire time the ticker resolves the definition name to the live anchor run +and delivers via the stock mail trigger route (`POST /workflows/:runId/mail`, +which Run Now also calls directly). If no anchor run is live between runs, the +schedule waits (`waitingSince`) and resumes on the next delivery; it stops +permanently (`stoppedReason: "agent_deleted"`) only when the definition itself +is deleted. + ## Tool visibility Interchange sends a model every tool on every turn. Workbench keeps that diff --git a/README.md b/README.md index 06c99f7734..8c89d53290 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ local [Ollama](https://ollama.com) works with no key, see | ------------- | ---------------------------------------------------------------- | | `apps/` | `hub` (API), `web` (React client), `sidecar` (execution host) | | `packages/` | Corbits libraries, and `worker`, the default agent harness | -| `workflows/` | Deployable workflow packages | | `vendor/intx` | Hand-copied Interchange packages, see [VENDORED.md](VENDORED.md) | ## Develop diff --git a/apps/web/src/auth/dither-background.tsx b/apps/web/src/auth/dither-background.tsx index 40d9c7e825..e44632bbfb 100644 --- a/apps/web/src/auth/dither-background.tsx +++ b/apps/web/src/auth/dither-background.tsx @@ -22,8 +22,9 @@ const STRENGTH_LERP = 0.08; // cursor influence easing per frame const FRAME_MS = 1000 / 30; // animation cap (~30fps) to spare the CPU const ASSET = "/images/hero-dither.png"; // same-origin source image -// Renders on a plain 2D canvas, not WebGL — see -// docs/auth-dither-background.md for why. +// Renders on a plain 2D canvas, not WebGL: a WebGL layer failed to composite +// behind the auth panel, whereas 2D canvas is CPU-rasterized and retains its +// last frame when requestAnimationFrame pauses on a hidden tab. export function DitherBackground({ className }: { className?: string }) { // The animation belongs to the canvas element, so it starts and stops with // it: a ref callback with a cleanup, never an effect reaching for a ref. diff --git a/apps/web/src/chat/threads-api.ts b/apps/web/src/chat/threads-api.ts index 20ed5b30ba..d2efc5b61b 100644 --- a/apps/web/src/chat/threads-api.ts +++ b/apps/web/src/chat/threads-api.ts @@ -283,7 +283,7 @@ export function subscribeToInbox(tenantId: string, onChange: () => void): () => }; } -// Workbenches: see docs/chat-mail-threading.md. +// Workbenches: see ARCHITECTURE.md (§ Workbench mail). export type WorkbenchParticipant = { readonly id: string; @@ -478,8 +478,8 @@ async function isTerminalRunBody(response: Response): Promise { return !(TerminalRunBody(await response.json().catch(() => undefined)) instanceof type.errors); } -// The one send seam for a workbench (see docs/chat-mail-threading.md for -// the roster mechanism). +// The one send seam for a workbench (see ARCHITECTURE.md § Workbench mail +// for the roster mechanism). export async function sendToWorkbench(input: { readonly workbenchTenantId: string; readonly participants: readonly WorkbenchParticipant[]; diff --git a/apps/web/src/chat/workbench-roster.ts b/apps/web/src/chat/workbench-roster.ts index 2f18d3640d..9d4587d4ce 100644 --- a/apps/web/src/chat/workbench-roster.ts +++ b/apps/web/src/chat/workbench-roster.ts @@ -1,6 +1,6 @@ -// See docs/chat-mail-threading.md for the roster mechanism. `stripRoster` -// is the inverse, used to keep the block out of a person's echoed-back sent -// message. +// See ARCHITECTURE.md (§ Workbench mail) for the roster mechanism. +// `stripRoster` is the inverse, used to keep the block out of a person's +// echoed-back sent message. const ROSTER_HEADING = "Participants:"; diff --git a/apps/web/src/needs-converge.ts b/apps/web/src/needs-converge.ts index 4115b96208..89285761ea 100644 --- a/apps/web/src/needs-converge.ts +++ b/apps/web/src/needs-converge.ts @@ -146,8 +146,7 @@ export type ForkSubThreadInput = { }; /** Replays by native Message-ID when parent + subject + recipients + body - * match an already-recorded fork; any difference sends anew. See - * docs/chat-mail-threading.md. */ + * match an already-recorded fork; any difference sends anew. */ export async function forkSubThread( storage: StringStorage, hub: StockHub, diff --git a/apps/web/src/pages/workbench-page.tsx b/apps/web/src/pages/workbench-page.tsx index 8187002308..0c1f2ced54 100644 --- a/apps/web/src/pages/workbench-page.tsx +++ b/apps/web/src/pages/workbench-page.tsx @@ -1,5 +1,5 @@ -// See docs/chat-mail-threading.md. The bench pill opens a drawer that pushes -// the thread left; the drawer's Information tab carries the bench overview. +// The bench pill opens a drawer that pushes the thread left; the drawer's +// Information tab carries the bench overview. import { Button, EmptyState, PageShell, Skeleton } from "@corbits/react-ui"; import { PanelLeft, WarningCircle } from "@/lib/icons"; diff --git a/apps/web/src/routines-api.ts b/apps/web/src/routines-api.ts index 331808cce1..c259b75f2a 100644 --- a/apps/web/src/routines-api.ts +++ b/apps/web/src/routines-api.ts @@ -1,6 +1,5 @@ -// The Routines page's seam to stock workflow deployments. See -// docs/routines-scheduling.md for the schedule join and why run-now/ -// run-now goes through the stock trigger route. +// The Routines page's seam to stock workflow deployments and `@corbits/cron` +// schedules (see ARCHITECTURE.md § Agents, workflows, and scheduling). import { type } from "arktype"; import { useQuery } from "@tanstack/react-query"; diff --git a/apps/web/src/settings/section-registry.tsx b/apps/web/src/settings/section-registry.tsx index f92faeeb39..43170c90c7 100644 --- a/apps/web/src/settings/section-registry.tsx +++ b/apps/web/src/settings/section-registry.tsx @@ -1,5 +1,3 @@ -// See docs/settings-sections.md for the grouping rationale. - import { Cpu, Key, ListBullets, Shield, Star, User, Users } from "@/lib/icons"; import { AccountSection } from "./account-section"; diff --git a/docs/GLOSSARY.md b/docs/GLOSSARY.md index 6d82cd8967..1d755f5980 100644 --- a/docs/GLOSSARY.md +++ b/docs/GLOSSARY.md @@ -12,7 +12,7 @@ paths keep the platform's own names. | **User** | principal | An identity that can act in a workspace or workbench — human or agent. | | **Agent** | principal (agent) | A named coworker principal, not a template. | | **Worker** | — | A workbench's own persistent agent, deployed on the `packages/worker` harness. Given a job it can't already do, it authors a new tool, skill, or workflow as code, deploys it with only the access it needs, and uses it. | -| **Thread** | mailbox thread | The ordered messages of one conversation, served by `@corbits/mailbox` (`GET /me/inbox/threads`, `POST /me/inbox/send`). | +| **Thread** | mailbox thread | The ordered messages of one conversation, served by `@corbits/mailbox` (`/api/tenants/:id/mailbox/me/inbox/...`). | | **Grant** | grant | Permission for a principal to act on a resource. | | **Approval** | approval | A human decision gating an external side effect. | | **Hub** | hub | The API and coordination service a workspace lives on. | @@ -20,6 +20,6 @@ paths keep the platform's own names. ## Package naming -`@corbits/*` is the default scope for every package under `packages/`, -`tools/`, `agents/`, `skills/`, and `workflows/` — with a kebab-case kind -suffix where a package is one of a family (`-agent`, `-tools`). +`@corbits/*` is the default scope for every package under `packages/` — +with a kebab-case kind suffix where a package is one of a family +(`-agent`, `-tools`). diff --git a/docs/auth-dither-background.md b/docs/auth-dither-background.md deleted file mode 100644 index c847502ac5..0000000000 --- a/docs/auth-dither-background.md +++ /dev/null @@ -1,14 +0,0 @@ -# Auth dither background: 2D canvas, not WebGL - -`apps/web/src/auth/dither-background.tsx` renders an animated ordered-dither -over a source image on a plain 2D canvas: a downscaled buffer is dithered -each frame with a slow ambient sine warp plus a cursor-driven displacement, -then upscaled with `image-rendering: pixelated`. - -This replaces an original WebGL port: a WebGL canvas promoted the auth panel -to a GPU-composited layer that failed to paint (blank/white). A 2D canvas is -CPU-rasterized, composites reliably, and retains its last frame when -`requestAnimationFrame` is paused on a hidden tab. - -The loop pauses whenever the canvas is offscreen or the tab is hidden, caps -to ~30fps, and honors `prefers-reduced-motion` reactively. diff --git a/docs/chat-mail-threading.md b/docs/chat-mail-threading.md deleted file mode 100644 index 6d47bc1eae..0000000000 --- a/docs/chat-mail-threading.md +++ /dev/null @@ -1,42 +0,0 @@ -# Workbench mail threading - -A workbench is a child tenant, and its conversation is that tenant's own -mailbox — reads and sends go through the stock mailbox routes scoped to -the child tenant id. No workbench-specific hub route exists. - -## Workbench send roster - -`sendToWorkbench` appends a trailing roster of every participant's name and -address to the message body (the same rows the Participants panel reads). -The hub only delivers to a run address, which only the client otherwise -knows, so this is what lets an agent hand a task to another agent in the -workbench. The mail tools have no `cc` field, so the roster also tells -agents to copy the person on a handoff by naming them as another `to` -recipient. - -## Mentions - -A message with no `@Name` token fans out to every live agent in the -workbench. Typing `@` in the composer opens a roster popover; choosing an -agent inserts its name as a plain token, which `mentionedAgents` reads back -at send time to narrow the `to` list to just those agents. The token stays -in the body so the agent sees who was addressed, and the roster block is -still appended in full, so a narrowed message can still be handed on. - -## A person's address is lowercase - -A person's address is their auth user id at the workbench domain, and that -id is mixed case. Small models lowercase an address before replying, so -`personMailAddress` lowercases it at every point the client derives one — -the roster, the Participants panel, a schedule's body — and the hub stamps -the same lowercase form on a person's outbound mail. Mailbox delivery -matches the local part case-insensitively, so both forms reach the same -inbox: threads written before this keep their mixed-case addresses, new -mail carries the lowercase one, and nothing is migrated. - -## Sub-thread fork replay - -`forkSubThread` treats a fork as already sent when parent + subject + -recipients + body match a recorded row, returning its native Message-ID -instead of resending. Rows recorded before recipients/body were stored -match on parent + subject only, to preserve their exactly-once replay. diff --git a/docs/routines-scheduling.md b/docs/routines-scheduling.md deleted file mode 100644 index 5b3ff7c529..0000000000 --- a/docs/routines-scheduling.md +++ /dev/null @@ -1,36 +0,0 @@ -# Routines page data sources - -`apps/web/src/routines-api.ts` reads stock deployments (`GET -/workflows/deployments`) joined against the tenant's workflow assets (`GET -/assets?kind=workflow`) for a display name — the two stock reads -`vendor/intx/hub-api/src/routes/workflows.ts` exposes. - -## Schedule column - -The `schedule` trigger is reserved on Interchange but unimplemented — no -scheduler fires it. A schedule shown here is a `@corbits/cron` row targeting -an agent by its workflow definition's name — the source asset's name, stable -across redeploys — joined in from `GET /cron` by that name. The ticker -resolves the name to the live anchor run at fire time, so a redeploy keeps -the schedule working. - -An agent between runs has no live anchor to deliver to, so the ticker skips -the tick and marks the row waiting (`waitingSince`) instead of firing. The -next delivery clears the marker, so a restart or a redeploy resumes the -schedule on its own; the workbench page shows such a row as waiting for its -agent, at full weight. - -Only a schedule whose agent's definition is gone is stopped by the ticker -(`stoppedAt` / `stoppedReason: "agent_deleted"`), and it never fires again: -a redeploy does not resume it, so the workbench page renders it as stopped -and offers to schedule it again from a prefilled form. Creating a schedule -for a name no definition carries is rejected the same way -(`400 unknown_definition`). - -## Run-now and pause/resume - -Run now posts to the stock trigger route -(`POST /workflows/:runId/mail`, `:runId` = the deployment id), carrying the -cron row's subject and body when one exists. A 409 means the run has ended. -Pause/resume has no stock or `@corbits/cron` primitive, so no control is -shown. diff --git a/docs/settings-sections.md b/docs/settings-sections.md deleted file mode 100644 index 3661d9bfe6..0000000000 --- a/docs/settings-sections.md +++ /dev/null @@ -1,17 +0,0 @@ -# Settings section registry - -`apps/web/src/settings/section-registry.tsx` collapsed Personal/Workspace -into one account-scoped group and one shared group: there is one workbench -per account now, so "workspace-scoped" and "account-scoped" name the same -tenant's settings. - -Shared Settings leads with what everyone inherits (credentials, then -People) and tucks access-control mechanics (Roles, Grants, Audit) under a -collapsed Advanced disclosure — nobody should have to parse grants and -roles just to find where a shared API key lives. - -Bench dies outright: there's no longer a second thing to name, distinct -from the account, so its rename/purpose/icon form and member list have no -home to keep them separate in. Conversation-scoped settings (agent, -capabilities, history) live on the workbench's own settings surface -instead.