Use pnpm (not npm or yarn). The lockfile is pnpm-lock.yaml. Install deps
with pnpm install; run scripts with pnpm run <script> or pnpm <script>.
The library is built with tsc (not vite build). vite.config.ts exists only
to configure Vitest and to run vite dev as a server for Playwright. Running
pnpm run build compiles src/ to dist/ via tsconfig.json.
tsconfig.json— library build only; includessrc/**/*tsconfig.dev.json— extends the above withnoEmit: true; addstest/**/*,vite.config.ts, andplaywright.config.tsso ESLint's type-aware rules cover all files
Do not add test files to tsconfig.json — they would be emitted into dist/.
test/node/— Vitest unit tests (pnpm run test:node); run in Nodetest/browser/— Playwright tests (pnpm run test:browser); run in real Chromium via a Vite dev server (pnpm run dev)
The dev script exists solely to give Playwright a server that can serve and
transform TypeScript source files on the fly. There is no browser app.
The package is ESM-only ("type": "module"). Local imports must use the .js
extension even though source files are .ts — e.g.
import { Example } from '../../src/index.js'. TypeScript's
moduleResolution: Bundler resolves these to the .ts source at compile time.
The current shape of the library lives in
ARCHITECTURE.md, rationale inline, updated in the
same change set that alters the shape. It is load-bearing for the
conventions below: the design gate scopes on the invariants it
documents, touches: entries name it as a deliverable, and the
breaking-release audit checks it against the code. The template copy
is a skeleton (layer map, numbered invariants, ownership heuristics,
glossary, current state labels); scaffolded repos fill it in and keep
the sections.
ARCHITECTURE.md's Glossary is the repo's vocabulary. Use its terms as
written in code identifiers, test names, docs, commit messages, and
conversation, and treat the Avoid: synonyms as banned.
Refine the glossary as you work, in the same change set that settles a term. When a term in the conversation conflicts with the glossary, say so before using it. When a term is vague or overloaded, propose one canonical term and record it. When a new module or concept needs a name the glossary lacks, add the entry. A new term that becomes a wire artifact (a field name, a log entry kind, an error name) is a wire-level convention and still needs core-contributor sign-off before it is coded; the glossary entry is written after that sign-off, not instead of it.
Skills and agent instructions written for other conventions refer to a
CONTEXT.md or CONTEXT-MAP.md glossary and a docs/adr/ directory.
In this ecosystem those map to ARCHITECTURE.md's Glossary section and
the repo's decisions/ directory. Do not create CONTEXT.md,
CONTEXT-MAP.md, or docs/adr/. The context map is the ecosystem
itself: each repo's "What lives elsewhere" and "Ownership heuristics"
sections state the relationships between repos.
All roadmap tracking lives in ROADMAP.md: narrative context
plus structured work items. Never create a parallel task list elsewhere (no
TODO.md, no task lists in other docs).
Each work item follows this schema:
- A heading
### ILT-N: Title, then a field block, then free prose context. - The prose opens with a
Context:paragraph: a few plain-language sentences stating what is wrong today, how it came about, and why it matters, readable on its own without following any file pointer. The detailed mechanics (file:line cites, stage orders, edge cases) follow in ordinary prose after it. Acceptance boxes record exit criteria, not motivation, so the Context paragraph is where a returning reader or an agent picks up the problem; on a design-gated item it also seeds the design doc's Problem-and-scope section. Items predating the convention (added 2026-08-25) are backfilled when next touched, not in bulk. - Fields:
status(todo/in-progress/draft/done),priority(high/medium/low),labels(comma-separated), optionalblocked-by(otherILT-Nids), atouches:list where the rule below applies,design:+design-approved:where the design gate below applies, and anacceptance:checklist. draftmarks items with no actionable done-state yet (blocked externally or parking records); a draft states why instead of acceptance criteria and must gain acceptance criteria when promoted totodo.touches:is required for any item that changes a spec, a wire contract, or a shared@interop/*API. It lists every affected repo AND that repo's ARCHITECTURE/AGENTS files -- the docs are entries in their own right, not an afterthought, since doc drift is what the field exists to prevent. Each entry starts unresolved and is resolved in place: marked shipped (naming what landed) or explicitly waived asunaffected: <repo> (<why>).
Rules:
- Item ids are permanent and never reused. A new item takes the next unused number, regardless of which section it lands in.
- Every non-draft item needs acceptance criteria before it may be moved to
in-progress. - Statuses are edited in place (change the
status:field); acceptance checkboxes are ticked as they are met. - An item carrying a
touches:field may not flip todonewhile any entry in it is unresolved -- an unresolved entry is unfinished work of the item itself, not a follow-up. - Completed items move verbatim (number, title, field block, prose, with
their
donedate) from ROADMAP.md to archived-roadmap.md once shipped, append-only -- this keeps ILT-N references resolvable. CHANGELOG.md remains the permanent record of what landed. Do not rewrite or summarize items on the way in, and do not fix old references. - Work discovered mid-implementation gets its own item immediately, noting
discovered-from: ILT-Nin its prose, plus ablocked-bylink if it blocks anything. - The design gate: a cross-cutting item (one that changes persistence
semantics, key custody, a ceremony's stage order, or any invariant the
repo's ARCHITECTURE.md documents) carries
design:(a doc per designs/TEMPLATE.md) anddesign-approved:(a date only core contributors set), and no implementation starts until the doc is approved. Approval extracts the design's durable decisions -- contract-binding ones, and do-not-reopen rejections of an approach -- into trackeddecisions/records. The full definition is canonical in this repo's designs/ directory. - Reference item ids in commit messages and PR descriptions where relevant.
Cross-repo decisions -- the ones whose driving roadmap item carries a
touches: field -- get a durable record in the owning repo's decisions/
directory (decisions/NNNN-slug.md). The convention and template are
canonical in this repo's decisions/ directory: required
sections Context / Decision / Consequences / Revisit Criteria, Rejected
Alternatives where applicable, records superseded in place rather than
rewritten. A pre-implementation design review may additionally mint a
record for a repo-internal do-not-reopen decision (an approach rejected
with concrete revisit criteria); other repo-internal decisions stay in
ARCHITECTURE.md prose and do not get a record. The full scope rule lives
in the decisions/ README. Within either case, a record is written only
when the decision passes the qualifying test in that README (hard to
reverse, surprising without context, a real trade-off); a skill's offer
to "write an ADR" is a proposal for such a record, pending that test and
core-contributor approval, and uses decisions/TEMPLATE.md.
The @interop/* publish convention (this section is its canonical home;
downstream repos defer here):
- The version published is the one the CHANGELOG's top entry names; its
TBDdate is replaced with the release date at publish time. - Breaking-release doc-vs-code audit. Before publishing a version whose
CHANGELOG carries a breaking entry, audit the ARCHITECTURE/AGENTS files of
the consumers named in the affected contract's "Parties to this contract"
registry (the AGENTS.md tables in app-connect-spec,
encrypted-collections-spec, and the WAS spec repo), and file roadmap items
for what the audit finds. The cheap mechanism, as run 2026-08-11: parallel
read-only agents, one per consumer repo, each checking that repo's
ARCHITECTURE/AGENTS statements against its own code and the new contract --
a recipe, not an aspiration. A consumer with nothing affected is recorded
as
unaffected: <repo> (<why>)on the driving roadmap item. - A breaking profile change also bumps the profile's version handle where the contract states one (e.g. the App Connect context URL) and the CHANGELOG names the profile version the package now speaks.
Code style, refactoring, JSDoc, comment, and error-handling conventions live in
@CONTRIBUTING.md -- follow them. That file's marked conventions block is the
canonical shared core copied across @interop/* repos; edit it there, not in
downstream copies.