* docs(pr): require user-perspective description and surface area The previous template asked for Summary + Validation, which encouraged code-perspective descriptions and let user-visible surface changes slip past review unnoticed. Replace with: - "Problem" — issue link + motivation - "What users will see" — first-person user-visible effect - "Surface area" — 9-item checklist (UI, shortcut, CLI/env, API/contract, extension point, i18n, top-level dependency, default behavior change, none) - "Screenshots" — required when UI surface is checked, focused on the entry point users discover rather than the feature in isolation - "Validation" — kept, retitled away from "Summary" Authoritative rules added to AGENTS.md under a new "Pull request expectations" section so external contributors' agents (Claude Code, Cursor, etc.) pick up the requirement when reading the repo. CONTRIBUTING.md gets one pointer line in "Commits & pull requests"; localized CONTRIBUTING variants (zh-CN, de, fr, ja-JP, pt-BR) are left for follow-up translation PRs per the existing docs-update workflow. The existing "Fixes #" prompt is preserved verbatim — that template addition from #1263 enforces PR-to-issue auto-linking and stays load-bearing. * docs(pr): broaden dependency surface to dev deps as well The "New top-level dependency" checkbox narrowed scope to runtime deps, but CONTRIBUTING.md L239 says "No new top-level dependencies" without limiting to runtime, and the AGENTS.md rule uses the same broad phrase. A new devDependency (tool/test/build package) belongs in the same bytes-vs-benefit explanation, so the checklist item should match. * docs(pr): add Why and Bug fix verification; scope deps to root package.json Three follow-up tweaks from review feedback: 1. Rename `## Problem` to `## Why` with a broader prompt that asks contributors to cover both their own use case (what made them write this PR today) and the pain being addressed. The old "Problem" framing only covered user-facing motivations and left no slot for the contributor's stake — a key signal for triaging external PRs. 2. New `## Bug fix verification` section between Screenshots and Validation, conditional on the PR being a bug fix. Surfaces the AGENTS.md "Bug follow-up workflow" red-spec requirement at PR-authoring time instead of leaving it implicit; asks for the test path and the red-on-main / green-on-branch confirmation. 3. Clarify the "New top-level dependency" checkbox to specify the **root** `package.json`. Without that word, contributors in a monorepo could read the check as applying to any workspace `package.json` (e.g. adding `react` to `apps/web/package.json` would be in scope) when CONTRIBUTING.md L239's "small on purpose" rule clearly meant root-level deps only. AGENTS.md `## Pull request expectations` and CONTRIBUTING.md's pointer line are updated to match the new section names and add the bug-fix red-spec expectation.
18 KiB
Directory guide
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.
Core documentation index
- 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 directories
- 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.
Inactive or placeholder directories
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.
Development workflow
Environment baseline
- 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.
Local lifecycle
- 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.
Root command boundary
- 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.
Boundary constraints
- 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 commit policy
- Git commits must not include
Co-authored-bytrailers or any other co-author metadata.
Pull request expectations
- 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.
Code review guide
- 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).
PR-duty tooling
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.
Validation strategy
- 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>/....
Bug follow-up workflow
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).
Common commands
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 check
pnpm guard
pnpm typecheck
pnpm 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 --json
pnpm --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 build
pnpm 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 --containerized
FAQ
Why is there no root pnpm dev / pnpm start?
To avoid starting daemon, web, and desktop through inconsistent env, port, namespace, or log paths. All local lifecycle flows must go through pnpm tools-dev.
Why should apps/nextjs not be restored?
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.
How does desktop discover the web URL?
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.
How are sidecar-proto, sidecar, and platform split?
@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.
Where is data written?
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.
When is pnpm install required?
Run pnpm install after changing package manifests, workspace layout, command entrypoints, bin/link-related content, or after adding/removing workspace packages.