Tool Conventions
The contract every packages/ tool follows so ~20 independently built CLIs feel like one product.
Tool Conventions
Every tool under packages/ follows the same rules. They are the contract that makes a
suite of independently built CLIs feel like one product. The canonical source is
CONVENTIONS.md at the
repo root; this page summarizes it.
Layout
Each tool is a self-contained package:
packages/<name>/
├── package.json # standard template
├── bin/<name>.mjs # CLI entry — thin: parse args, call lib, exit
├── lib/*.mjs # logic — pure functions wherever possible
├── test/*.test.mjs # node:test — MUST pass offline (no network, no API keys)
└── README.md # what it does, usage, exit codes, ADLC phase it servesThe package.json is uniform: @adlc/<name>, "type": "module", a single bin entry,
"test": "node --test test/*.test.mjs", "engines": { "node": ">=18" }, MIT license.
Hard rules
- Zero runtime dependencies. Node 18+ built-ins and
@adlc/coreonly. Core is imported by relative path (../../core/index.mjs). - Core is frozen. Never edit anything under
packages/core/. If core lacks something, implement it locally in yourlib/and note the gap in your README under "Core gaps." - Scope discipline. Write ONLY inside your own
packages/<name>/. Never touch other packages,ADLC.md, root files, or.adlc/. - Exit codes are the contract.
0= gate passes,1= operational error,2= gate fails, produced withpass/opError/gateFailfrom core. CI gating depends on this. See Exit Codes. --prompt-onlyon every LLM-backed tool. Print the exact prompt(s) and exit0, so the tool is usable with zero API keys. Paste the prompt into any harness. UsespromptOnly()from core.--jsonon every tool. Machine-readable output for orchestrators, alongside the default human-readable output.- Tests run offline and leave no trace. Test pure logic with fixtures; use
mkdtempSynctemp dirs (with scratch git repos inside them when git behavior is under test) and always clean up. Never call LLM providers in tests. - Never mutate the working tree without a flag. Writing tools (e.g.
--write,--append) default to dry-run reporting. Mutation-testing tools restore files in afinallyblock and refuse to run on a dirty tree. - No silent error swallowing. Operational failures become
opErrorwith a clear message; partial data (e.g. skipped ledger lines) is surfaced in the output. - File size. Keep files under 400 lines; split
lib/by concern. - The README is part of the tool. It documents usage examples, every flag, exit-code semantics, which ADLC phase (P0–P7 / D1–D3) the tool serves, and its relationship to sibling tools.
Shared data: read via core, never reinvent
Tools never re-parse shared state; they go through @adlc/core:
| Data | Location | Access |
|---|---|---|
| Tickets | .adlc/tickets.json | loadTickets() (schema in packages/core/lib/tickets.mjs) |
| Ledgers | .adlc/<name>.jsonl | appendEntry / readEntries |
| Foundation rails | rails paths on a ticket | read-only to builders; enforced by tools that read them |
Well-known ledgers are manifest (gate-manifest entries) and findings (prosecution
findings). See The .adlc/ Runtime for the full layout.
CLI shape
Tools share a verb-and-flags shape (<name> [verb] [--flags]) and a common flag
vocabulary where applicable: --base <ref> (git base, default HEAD), --tickets <path>
(default .adlc/tickets.json), --json, --prompt-only, --n <int> (fan width), and
--tier cheap|mid|frontier.