Document the package hierarchy and finalize the RFC
Add a README to each group dir (core/llm/bash/session-persistence/ui/ support) stating its role and product-vs-support classification, and rewrite packages/README.md around the hierarchy (group table, grouped "what goes where", removed the package-hierarchy FIXME). Move the package-hierarchy RFC to implemented/architecture/ and rewrite it to describe what shipped (placement rationale, the paths-wildcard and publint dedup, the two new guardrail gates). Fold the remaining tsconfig.build.json references dedup into the discover-package-inventory proposal and fix its cross-link. Update AGENTS.md: regrouped repo-layout map, depth-2 globs, the new verify-package-paths gate in the doc-sync listing, and a note that we lean toward stricter lint in the agentic-coding era (machine-caught errors and a consistent foundation outweigh the one-time cost).
This commit is contained in:
@@ -64,7 +64,6 @@ Do NOT write one for a mechanical or local choice (a variable name, a one-file r
|
||||
| [Runtime schemas for the event vocabulary (Zod vs the merge-extensible-map pattern)](proposed/architecture/2026-06-16-typed-event-schemas.md) | 2026-06-16 |
|
||||
| [Extract a generic long-running tool runtime](proposed/architecture/2026-06-20-generic-long-running-tool-runtime.md) | 2026-06-20 |
|
||||
| [Make the shared example base providerless](proposed/architecture/2026-06-20-providerless-example-base.md) | 2026-06-20 |
|
||||
| [Reorganize packages into a modular hierarchy](proposed/architecture/2026-06-20-package-hierarchy.md) | 2026-06-20 |
|
||||
|
||||
### Process
|
||||
|
||||
@@ -115,6 +114,7 @@ Do NOT write one for a mechanical or local choice (a variable name, a one-file r
|
||||
| [Every session event is enclosed in a turn](implemented/architecture/2026-06-15-turn-enclosure-invariant.md) | 2026-06-15 |
|
||||
| [Shared persistence write coordinator](implemented/architecture/2026-06-18-shared-persistence-write-coordinator.md) | 2026-06-18 |
|
||||
| [Agent lifecycle and ownership seams](implemented/architecture/2026-06-18-agent-lifecycle-and-ownership-seams.md) | 2026-06-18 |
|
||||
| [Reorganize packages into a modular hierarchy](implemented/architecture/2026-06-20-package-hierarchy.md) | 2026-06-20 |
|
||||
|
||||
### Process
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# RFC: Reorganize packages into a modular hierarchy
|
||||
|
||||
Status: implemented
|
||||
|
||||
## Problem
|
||||
|
||||
`packages/` was flat: 18 packages all sat at `packages/<name>/`, so a package's location said nothing about whether it was core product API, a swappable capability seam, a provider adapter, a product integration, or example/test support. The package README carried a `FIXME(package-hierarchy)` and `scripts/publint-all.ts` a `TODO(package-inventory)` flagging exactly this. Core packages, provider integrations, capability seams, example UI support, and snapshot-only replay support all looked equally foundational.
|
||||
|
||||
This was not just cosmetic. Because every top-level package looked like part of the same public surface, future removal was harder, and publish/lint/doc scripts had to encode intent through comments or hand-maintained static lists rather than reading it off the layout.
|
||||
|
||||
## What landed
|
||||
|
||||
Packages are grouped by modular role at a uniform `packages/<group>/<pkg>/` depth. Group directories are pure containers (no `package.json`); every package keeps its `@deepseek-ai/dsh-<pkg>` name — this is repo structure and maintenance policy, not package renaming.
|
||||
|
||||
```text
|
||||
packages/
|
||||
core/ (product API spine)
|
||||
session/
|
||||
system-prompt/
|
||||
tools/
|
||||
agent/
|
||||
agent-loop/
|
||||
llm/ (product — capability family)
|
||||
llm/
|
||||
llm-deepseek/
|
||||
llm-pi-ai/
|
||||
bash/ (product — capability family)
|
||||
bash/
|
||||
bash-local/
|
||||
tool-bash/
|
||||
session-persistence/ (product — capability family)
|
||||
session-persistence/
|
||||
session-persistence-jsonl/
|
||||
session-persistence-sqlite/
|
||||
ui/ (product integration)
|
||||
acp/
|
||||
support/ (dev/test/example infrastructure)
|
||||
invariants/
|
||||
ui-stdio/
|
||||
llm-replay/
|
||||
```
|
||||
|
||||
### Placement decisions
|
||||
|
||||
- **Same-name nesting for capability families.** A family's interface package sits at `packages/<group>/<group>/` (`llm/llm`, `bash/bash`, `session-persistence/session-persistence`), with implementations and consumers as flat siblings. There is no extra `adapters/`/`impls/` sub-tier — every package is exactly depth 2, which keeps the workspace glob a clean `packages/*/*` and lets one `@deepseek-ai/dsh-*` tsconfig wildcard resolve every package (unique dir names make first-on-disk-wins unambiguous).
|
||||
- **`session` stays in `core/`; persistence is its own family.** The session log is core product API. Its storage backends form a parallel capability family (`session-persistence/`) mirroring `llm/` and `bash/`, rather than nesting under `core/session/`.
|
||||
- **`agent-loop` is in `core/`.** It is the one concrete implementation of the `agent` seam, but it ships as the harness's default product loop, so it lives with the core spine. Plugins still depend on the `agent` vocabulary, never on `agent-loop`, so the loop stays swappable.
|
||||
- **`invariants` and `ui-stdio` are `support/`, not product.** `invariants` is dev-mode contract checking. `ui-stdio` was extracted from the examples for reuse and the coverage gate — it is example-coupled, so it sits in `support/` alongside `llm-replay` (the snapshot-test replay adapter). `acp` is the only `ui/` member because it is a real product surface (the ACP bridge an editor drives), structurally distinct from the readline demo helper.
|
||||
|
||||
### Deduplicating the package lists
|
||||
|
||||
The package list had been enumerated in five places. The uniform depth-2 layout collapsed most of them:
|
||||
|
||||
- `tsconfig.base.json` and `tsconfig.typecheck.json` each replaced their 18 per-package `paths` entries with a single `@deepseek-ai/dsh-*` wildcard listing one candidate per group.
|
||||
- `scripts/publint-all.ts` derives its list by reading the hierarchy (`packages/<group>/<pkg>`), resolving the `TODO(package-inventory)`.
|
||||
- `tsconfig.build.json`'s project `references` stay an explicit list — TypeScript project references have no wildcard form. Generating these from a manifest is left to a follow-up (see [discover package inventories](../../proposed/process/2026-06-20-discover-package-inventory.md)).
|
||||
|
||||
### Guardrails added
|
||||
|
||||
Two doc-sync/hygiene gates keep the structure and its references honest, so the manual checks this restructure required do not have to be repeated by hand:
|
||||
|
||||
- `scripts/verify-package-paths.ts` flags a `packages/<path>` reference (in Markdown or a `.ts` comment/string) that does not resolve **and** names a real package in a segment — i.e. a stale path to a moved package. A path naming a package that exists nowhere (a forward-looking proposal) is left alone, so the gate applies uniformly across proposed/implemented/rejected.
|
||||
- `scripts/check-workspace-constraints.ts` asserts the `packages/<group>/<pkg>` shape: group dirs carry no `package.json`, and no package sits flat at the root or nests deeper. Group names stay open — a new group may be added without editing the gate; only the depth-2 shape is fixed.
|
||||
|
||||
## What we gave up
|
||||
|
||||
The restructure churned imports, workspace globs, doc links, build references, and package paths in one coordinated move. That churn is acceptable pre-release (per the AGENTS.md foundation-over-blast-radius stance) because it stops the flat layout from fossilizing support packages as product contracts, and it is a one-time cost: the wildcard `paths`, the glob-derived publint list, and the shape gate mean a new package needs no further structural edits.
|
||||
@@ -1,59 +0,0 @@
|
||||
# RFC: Reorganize packages into a modular hierarchy
|
||||
|
||||
Status: proposed
|
||||
|
||||
## Problem
|
||||
|
||||
`packages/` is flat. Core product packages, provider integrations, capability seams, example UI support, and snapshot-only replay support all sit at the same level and look equally foundational. The [package README](../../../../packages/README.md) already has a `FIXME(package-hierarchy)` noting that `ui-stdio` and `llm-replay` were extracted from examples mostly for reuse and coverage. The flat layout makes support packages appear more product-shaped than they are and forces publish/lint/doc scripts to encode intent through comments or static lists.
|
||||
|
||||
This is not just cosmetic. A package's location currently says little about whether it is core API, a swappable capability, an adapter integration, an example harness helper, or test infrastructure. That makes future removal harder because every top-level package looks like part of the same public surface.
|
||||
|
||||
## Proposal
|
||||
|
||||
Move packages into a deliberate hierarchy under `packages/`. The exact layout is deferred to the implementing PR, but it should group packages by modular role rather than keep every package at one flat level.
|
||||
|
||||
One plausible shape:
|
||||
|
||||
```text
|
||||
packages/
|
||||
core/
|
||||
session/
|
||||
system-prompt/
|
||||
tools/
|
||||
agent/
|
||||
agent-loop/
|
||||
invariants/
|
||||
llm/
|
||||
llm/
|
||||
adapters/
|
||||
llm-deepseek/
|
||||
llm-pi-ai/
|
||||
bash/
|
||||
bash/
|
||||
bash-local/
|
||||
tool-bash/
|
||||
session-persistence/
|
||||
session-persistence/
|
||||
session-persistence-jsonl/
|
||||
session-persistence-sqlite/
|
||||
acp/
|
||||
support/
|
||||
ui-stdio/
|
||||
llm-replay/
|
||||
```
|
||||
|
||||
The final implementation may choose different names or groupings, but it should keep the same intent: core APIs, package families such as LLM/bash/session persistence, standalone integrations such as ACP, and support/test/example packages are distinguishable from the filesystem alone. Npm package names can stay `@deepseek-ai/dsh-*`; the hierarchy is about repo structure and maintenance policy, not public package renaming.
|
||||
|
||||
This proposal does not delete `llm-replay` or `ui-stdio` by itself. It makes their status honest: either they graduate into product packages with documented consumers, or they live under a support/testing/example classification where release and compatibility expectations are lower.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- Packages move from the flat `packages/<name>/` layout into a documented modular hierarchy.
|
||||
- The implementing PR chooses the exact hierarchy and updates workspace globs, TypeScript paths, package docs, generated module graphs, `cordis.yml` package paths, build scripts, and publish/lint scripts in one coordinated move.
|
||||
- Scripts that publish, lint publishability, or generate package inventories use the hierarchy instead of an ad hoc static list where the hierarchy is enough to express the policy.
|
||||
- Docs explain which package groups are part of the product API and which groups are support/test/example infrastructure.
|
||||
- New package guidance tells authors where to place a package and discourages new one-off top-level groups.
|
||||
|
||||
## What we give up
|
||||
|
||||
The restructure churns imports, workspace globs, docs links, and package paths. That churn is acceptable pre-release if it prevents the flat layout from fossilizing support packages as product contracts.
|
||||
@@ -4,20 +4,22 @@ Status: proposed
|
||||
|
||||
## Problem
|
||||
|
||||
Package and gate inventories are repeated by hand. [scripts/publint-all.ts](../../../../scripts/publint-all.ts) has a static list of publishable packages. The [package cookbook](../../../cookbook/adding-a-package.md) tells authors to update several files. The [package README](../../../../packages/README.md) carries a hand-written dependency graph. [CI](../../../../.github/workflows/ci.yml) and [development docs](../../../development.md) can drift from the actual `doc-sync` subcommands when new gates are added. These lists are small today, but every new package or gate creates another manual synchronization point.
|
||||
Package and gate inventories are repeated by hand. The [package cookbook](../../../cookbook/adding-a-package.md) tells authors to update several files. The [package README](../../../../packages/README.md) carries a hand-written dependency graph. [CI](../../../../.github/workflows/ci.yml) and [development docs](../../../development.md) can drift from the actual `doc-sync` subcommands when new gates are added. `tsconfig.build.json` lists all 18 packages as explicit project `references`. These lists are small today, but every new package or gate creates another manual synchronization point.
|
||||
|
||||
The [package hierarchy](../../implemented/architecture/2026-06-20-package-hierarchy.md) already removed several of these by hand: `scripts/publint-all.ts` now derives its list from the `packages/<group>/<pkg>` layout, and the two `tsconfig` `paths` maps collapsed to one `@deepseek-ai/dsh-*` wildcard. What remains is the inventory that cannot be globbed away — chiefly `tsconfig.build.json`'s project `references`, which TypeScript requires as an explicit array (no wildcard form).
|
||||
|
||||
Static lists are appropriate when they encode policy; they are needless friction when they duplicate manifest data or layout facts that already exist in `package.json`, workspace globs, or the package hierarchy.
|
||||
|
||||
## Proposal
|
||||
|
||||
Make package/gate inventories discoverable. Publishability should come from the deliberate [package hierarchy](../architecture/2026-06-20-package-hierarchy.md) plus package manifests, not from a static array in a script or the npm `private` flag. Module graph generation should read package manifests. `doc-sync` should be the one command that defines and prints its sub-gates, with docs linking to that command rather than restating a second list.
|
||||
Make the remaining package/gate inventories discoverable. A single canonical source — the `packages/<group>/<pkg>` hierarchy plus package manifests — should drive `tsconfig.build.json`'s `references`, the module graph, and any other full-package list, with a generate-and-verify step (the existing `gen-module-graph` / `gen-cordis-catalog` pattern: a generator writes the artifact, a `--check` mode in `hygiene`/`doc-sync` fails on a stale committed copy). Module graph generation already reads package manifests. `doc-sync` should be the one command that defines and prints its sub-gates, with docs linking to that command rather than restating a second list.
|
||||
|
||||
The hierarchy does not need to encode every fact about a package, but it should encode the broad maintenance policy: core/product packages, integrations, capability seams, and support/test/example packages should not all require a hand-maintained exception list before scripts can tell them apart.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- `publint-all` discovers publishable packages from the hierarchy plus manifests instead of a hard-coded array.
|
||||
- Adding a package does not require editing a static package list for every gate.
|
||||
- `tsconfig.build.json` project `references` are generated from the hierarchy (a generator emits them; a `--check` gate fails when the committed copy is stale), rather than hand-maintained.
|
||||
- Adding a package does not require editing a static package list for any gate.
|
||||
- Docs describe the source of truth rather than repeating generated inventories.
|
||||
- CI invokes the aggregate commands and lets those commands own their sub-gate lists.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user