From 6d454ee81973c78d5de5d5442938793c13435c16 Mon Sep 17 00:00:00 2001 From: DanMat Date: Sat, 1 Aug 2026 11:31:32 -0400 Subject: [PATCH] docs: bring README + ROADMAP up to date with 3.0/3.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README: regenerate the reference tables — they were missing the fullstack preset (fs/app) and the --monorepo-layout option (both shipped 2.9). Preset count corrected 18 → 17. - ROADMAP: check off items that shipped but were left open — type declarations (3.0), collision diagnostics (3.0 detection + 3.1 enforcement); note the diagnostics half of staged resolution and the embedded half of writer path-validation are done; ARCHITECTURE.md now exists; add lint-staged to the held-back deps and point at the catalog. Co-Authored-By: Claude Opus 4.8 --- ROADMAP.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 2b6d984..25fe7eb 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -23,7 +23,7 @@ Planned and considered features for Packkit. Not commitments — a backlog to pu - Package managers: npm · pnpm · yarn · bun **Surfaces & DX** -- CLI wizard + 18 presets + short aliases + full non-interactive flag parity +- CLI wizard + 17 presets + short aliases + full non-interactive flag parity - One-command scaffold (`--recommended`; flags imply non-interactive) + `--from` profiles - Web configurator: client-side zip download + reproducible one-shot command + preset gists - Agent-native: `--schema` (JSON), `llms.txt`, and an **MCP server** (`packkit-mcp`) @@ -31,7 +31,7 @@ Planned and considered features for Packkit. Not commitments — a backlog to pu **Staying honest** - **Template-dependency freshness** — weekly Action flags any version we write into generated projects that falls a major behind (what Dependabot can't see) and opens/auto-closes an issue - **End-to-end integration matrix** — generates every preset + tooling variant, installs, and runs its real checks (build/test/lint, starts services) on any change to generation logic or deps -- **All template deps refreshed to current** (Vite 8, Vitest 4, ESLint 10, Storybook 10, React 19, jsdom 29, Biome 2, Jest 30, …) — verified green by the matrix. Intentional holds: `typescript` (5.9, pending typescript-eslint TS 7 support) and `knip` (5, oxc-parser native-binding crash in 6). +- **All template deps refreshed to current** (Vite 8, Vitest 4, ESLint 10, Storybook 10, React 19, jsdom 29, Biome 2, Jest 30, …) — verified green by the matrix, and since 3.1 they live in one catalog (`src/core/versions.js`). Intentional holds (`HELD` in that file): `typescript` (5.9, pending typescript-eslint TS 7 support), `knip` (5, oxc-parser native-binding crash in 6), and `lint-staged` (16, v17 needs Node >=22.22.1, above the Node 22 floor). **On defaults:** tsup + ESLint/Prettier + Vitest + Changesets stay the conservative, best-supported defaults; tsdown/Biome/oxlint are one click away. @@ -53,7 +53,7 @@ Planned and considered features for Packkit. Not commitments — a backlog to pu ### Layering (from the 3.0 approval review) _3.0 turned the embedded API into Packkit's public programmatic surface. Keeping the layers distinct is now an explicit design rule, not an accident of the current code._ -**Architecture principles** — worth writing into a short `ARCHITECTURE.md`: +**Architecture principles** — now written down in [ARCHITECTURE.md](ARCHITECTURE.md) (shipped 3.1): 1. The core never performs side effects. 2. The embedded API is the only supported programmatic surface. 3. The CLI is implemented on top of the embedded API. @@ -93,19 +93,19 @@ _First unsolicited review from an agent that used Packkit to build a real intern ## From architecture review (2026) _Second external review, this one of the codebase rather than the experience. Ratings: architecture 8.5, engineering 8, product 7, OSS readiness 8, maintainability 7. Every claim below was checked against the code before being written down._ -**The theme worth acting on: Packkit doesn't eat its own cooking.** It generates `publint` + are-the-types-wrong checks, strict TypeScript, and lint gates for other people's packages, then ships without any of them. `npm run check:pkg` is something Packkit writes for you, not something it runs on itself — and its own package would fail it. +**The theme: Packkit didn't eat its own cooking.** It generates `publint` + are-the-types-wrong checks, strict TypeScript, and lint gates for other people's packages, then shipped without any of them. Largely addressed in 3.0/3.1 — it now ships type declarations and lints its own source (below). -- [ ] **Type declarations for the public API** — `exports` exposes `.`, `./core`, `./cli`, `./scaffold`, with **no `types`**. Programmatic consumers and the MCP server get nothing. JSDoc + `tsc --declaration` is enough; a rewrite isn't needed. Types wanted for `PackkitConfig`, the resolved config, `GenerateResult`, the file map, preset names, and scaffold results. **v3 requirement.** +- [x] ~~**Type declarations for the public API**~~ — **Shipped (3.0).** `types/` (`index`/`core`/`embedded`/`writer`), a `types` entry, and per-export `types`. Covers `PackkitConfig`, `ResolvedPackkitConfig`, `GeneratedProject`, the file map, diagnostics, the deployment contract, definitions, and the writer result. Verified resolvable from a packed install. - [x] ~~**Self-enforcement**~~ — **Shipped (3.1).** Packkit now lints its own source with the stack it generates for users (eslint flat config + `@eslint/js`): `npm run lint`, a `check` script (lint + test), and a CI lint job. It immediately earned its keep — caught a real duplicate `minify` key in the CLI arg parser plus several dead vars/imports, all fixed. Full `tsc --checkJs` was assessed and deferred: 95 errors, nearly all the incremental-object-literal pattern (noise), not worth annotating around right now — though it did surface the dead `reactEntry` reference removed in the CLI-on-embedded PR. -- [ ] **Validate generated paths at the writer** — `writeProject` joins each relative path onto the target and writes it. Fine while every feature is trusted first-party code; a security boundary the moment third-party features, community recipes, or MCP-supplied data can contribute paths. Reject absolute paths, `..` escapes, and post-normalization collisions **in the writer**, not per feature — before extensibility ships, not after. -- [ ] **Collision diagnostics** — `deepMerge` ends in `return source`, so when two features set the same `scripts.build` or `exports` key the last one silently wins. Additive fields (dependencies) should keep merging; `scripts`, `exports`, `bin`, `files` should report which features collided. Same for two features emitting the same file path. +- [ ] **Validate generated paths at the writer** — **done for the embedded writer (3.0/3.1):** `writeGeneratedProject` rejects absolute paths, `..` escapes, null bytes, Windows-reserved names, case-insensitive collisions, and (3.1) paths that resolve through a symlink — validated both when a project is assembled and again at the write boundary; the CLI now writes through it. What's still on the older, looser path: `writeProject` in `src/cli/write.js`, which the **MCP server** uses directly, doesn't run these checks. Route MCP's writes through the embedded writer (or apply the same validation there) before any third-party/MCP-supplied paths are trusted. +- [x] ~~**Collision diagnostics**~~ — **Shipped (3.0 detection + 3.1 enforcement).** The embedded API surfaces `FEATURE_FILE_COLLISION` and `PACKAGE_FIELD_CONFLICT` diagnostics (with each feature named), dependency conflicts are keyed per section, and `deepMerge` now refuses prototype-pollution keys. 3.1's `test/feature-registry.test.js` turns it into an enforced invariant — no two features may collide across the matrix — which caught and fixed a real `scripts.dev` collision on `node-service`. - [ ] **Structured subprocess errors** — `run()` returns a boolean and discards stdout/stderr when quiet, so "install failed" can't distinguish a missing executable from a network error, an unconfigured git identity, or a rejected push. Return `{ ok, command, exitCode, stdout, stderr, category }`; the CLI can still print something friendly, while MCP callers get something actionable. - [ ] **Windows command-execution tests** — `run()` and `capture()` both set `shell: true` on win32, where quoting and metacharacter handling change. Test repo slugs, descriptions, remote URLs, and paths containing spaces, `&`, quotes, pipes, parens, and Unicode. Prefer resolving the executable (or `.cmd`) over enabling the shell globally. - [ ] **Test the packed tarball** — `npm pack`, install the tgz, then check every export path resolves, the shebang and exec bit survive, only intended files ship, and the core stays browser-safe. The `./scaffold` export added in 2.8 makes this sharper: it pulls `node:child_process` into the published surface. ### Architecture, once the gates are in -- [ ] **Staged resolution with diagnostics** — `normalizeConfig` now applies defaults, coerces conflicts, derives helpers, and encodes framework/target policy in one pass, and **silently** disables what it can't support. Split into ordered stages returning `{ config, changes[], warnings[], errors[] }` so "we turned Storybook off because this isn't a component library" is reported rather than inferred. +- [ ] **Staged resolution with diagnostics** — the **diagnostics half shipped (3.0)**: `normalizeConfig` takes a collector and records every coercion it makes (previous → resolved value, with a code), which the embedded API and CLI surface — "Storybook was disabled because this isn't a component library" is now reported, not inferred. What's left is the structural half: splitting the single pass into ordered, named stages (defaults → validate → target rules → framework rules → …) each returning its own diagnostics. Lower priority now that the reporting exists. - [x] ~~**A resolved domain model**~~ — **Shipped (3.1).** `normalizeConfig` now attaches a grouped `cfg.resolved` view (`targets` / `language` / `framework` / `build` / `module` / `package`) so consumers can read structured state (`cfg.resolved.targets.app`) instead of re-interpreting raw selections; the deployment contract already does. The reviewer's real worry — "flags can in principle contradict" — is now **proven impossible**: `test/invariants.test.js` asserts across a broad matrix that the grouped view never disagrees with the flat flags and that the derived state is internally consistent (publishable ⟺ its definition, an app is always ESM and never publishable, at most one framework, gated options off when their precondition fails, …). A future `normalizeConfig` change that introduces a contradiction fails there. Deliberately *not* done: a mechanical rewrite of every `cfg.hasApp` reference to `cfg.resolved.targets.app` — it's byte-safe but pure cosmetic churn; the flat flags stay supported and the invariants test guarantees they can't drift. - [x] ~~**Make feature ordering explicit**~~ — **Shipped (3.1).** Rather than build speculative phase/requires/conflictsWith machinery, the real risk — order silently deciding a collision — is now an enforced invariant: `test/feature-registry.test.js` asserts every feature has a unique id and that **no two features collide** on a file or package.json field across the full config matrix (1683 configs). That immediately surfaced a real one — `bundler` and `service` both set `scripts.dev` on `node-service`, resolved only by array position — now fixed by making the service the explicit owner of its `dev` script. Ordering is no longer load-bearing for correctness; a colliding new feature fails the test instead of shipping. - [ ] **Pairwise combination testing** — the integration matrix covers presets, which are the *common* paths. The risk is cross-feature interaction (dual + Rollup + Jest + Biome). Generate a matrix where every option pair appears at least once.