# Architecture ## Product model Factory is a durable local issue tracker whose execution unit is a DSH Agent Session. A task is a dependency-graph node. A standard flow is a named group assembled from explicit tasks and dependency edges; model skills create those nodes directly instead of selecting a stored graph template. Each project owns at most one inbox flow named Emerging work: live Sessions without Factory ownership become ordinary waiting tasks there and use the same cards and graph operations. Human task and flow intake stays on DSH New Session; model skills may still create explicit nodes and group a relationship-complete set. Model-created work defaults to draft. New Session task/flow intake also creates a draft: it consumes the composer submit without prompting or binding the blank Session, then opens that task card after Factory acceptance. Standalone enqueue makes one task eligible, and standard flow start atomically queues ordinary nodes and enables timed nodes. A recurring task has durable `scheduled` status between occurrences, never reaches task-level completion, and stores each terminal occurrence on its run for Triage. `current` uses the repository primary checkout, `isolated` requests a managed linked checkout, and `reuse` continues in a predecessor's retained checkout. Recurring isolated work reuses its latest retained managed checkout. Project settings supply the inherited task model, title model, optional title/description instructions, title-generation opt-out, checkout/base ref, and setup command. During an active run, the task discussion is a first-class view of that DSH Session's user prompts: queued follow-ups remain editable and reorderable, steering is explicit, and claimed prompts hand off to immutable posted comments. ## Authorities | Concern | Authority | | --- | --- | | Project/task/flow/run/metadata records | Factory SQLite document | | Atomic revision, presence, scheduler lease | `dsh-factory-store` provider | | Dependency, inbox, metadata, recurrence, Triage, and lifecycle transitions | `dsh-factory-domain` | | Readiness, lane allocation, Agent ownership | `dsh-factory-scheduler` | | Pending task follow-up prompts and delivery order | DSH Agent inbox (`session/queue`) | | Posted task prompts and model-visible history | DSH Session log | | Model execution | DSH Agent loop | | Tool execution | DSH ToolRuntime | | Repository checkout safety | DSH Worktree provider | | Browser business projection | Typert Remote snapshot | | Observed-inbox Session lifecycle projection | Harness `sessionDisposition` client service | | Run visual-review media | Image/video files under the run checkout's `.artifacts` directory | | Browser selection/filter/editor state | React | ## Scheduling transaction The elected process renews a lease row, expires stale presence, and enters one SQLite `BEGIN IMMEDIATE` transaction. The transaction verifies the same process still owns an unexpired lease, activates due delayed, one-time, or recurring automations, derives ready tasks, excludes occupied resolved lanes, creates run attempts, and moves tasks to `dispatching`. Recurring activation calculates the next local-time Croner occurrence before dispatch, so ticks cannot overlap one task. Terminal success or failure copies its result to the run and returns the task to `scheduled`; a takeover records a disappeared recurring occurrence as failed and returns the definition to `scheduled` instead of exhausting its lifetime attempt count. An abruptly failed scheduler-owned run requeues its task with an exponential-backoff claim gate — task retry policy over workspace settings, three retries doubling from thirty seconds by default — and settles terminally only after the streak exhausts or an explicit report decides the outcome. A no-op inspection rolls back without advancing the revision. ## Task identifiers Every project assigns its first task a stable uppercase key derived once from the project title — `DOCS-3` for one Docs workspace, `SR-2` for another Super Repository — and advances its own monotonically increasing counter on the project record, so deleting a task never recycles a number and wiring dependencies across workspaces no longer relies on one global sequence. Colliding keys gain a numeric suffix per document. Tasks created before project keys arrived keep their document-wide `FAC` numbers; a project that legitimately derives `FAC` continues that shared pool from its documented floor, so every historical identifier keeps resolving in dependencies, flows, results, and discussions. ## Metadata and inbox transactions Task creation commits the prompt-derived fallback and the exact title-model route, custom-or-default title and description instructions, system text, input, token limit, and running receipt before making the auxiliary model request. Settlement records output or failure and replaces only fallback fields that a concurrent edit has not changed. Project `autoTitle: false` skips the request while preserving the same deterministic fallback. Resetting advanced prompts omits overrides so both browser and domain use the exported defaults. Presence observations with accepted user work but without task ownership are idempotently adopted into Emerging work for their canonical project; blank Sessions are excluded. New Session intake atomically creates or reuses a draft by branded `intakeId`, retains its blank source as `intakeSessionId` provenance, preserves composer images and the selected model/preset, starts no run, and can start a named flow or place the task as a parallel, sequential, or always-run finalizer node in a nonterminal same-project flow. One submission identity makes transport retries idempotent; another submission from the same blank Session appends another task. An independently observed run becomes running with its local Agent. At its first normal response stop, the tools Consumer waits for a fresh presence/adoption publication, installs a Session-scoped `factory_finish`, and appends one logged next-step notice when no report exists. A Session that disappears while the run remains active still fails abruptly; scheduler takeover recovers only `scheduler`-origin attempts, so a host restart cannot queue duplicate work for an external Session. Inbox dependencies stay inside the sink. Grouping requires a relationship-complete inbox selection; model tools may also group same-project standalone tasks after creating their complete dependency graph. ## Agent execution and Triage The scheduler resolves a checkout through `ctx.worktrees`, runs the inherited project setup command through `ctx.shell`, resolves a task model override or the project model against the shared default, mounts the Agent preset, and installs run-scoped `ask_user_question` and `factory_finish` tools before the assignment. The initial assignment, direct predecessor output, bounded mutation-ledger handoff, and screenshots enter as one logged user message with `factory-task` provenance. User task comments submit through the active Session's Queue or Steer path, so every delivered prompt is logged and pending rows retain the Agent inbox's stable identity and order. An outstanding human question keeps the tool call and Agent turn running, which leaves the task nonterminal and its dependent nodes ineligible. The answer returns through the existing DSH question provider; `factory_finish` is rejected when it appears in the same model step as the question, so a later request must receive the answer before the Agent can continue, verify, and report completion. A missing report at a normal stop receives at most one reminder in that turn; max-token, abnormal, already-reported, and terminal-run stops receive none. After `factory_finish`, one final model step preserves user-facing output. Scheduler reports remain buffered until whole-Agent idle and a successful Session flush; observed reports flush and settle at the terminal response boundary before a one-shot Agent can disappear. Terminal observed settlement removes its scoped tool and listener, so later user conversation remains ordinary. Blocked runs retain the live Session and lane. Each run snapshots its terminal summary or failure, reported artifact paths, receipt-backed file mutations, and review timestamp; its task card and exact Triage occurrence lazily list bounded image/video files from that run checkout's `.artifacts` directory and revision-check each requested file without copying media into durable Factory state. recurring attempts also snapshot the cadence that launched them. Triage filters all, unread, and failed regular, observed-Session, and recurring occurrences; opening or bulk-reviewing results commits `reviewedAt` under the same optimistic revision. Historical entries remain reviewable if the task's future cadence changes or is cancelled. ## Safety and cleanup The default cleanup policy retains isolated worktrees. Recurring tasks reuse the latest retained managed checkout when present. Optional removal and periodic sweeping preserve paths referenced by nonterminal reuse tasks, then call the Worktree capability, whose provider refuses unmanaged paths, dirty work, main checkouts, and live Session ownership. Factory never force-removes or implicitly publishes. Publishing is an explicit task, commonly a success-only finalizer. ## Browser The external browser module mounts generated Factory Typert descriptors, contributes one `sidebar.navigation` row, and takes over `application.surface` only when selected. It also contributes one compact Task/Flow control after Full access on the blank-session composer and wraps admission through `ctx.conversation.submissions`; Task defaults to the ordinary send so Agent observation captures the live Session in Emerging work, while nested Run later and Flow return a successful consumed outcome after Factory commits the draft. It replaces complete snapshots after polling or mutation; no browser task-graph store exists. Work renders Emerging work first and derives counts, labels, nested relationship rails, and Scheduled state from each snapshot. It maps each task's latest Session-bearing run to the Harness `sessionDisposition` service: settled cards enter a collapsed reversible history shelf, while snoozed or archived cards disappear, including named-flow nodes. Task detail can settle or archive that linked Session through the shared services. A cancelled task with no linked Session can instead be permanently deleted after explicit acknowledgement; the domain transaction removes its unlinked history and safe graph references while rejecting retained checkouts, reuse relationships, and runnable dependents. Dependency selection omits terminal tasks and tasks whose linked Session is settled, snoozed, or archived; the domain also rejects terminal prerequisites from stale clients. Pending root, branch, join, and finalizer nodes use neutral Lucide glyphs over main and branch rails with the same initial color; running, succeeded, and abruptly failed nodes become a blue spinner, green check, or red cross. The root tabs are Work, Triage, and Settings, and the application contains no task or flow creation modal. Task dependency selection uses a DSH Menu with a pinned query and keyboard selection. Task prompts are fixed-height scroll regions with schedule and a concrete effective model selector; a searchable, creatable label multi-select belongs to the right properties sidebar. The task discussion opens its active or latest run Session through the existing client runtime, disables active-run input until that Session binding exists, renders durable messages and Factory notes by timestamp followed by pending steering and queued placements, and uses adjacent move, edit, remove, Queue, and Steer operations without a React queue store. Steered prompts lead pending follow-ups; controls disappear after posting. The composer accepts pasted images, reusing the Session attachment rail, history gallery, and fullscreen lightbox for draft, queued, steered, posted, and Factory-note images. Terminal tasks keep Session history read-only while new text or images remain an ordinary Factory note. Recurring composition offers hourly, daily, weekday, weekly, monthly, and raw five-field cron schedules with native date/time controls. Settings retains Models and Worktrees section glyphs without app/header icon tiles, uses equal-height controls, and exposes resettable advanced metadata prompts on one spaced content track. On mobile, the Work hierarchy truncates secondary columns, Triage switches from its list to one animated detail view, task columns stack, controls span the available width, the Factory title aligns with the host drawer button, and safe-area padding keeps terminal actions reachable. Output and Triage reuse the receipt-backed deliverables card. Task detail resolves the active/latest run while Triage retains its exact historical run id; both poll `.artifacts` metadata, cache unchanged file revisions, reuse the chat composer's 64px horizontally paged thumbnail rail for images/videos, and open a fullscreen keyboard carousel across that run's loaded media. React owns only tab/card selection, filters, staged New Session intent, drafts, menu/editor state, and request feedback.