<objective> Scaffold the strict ESM TypeScript package: the ONLY plan that touches root files. Installs the verified toolchain plus the single JSON Schema validator dependency that plan 01-05 will import.
Purpose: Give every later module a compiling, linted, tested, buildable root with no suppressions. Honors D-04 (vertical-slice first): this plan adds only the scaffold the slice needs, with no runtime or transport code. Output: Root configs, committed lockfile, four passing scripts, recorded verified versions. </objective>
<execution_context> @~/.claude/gsd-core/workflows/execute-plan.md @~/.claude/gsd-core/templates/summary.md </execution_context>
<context> @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/01-scaffold-and-frozen-contracts/01-CONTEXT.md @.planning/phases/01-scaffold-and-frozen-contracts/01-PATTERNS.md @docs/integration/hosts.md </context>
<tasks>
<task type="checkpoint:human-verify" gate="blocking-human"> <name>Task 1: Approve the registry-verified toolchain and validator selection</name> <files>package.json</files> <read_first> - .planning/phases/01-scaffold-and-frozen-contracts/01-CONTEXT.md (D-07 registry verification, D-05 no system packages) - docs/integration/hosts.md (Node requirement constraining engine-compatible choices) </read_first> <action>Query the npm registry at execution time for each candidate toolchain item (TypeScript compiler, ESLint flat-config core with a TypeScript parser, Node type definitions) and for exactly one JSON Schema validation library that plan 01-05 will import; verify each entry exists in its registry record at execution time and record the verified name and version in the task output, never typing a name or version from memory per D-07, and never installing or upgrading system packages per D-05. Present the verified name-plus-version list for human approval before any install command runs. This gate also covers threat T-01-SC: every installed item below is registry-verified and reasoned, none is assumed.</action> <acceptance_criteria> - Task output lists each selected item with the registry-verified name and version plus the check used (registry query at execution time) - No install command has run before this approval </acceptance_criteria> <verify> <automated>MISSING — Wave 0 must create the approval record first</automated> </verify> <done>Human approved the registry-verified name-plus-version list; install may proceed.</done> </task>
<task type="auto"> <name>Task 2: Create root configs and install the approved toolchain</name> <files>package.json, package-lock.json, tsconfig.json, eslint.config.js, .gitignore</files> <read_first> - .planning/phases/01-scaffold-and-frozen-contracts/01-CONTEXT.md (D-07 toolchain, D-08 four scripts and suppression ban, D-09 engines from hosts.md) - docs/integration/hosts.md (the Node requirement value to copy verbatim into engines) </read_first> <action>Create package.json with type module, the engines field copied verbatim from the Node requirement recorded in docs/integration/hosts.md per D-09, and the four scripts typecheck, lint, test, and build where test invokes the Node built-in test runner per D-07; create tsconfig.json in strict mode with ESM module settings plus explicit choices beyond strict recorded in the task output (Claude's Discretion); create eslint.config.js in flat-config form with the TypeScript parser and the no-empty rule configured to disallow empty catch blocks per D-08 and QUAL-01 plus the exact additional rule set recorded in the task output (Claude's Discretion); create .gitignore covering node_modules and build output; install only the Task 1 approved items and commit package-lock.json per D-07. Choose test layout test/ mirroring src/ per Claude's Discretion (no test config file unless the runner needs one, governed by D-07 and D-08). Record every installed name-plus-version in the task output.</action> <acceptance_criteria> - package.json contains "type": "module" and an engines field matching the hosts.md Node requirement - package.json defines scripts typecheck, lint, test, and build - tsconfig.json enables strict mode and ESM; eslint.config.js configures no-empty to disallow empty catch blocks - package-lock.json exists and is committed; task output records every installed name-plus-version - No @ts-ignore, eslint-disable, skip, or todo text exists in any created file </acceptance_criteria> <verify> <automated>npm run typecheck</automated> <fails_when>non-zero exit, or any "@ts-ignore", "@ts-expect-error", or "eslint-disable" string in the output</fails_when> </verify> <done>Root package scaffolds, installs from the approved list only, lockfile committed, typecheck passes with no suppressions.</done> </task>
<task type="auto"> <name>Task 3: Prove all four quality scripts pass with no suppressions</name> <files>eslint.config.js, package.json</files> <read_first> - .planning/phases/01-scaffold-and-frozen-contracts/01-CONTEXT.md (D-08 suppression ban; QUAL-01 no silent catch blocks; Phase 1 has no runtime async boundary so the timeout rule applies from Phase 2) </read_first> <action>Run the four scripts to green on the scaffold and add no source yet; confirm the lint configuration fails a probe file containing an empty catch block and passes once the probe is removed, proving QUAL-01 no-empty enforcement without leaving the probe in the repo; state in the task output that Phase 1 contains no runtime async boundary so timeout coverage starts in Phase 2. Do not create src/ or test/ files here; those belong to plans 01-04 and 01-05.</action> <acceptance_criteria> - npm run typecheck, npm run lint, npm test, and npm run build each exit 0 - A temporary empty-catch probe fails lint and is removed afterwards, leaving the repo without it - No file under src/, test/, runtime, anda, omp, mcp, acp, cli, or plugin/ is created </acceptance_criteria> <verify> <automated>npm run lint</automated> <fails_when>non-zero exit, or any "eslint-disable" string in the output</fails_when> </verify> <done>All four scripts exit 0, no-empty enforcement is demonstrated, no suppressions exist, no phase-out-of-scope files created.</done> </task>
</tasks>
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| registry -> repo | npm registry metadata is read to verify identity, never executed blindly |
STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-01-SC | T (Tampering) | npm installs | high | mitigate | Task 1 blocking-human gate: every item registry-verified at execution and human-approved before install; versions recorded in task output |
| T-01-05 | I (Information Disclosure) | repo hygiene | low | mitigate | .gitignore covers build output and secrets; grep created files for host-local paths before finishing |
| T-01-06 | D (Denial of Service) | build/test scripts | low | accept | Phase 1 has no runtime async boundary; timeout rule applies from Phase 2 onward, stated in Task 3 output |
| </threat_model> |
<verification> npm run typecheck, npm run lint, npm test, and npm run build each exit 0; grep for ts-ignore, eslint-disable, skip, and todo suppressions returns nothing; package.json engines matches hosts.md. </verification>
<success_criteria> Strict ESM package scaffolds with committed lockfile, four scripts all exit 0 with no suppressions, engines set from hosts.md, installed versions recorded, no-empty enforcement demonstrated. </success_criteria>
<output> Create .planning/phases/01-scaffold-and-frozen-contracts/01-03-SUMMARY.md when done </output>
Artifacts this phase produces
- package.json (ESM, engines from hosts.md per D-09, four scripts per D-08; sole owner is this plan)
- package-lock.json (committed per D-07)
- tsconfig.json (strict + ESM fixed; extra options are Claude's Discretion, recorded in output)
- eslint.config.js (flat config, no-empty disallowing empty catch per QUAL-01; extra rules are Claude's Discretion)
- .gitignore (build output, secrets hygiene)
- Installed toolchain + one JSON Schema validator dependency, each registry-verified at execution with versions recorded (names never typed from memory)