This file is the single source of truth for agents entering this repository. Read this file first; after entering apps/, packages/, tools/, or e2e/, read that layer's AGENTS.md for module-level details. Do not copy module details back into the root file; root stays focused on cross-repository boundaries, workflow, and commands.
- Product and onboarding:
README.md,README.zh-CN.md,QUICKSTART.md. - Contribution and environment:
CONTRIBUTING.md,CONTRIBUTING.zh-CN.md. - Architecture and protocols:
docs/spec.md,docs/architecture.md,docs/skills-protocol.md,docs/agent-adapters.md,docs/modes.md. - Roadmap and references:
docs/roadmap.md,docs/references.md,docs/code-review-guidelines.md,specs/current/maintainability-roadmap.md. - Directory-level agent guidance:
apps/AGENTS.md,packages/AGENTS.md,tools/AGENTS.md,e2e/AGENTS.md.
- Workspace packages come from
pnpm-workspace.yaml:apps/*,packages/*,tools/*, ande2e. - Top-level content directories:
skills/(functional skills the agent invokes mid-task — utilities, briefs, packagers; seeskills/AGENTS.md),design-templates/(rendering catalogue: decks, prototypes, image/video/audio templates; seedesign-templates/AGENTS.mdandspecs/current/skills-and-design-templates.md),design-systems/(brandDESIGN.mdfiles),craft/(universal brand-agnostic craft rules a skill can opt into viaod.craft.requires). apps/webis the Next.js 16 App Router + React 18 web runtime; do not restoreapps/nextjs.apps/daemonis the local privileged daemon andodbin. It owns/api/*, agent spawning, skills, design systems, artifacts, and static serving.apps/desktopis the Electron shell; it discovers the web URL through sidecar IPC.apps/packagedis the thin packaged Electron runtime entry; it starts packaged sidecars and owns theod://entry glue only.packages/contractsis the pure TypeScript web/daemon app contract layer.packages/sidecar-protoowns the Open Design sidecar business protocol;packages/sidecarowns the generic sidecar runtime;packages/platformowns generic OS process primitives.tools/devis the local development lifecycle control plane.tools/packis the local packaged build/start/stop/logs control plane and mac beta release artifact preparation surface.tools/pris the maintainer PR-duty control plane: a thinghwrapper that encodes this repo's review-lane derivation, forbidden-surface flags, lane checklists, and validation-command suggestions.e2eowns user-level end-to-end smoke tests and Playwright UI automation; reade2e/AGENTS.mdbefore editing its tests or commands.
apps/nextjsandpackages/sharedhave been removed; do not recreate or reference them..od/,.tmp/, Playwright reports, and agent scratch directories are local runtime data and must stay out of git.
- Runtime target is Node
~24andpnpm@10.33.2; use Corepack so the pnpm version pinned inpackage.jsonis selected. - New project-owned entrypoints, modules, scripts, tests, reporters, and configs should default to TypeScript.
- Residual JavaScript is limited to generated output, vendored dependencies, explicitly documented compatibility build artifacts, and the allowlist in
scripts/guard.ts.
- Use
pnpm tools-devas the only local development lifecycle entry point. - Do not add or restore root lifecycle aliases:
pnpm dev,pnpm dev:all,pnpm daemon,pnpm preview, orpnpm start. - Ports are governed by
tools-devflags:--daemon-portand--web-port. tools-devexportsOD_PORTfor the web proxy target andOD_WEB_PORTfor the web listener; do not useNEXT_PORT.
- Keep root scripts reserved for true repo-level checks and tools control-plane entrypoints:
pnpm guard,pnpm typecheck,pnpm tools-dev,pnpm tools-pack, andpnpm tools-pr. - Do not add root aggregate
pnpm buildorpnpm testaliases. Build/test commands must stay package-scoped (pnpm --filter <package> ...) or tool-scoped (pnpm tools-pack .../pnpm tools-pr ...). - Do not add root e2e aliases; e2e package commands and ownership rules live in
e2e/AGENTS.md.
- Tests under
apps/,packages/, andtools/live in a package/app/tool-leveltests/directory sibling tosrc/; keepsrc/source-only and do not add new*.test.tsor*.test.tsxfiles undersrc/. Playwright UI automation belongs toe2e/ui/, not app packages. - App packages must not import another app's private
src/ortests/implementation as a shared helper. In particular,apps/web/**must not importapps/daemon/src/**; web/daemon integration belongs behind HTTP APIs,packages/contracts, and app-local provider boundaries. - Cross-app, cross-runtime, or repository-resource consistency checks belong in
e2e/tests/when they need to observe more than one app/package boundary; promote reusable logic to a pure package instead of borrowing another app's private source. - Keep shared API DTOs, SSE event unions, error shapes, task shapes, and example payloads in
packages/contracts; update contracts before wiring divergent web/daemon request or response shapes. - Keep
packages/contractspure TypeScript and free of Next.js, Express, Node filesystem/process APIs, browser APIs, SQLite, daemon internals, and sidecar control-plane dependencies. - Keep project-owned entrypoints, modules, scripts, tests, reporters, and configs TypeScript-first; generated
dist/*.jsis runtime output, and source edits belong in.tsfiles. - New
.js,.mjs, or.cjsfiles need an explicit generated/vendor/compatibility reason and must passpnpm guard. - App business logic must not know about sidecar/control-plane concepts. Keep sidecar awareness in
apps/<app>/sidecaror the desktop sidecar entry wrapper. - Shared web/daemon app contracts belong in
packages/contracts; that package must not depend on Next.js, Express, Node filesystem/process APIs, browser APIs, SQLite, daemon internals, or the sidecar control-plane protocol. - Sidecar process stamps must have exactly five fields:
app,mode,namespace,ipc, andsource. - Orchestration layers (
tools-dev,tools-pack, packaged launchers) must call package primitives; do not hand-build--od-stamp-*args or process-scan regexes. - Packaged runtime paths must be namespace-scoped and independent from daemon/web ports; ports are transient transport details only.
- Default runtime files live under
<project-root>/.tmp/<source>/<namespace>/...; POSIX IPC sockets are fixed at/tmp/open-design/ipc/<namespace>/<app>.sock.
- Git commits must not include
Co-authored-bytrailers or any other co-author metadata.
- Opening a PR uses
.github/pull_request_template.md; fill every section, not just the title. - "Why" must answer both the author's use case (what made you write this PR) and the pain being addressed (user problem, technical debt, prod issue, or unblocker), not just a one-line restatement of the title.
- "What users will see" describes the change from a user's perspective — what they click, what new thing appears, what default behavior changed — not from a code perspective.
- The Surface area checklist must reflect actual surfaces touched; check every box that applies, including extension points (
skills/,design-systems/,design-templates/,craft/), CLI flags, env vars, i18n keys, and new rootpackage.jsondependencies. - If any UI surface is checked, attach screenshots showing the entry point — where users discover the change — not just the feature in isolation; before/after is best for behavior changes.
- For bug-fix PRs, link the red-spec test that reproduces the bug and confirm it went red on
mainand green on the branch, per theBug follow-up workflowsection above. CONTRIBUTING.mdcovers PR scope, title format, dependency policy, and the issue-first rule for non-trivial features;docs/code-review-guidelines.mdis the reviewer-facing complement.
- Use
docs/code-review-guidelines.mdas the repository-wide review standard. That document is the operational guide; thisAGENTS.mdis the source of truth when the two disagree. - Walk reviews top-down through
docs/code-review-guidelines.md: Product relevance test → forbidden surfaces → ownership/scope → matching lane → checklist → comments → approval bar. - Pick the matching review lane: default code/tests, contract and protocol changes, design-system additions, skill additions, or craft additions.
- Before reviewing changes under
apps/,packages/,tools/, ore2e/, read that directory'sAGENTS.mdand apply its local boundaries. - Blocking review feedback should focus on correctness, security/secrets, data integrity, repository boundary violations, contract/migration breakage, missing required validation, or high-risk maintainability issues.
- Only maintainers may close a PR instead of requesting changes, and only when the change is not salvageable on the existing branch (wrong target product, foreign test harness, DOM/API assumptions absent from this repo, or scripts that conflict with lifecycle rules).
pnpm tools-pr is the maintainer-only control plane for PR-duty work on this repo. It is a thin gh wrapper that encodes repo-specific knowledge — review-lane derivation, forbidden-surface flags, per-lane checklists, validation-command suggestions, and a fixed dictionary of factual classify tags (bot-only-approval, needs-rebase, stale-approval, unresolved-changes-requested, awaiting-* timing, org-member, etc.). The tool is read-only on the PR surface: it never approves, merges, comments, or closes; those side effects stay in explicit gh invocations the maintainer runs.
Common subcommands:
pnpm tools-pr list— triage the open queue by lane and review-state bucket.pnpm tools-pr view <num>— factual review brief for a single PR.pnpm tools-pr classify --all— script-level tag JSON for the whole open queue (entry point for cron / digest consumers); per-PRclassify <num>for spot checks.pnpm tools-pr assignment— assigner-perspective ownership + idle-time / blocker view across the queue.
For the full tag dictionary, operational playbook (direct merge / duplicate-title / awaiting-author / org-member / agent-review flows), comment templates, language-detection rules, and tool-design constraints (precision boundaries, factual-output rule, retry + pagination strategy), see tools/pr/AGENTS.md.
- After package, workspace, or command-entry changes, run
pnpm installso workspace links and generated dist entries stay fresh. - Before marking regular work ready, run at least
pnpm guardandpnpm typecheck, plus the package-scoped tests/builds that match the files changed. Do not use or add rootpnpm test/pnpm buildaliases. - For local web runtime loops, prefer
pnpm tools-dev run web --daemon-port <port> --web-port <port>. - On a GUI-capable machine, validate desktop by running
pnpm tools-dev, thenpnpm tools-dev inspect desktop status. - Stamp/namespace changes must validate two concurrent namespaces and run desktop
inspect evalplusinspect screenshotfor each namespace. - Path/log changes must run
pnpm tools-dev logs --namespace <name> --jsonand confirm log paths are under.tmp/tools-dev/<namespace>/....
The following is a working playbook for routine bug follow-ups, distilled from recent practice. Treat it as a default action shape, not a contract — production reality always has edges these bullets can't anticipate, so use judgment when the situation doesn't fit cleanly.
- Lead with a red spec. Default to encoding the bug as a falsifiable test that goes red before any source change, so the fix is anchored in observable behavior rather than source-code intuition. If a red spec can't be written cheaply, that's usually a signal to clarify scope rather than push forward on a guess.
- Try the cheapest layer first. Reach for the lightest test layer that can still see the symptom (e2e Vitest at the daemon HTTP boundary → app-local Vitest → Playwright UI → platform-native harnesses), and drop down only when the cheaper layer can't.
- Hold the spec's scope. Defects discovered outside the bug's described boundary belong in a follow-up — their own red spec, their own PR — not in this fix. List them in the PR body's "Adjacent issues" section with the rationale and move on.
- Let the fix read as an invariant. Prefer a named helper whose docblock describes what must hold over a bolt-on
ifguard with apologetic history-comments. The call site should read as intent. - Diff against the baseline. When neighboring suites have pre-existing failures, stash or check out upstream before claiming "no new failures."
- Link the issue from the PR body. Use
Fixes #N/Closes #N/Resolves #Nso the issue auto-closes on merge and the release-time reverse lookup (gh issue view N --json closedByPullRequestsReferences→git tag --contains <merge sha>) actually has a chain to follow. The repo's PR template prompts for this; deleting the prompt is fine when the PR genuinely closes nothing. - Stage human verification for visible bugs. When the symptom needs an eye to confirm — UI, platform-native behavior, animations, race conditions a unit test can't see — green specs alone aren't acceptance. Stand up a buggy-vs-fix comparison the reviewer can drive themselves (typical shape: two namespaced runtimes, one on
main, one on the fix branch), and seed any required data only through production HTTP APIs; source-level test backdoors invalidate the verification because they prove a fake flow rather than the real one.
For a worked example of one full loop (red e2e spec → fix → green), see e2e/tests/dialog/stop-reconciles-message.test.ts (issue #135).
pnpm install
pnpm tools-dev
pnpm tools-dev start web
pnpm tools-dev run web --daemon-port 17456 --web-port 17573
pnpm tools-dev status --json
pnpm tools-dev logs --json
pnpm tools-dev inspect desktop status --json
pnpm tools-dev inspect desktop screenshot --path /tmp/open-design.png
pnpm tools-dev stop
pnpm tools-dev checkpnpm guard
pnpm typecheckpnpm tools-pr list
pnpm tools-pr list --bucket=merge-ready,approved-blocked
pnpm tools-pr list --lane=skill,contract --json
pnpm tools-pr view 1180
pnpm tools-pr view 1180 --jsonpnpm --filter @open-design/web typecheck
pnpm --filter @open-design/web test
pnpm --filter @open-design/web build
pnpm --filter @open-design/daemon test
pnpm --filter @open-design/daemon build
pnpm --filter @open-design/desktop build
pnpm --filter @open-design/tools-dev build
pnpm --filter @open-design/tools-pack build
pnpm --filter @open-design/tools-pr buildpnpm tools-pack mac build --to all
pnpm tools-pack mac install
pnpm tools-pack mac cleanup
pnpm tools-pack win build --to nsis
pnpm tools-pack win install
pnpm tools-pack win cleanup
pnpm tools-pack linux build --to appimage
pnpm tools-pack linux install
pnpm tools-pack linux build --containerizedTo avoid starting daemon, web, and desktop through inconsistent env, port, namespace, or log paths. All local lifecycle flows must go through pnpm tools-dev.
The current web runtime is apps/web. The historical apps/nextjs layout has been removed from the active repo shape; restoring it would reintroduce duplicate app boundaries and stale scripts.
Desktop queries runtime status through sidecar IPC. The web URL comes from tools-dev launch status, not from desktop guessing ports or reading web internals.
@open-design/sidecar-proto owns Open Design app/mode/source constants, namespace validation, stamp fields/flags, IPC message schema, status shapes, and error semantics. @open-design/sidecar provides only generic bootstrap, IPC transport, path/runtime resolution, launch env, and JSON runtime files. @open-design/platform provides only generic OS process stamp serialization, command parsing, and process matching/search primitives, consuming the proto descriptor.
The daemon writes .od/ by default: SQLite at .od/app.sqlite, agent CWDs under .od/projects/<id>/, saved renders under .od/artifacts/, and credentials at .od/media-config.json. Two env vars override the storage root, in order:
OD_DATA_DIR=<dir>— relocates all daemon runtime data to<dir>(used by Playwright for test isolation, and by the packaged daemon and the Home Manager / NixOS modules to point the daemon at a writable directory when the install root is read-only). The path is resolved with~/expansion and relative paths anchored to<projectRoot>.OD_MEDIA_CONFIG_DIR=<dir>— narrower override that relocates onlymedia-config.json. Same resolution semantics. Most installs do not need this; it exists for setups that want to keep API credentials in a different location from the rest of the runtime data.
Default precedence is OD_MEDIA_CONFIG_DIR > OD_DATA_DIR > <projectRoot>/.od.
Run pnpm install after changing package manifests, workspace layout, command entrypoints, bin/link-related content, or after adding/removing workspace packages.