Move `docs/defensive-patterns.md` into `docs/defensive-patterns/`, one page per artifact plus a README router, and state each rule as its requirement, the condition a reviewer judges it by, its boundary and approver, and a correct and a wrong example. - `general.md` (the moved page): the existing bug-class rules plus timing, value representation, shared-state ownership, decentralization, performance evidence, and migrations. - `package-surface.md`, `remote-and-api.md`, `client-components.md`, `conversation-and-session.md`: the review requirements for those artifacts, linking type definitions and semantics to `docs/subsystems/` instead of restating them. - `README.md`: the habits behind the rules and the route from artifact to page. - `dsh-code-review` reads the owning pages before judging a diff; the `dsh-architecture-discipline` routing skill is retired in favour of the directory, and every inbound reference is repaired. - Chinese counterparts, pairing records, and the documentation tier table are updated together.
6.2 KiB
6.2 KiB
AGENTS.md — Harness Packages
These package-specific rules supplement the repo-wide conventions.
- Plugin exports: service packages default-export their service class; function plugins named-export
name/inject/Config/applyand have no default export. Mixing the forms makes the Loader discard the function plugin's namespace (postmortem). - Optional services use
ctx.get(name). Reservectx.<name>for declared injections; the property proxy is topology-sensitive, while strictctx.getreads the global service store (postmortem). - Product-visible plugins require a non-unit REAL-composition test. Hand-built
ctx.plugin(...)suites are insufficient. Boot test-onlycordis.ymlthrough the Loader and app/process; mock only external services or nondeterministic inputs and assert model-visible, durable, or user-visible output. Keep opt-ins out of shipped defaults. Policy. - Initiator-owned private chains derive, then capture. Under
ctx.agents.withInitiator(), recover the Agent at each orchestration entry, deriveagent.session, and let operation-local helpers close over it. KeepAgentandSessionexplicit at lifecycle, session-log, service, authority, worker/process, persistence, and wire interfaces; do not widen a leaf helper fromSessiontoContextmerely to hide a parameter (rationale). - Represent one asynchronous operation with one lifecycle controller or transaction. Separate readiness, cancellation, disposal, reservation, or sentinel state requires an independent owner or settlement point; otherwise fold it while preserving rollback, callback containment, and quiescence.
- Design Service Definitions for all current Consumers. Keep tool-schema, Loader, UI, transport, and provider-specific behavior in the Consumer or provider; do not let one Consumer dictate the service contract (capability-seam rationale). Inverse smell: a public service method with one internal caller — pass a private capability closure instead (
RunCodeBridgeOptions). - Require a current owner and need. Tie each abstraction, state machine, option, defensive copy, and compatibility path to a current contract or production consumer, and keep behavior in its owning plugin or service.
- Require evidence for public choices. Configurability does not justify an unsupported default, public operation set, format, or imported external concept. Use current-consumer evidence or relevant prior art; otherwise require an explicit value or defer the choice.
- Give shared state an owner. Hang shared state on a service or instance. A module-level cache, a singleton, a global
WeakMap, or aSymbol.for/globalThis/window.__X__bypass hides that owner; add no new globalcurrent,active, orselectionsingleton, and derive context from the current scope instead. - Keep business rules in their own package. A runtime, controller, manager, gateway, or API dispatch layer carries generic orchestration; a business branch belongs to an independent package and attaches through a registration surface or projected data (where new behavior goes).
- Write model-facing contracts from the model's perspective. Prompts, tool schemas, results, and diagnostics contain only task-relevant concepts, not UI, transport, or implementation vocabulary. Pin stable model-visible text verbatim and dynamic behavior through snapshots or end-to-end coverage.
- Enforce a decision in the operation that makes it. Schema omission, prompt filtering, facades, wrappers, and listener order are not enforcement when direct or alternate callers can bypass them; test denial through the executor.
- Publish state only at its commit point. Emit each notification and update derived state only after the operation succeeds; derive caches, prompts, UI echoes, replay, and query views from one authoritative source.
- Apply bounds to the complete result. Enforce byte, token, item, and time limits where the complete emitted or retained value, including wrappers and metadata, is known; test tiny and exact limits, oversized single chunks, and multibyte byte limits.
- Registry contributions prove disposal through the HMR-safety test required by testing policy: dispose the fiber and observe removal.
- Specs run concurrently in forked workers beside other gate processes. Own each acquired port, path, and child process through teardown; a spec that passes only when run alone is a defect in the spec (execution model).
- Package tsconfig: extends
tsconfig.base.json(Client:tsconfig.base.client.json), setsrootDir: srcandoutDir: lib/types, references workspace dependencies, and registers in one aggregate. Packages with distinct Host and Client compiler faces usetsconfig.host.jsonandtsconfig.client.jsonleaves plus a solution-only root; ordinary two-entry Client plugins do not split (layout). src/types.tscontains only types — no runtime code.- Tests live at package level under
tests/, notsrc/__tests__/. - Update package README and JSDoc contracts in the same commit as behavior, and verify them against code with dsh-prose-standard. Group READMEs declare subsystem ownership through a canonical English page link or justified exemption.
- Package READMEs document model, token, and KV-cache effects using the canonical Model Experience format.
- Package READMEs put durable consumer gaps and non-obvious maintainer constraints under
## Known Limitations and Deferred Work; ordinary cleanup stays in its TODO or Agent Note. Packages with none use a justified allowlist entry.