feat(F-001): scaffold modular monolith skeleton with boundary checker
- TypeScript + Fastify skeleton under project/ (src/modules, shared, infrastructure, app) - scripts/check-module-boundaries.mjs enforcing module public-API rules (tested with fixtures) - GET /health endpoint, error envelope without stack leakage - specs/F-001-scaffold (SPEC/DESIGN/TASKS/TESTS), spec/tech.md dependency justification - 30-ticket MercadoDeVida roadmap in backlog/features.json, spec/roadmap.md - All gates approved: reviewer, security, qa; verify.sh green
This commit is contained in:
0
work/artifacts/.gitkeep
Normal file
0
work/artifacts/.gitkeep
Normal file
31
work/artifacts/F-001/architect.md
Normal file
31
work/artifacts/F-001/architect.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Architect — F-001 Scaffold modular monolith skeleton
|
||||
|
||||
done -> work/artifacts/F-001/architect.md
|
||||
|
||||
## Decision summary
|
||||
- Backend skeleton: Node 22 + TypeScript strict + Fastify 5, ESM, tsc build. No framework magic, no aliases (relative imports only) so runtime resolution stays trivial.
|
||||
- Composition root: `src/app/build-app.ts` is the only place that wires modules onto the HTTP server.
|
||||
- Boundary enforcement: explicit script `scripts/check-module-boundaries.mjs` wired into `lint:boundaries`, tested with fixtures. ESLint additionally blocks deep `@module` style imports is NOT needed because we use no aliases; the script is the single source of truth.
|
||||
- Spec-first artifacts at `specs/F-001-scaffold/` (SPEC, DESIGN, TASKS, TESTS).
|
||||
|
||||
## Expected blast radius
|
||||
|
||||
```text
|
||||
EXPECTED BLAST RADIUS
|
||||
|
||||
Modules modified: none (greenfield; new project/ skeleton only)
|
||||
Modules indirectly affected: none
|
||||
Database changes: none
|
||||
API changes: adds GET /health
|
||||
Events added/changed: none
|
||||
Risk level: low
|
||||
```
|
||||
|
||||
## Stack justification (boring choices)
|
||||
- Fastify: maintained, typed, small, no magic.
|
||||
- Vitest: TS-native, same mental model as the test pyramid required by design_prompt.
|
||||
- ESLint flat config + Prettier: standard.
|
||||
- No ORM, no Redis client, no config framework yet — each arrives with the ticket that needs it.
|
||||
|
||||
## Handoff to implementer
|
||||
Follow `specs/F-001-scaffold/TASKS.md` in order. Definition of done for build stage: all five commands green and evidence written to `work/artifacts/F-001/implementer.md`.
|
||||
22
work/artifacts/F-001/documenter.md
Normal file
22
work/artifacts/F-001/documenter.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Documenter — F-001 Scaffold modular monolith skeleton
|
||||
|
||||
done -> work/artifacts/F-001/documenter.md
|
||||
|
||||
## Why documentation applies
|
||||
F-001 introduces new developer-facing contracts: npm scripts, module boundary rules, and project layout.
|
||||
|
||||
## Documents reviewed/produced
|
||||
- `project/README.md` (created at build stage, verified accurate):
|
||||
- requirements (Node >= 22, npm)
|
||||
- all 6 npm scripts match package.json exactly: build, start, lint, lint:boundaries, typecheck, test
|
||||
- layout diagram matches actual tree
|
||||
- module rules match scripts/check-module-boundaries.mjs behavior (R1/R2)
|
||||
- `specs/F-001-scaffold/` SPEC/DESIGN/TASKS/TESTS kept consistent with final implementation (TESTS.md updated at build stage).
|
||||
- `spec/tech.md` completed with stack and dependency justification table (security policy requirement).
|
||||
|
||||
## Verification
|
||||
- `grep` cross-check: README command list == package.json scripts keys (build, start, lint, lint:boundaries, typecheck, test) -> match.
|
||||
- No API docs needed yet beyond /health (documented in implementer.md and SPEC.md).
|
||||
|
||||
## Pending docs (not this ticket)
|
||||
- API reference will start being meaningful from F-005 onward.
|
||||
55
work/artifacts/F-001/implementer.md
Normal file
55
work/artifacts/F-001/implementer.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Implementer — F-001 Scaffold modular monolith skeleton
|
||||
|
||||
done -> work/artifacts/F-001/implementer.md
|
||||
|
||||
## Files created (all under project/)
|
||||
- package.json, tsconfig.json, tsconfig.build.json, eslint.config.mjs, vitest.config.ts
|
||||
- .prettierrc.json, .prettierignore, .gitignore, README.md
|
||||
- scripts/check-module-boundaries.mjs
|
||||
- scripts/tests/boundary-checker.test.ts
|
||||
- scripts/tests/fixtures/{ok,cross-module-internal,deep-from-app}/ (boundary test fixtures)
|
||||
- src/app/build-app.ts (composition root)
|
||||
- src/app/tests/build-app.test.ts
|
||||
- src/infrastructure/http/server.ts (listen entrypoint)
|
||||
- src/modules/health/index.ts, src/modules/health/api/health.routes.ts, src/modules/health/tests/health.test.ts
|
||||
- src/shared/errors.ts
|
||||
|
||||
## Files modified
|
||||
- specs/F-001-scaffold/TESTS.md (test locations updated to final layout)
|
||||
|
||||
## Database migrations
|
||||
- None.
|
||||
|
||||
## API changes
|
||||
- Adds GET /health -> 200 {"status":"ok"}
|
||||
- Adds 404 JSON envelope: {"error":{"statusCode":404,"message":"Not Found"}}
|
||||
- Error handler never leaks stack traces (500 -> generic message).
|
||||
|
||||
## Tests added
|
||||
- health module API test (module-scoped, no cross-module imports)
|
||||
- composition root tests (health wired, 404 envelope without stack leak)
|
||||
- boundary checker tests: ok tree, R1 violation fixture, R2 violation fixture, index import allowed
|
||||
|
||||
## Tests passed (evidence)
|
||||
```
|
||||
npm run lint -> OK (eslint + prettier)
|
||||
npm run lint:boundaries -> Boundary check OK: 7 file(s) checked
|
||||
npm run typecheck -> exit 0
|
||||
npm run build -> exit 0 (dist generated)
|
||||
npm test -> Test Files 3 passed (3), Tests 7 passed (7)
|
||||
smoke: PORT=3999 node dist/infrastructure/http/server.js
|
||||
curl /health -> HTTP 200 {"status":"ok"}
|
||||
curl /nope -> {"error":{"statusCode":404,"message":"Not Found"}}
|
||||
```
|
||||
|
||||
## Known limitations
|
||||
- Boundary checker covers static import/export specifiers only (no require(), no runtime reflection). Acceptable for TS ESM codebase.
|
||||
- No request_id / structured logging yet (F-003 scope).
|
||||
- No CI pipeline defined (repo-level decision, out of F-001 scope).
|
||||
|
||||
## Follow-up work
|
||||
- F-002 database foundation, F-003 request context, F-004 config/flags (per backlog dependencies).
|
||||
|
||||
## Notes
|
||||
- The boundary checker caught a real violation during build (health test importing src/app). Fixed by moving composition tests to src/app/tests and keeping module tests module-scoped. This is the rule working as designed.
|
||||
- design_prompt.md added to .prettierignore: it is a project input document, not product code.
|
||||
29
work/artifacts/F-001/leader-close.json
Normal file
29
work/artifacts/F-001/leader-close.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"feature_id": "F-001",
|
||||
"agent": "leader",
|
||||
"verdict": "APPROVED",
|
||||
"summary": "F-001 closed. Scaffold modular monolith implemented in project/, all gates APPROVED, verify.sh exit 0.",
|
||||
"gates": {
|
||||
"reviewer": "APPROVED (reviewer.json)",
|
||||
"security": "APPROVED (security.json)",
|
||||
"qa": "APPROVED (qa.json)",
|
||||
"verify_sh": "exit 0"
|
||||
},
|
||||
"deliverables": [
|
||||
"project/: TypeScript + Fastify modular monolith skeleton (7 source files + toolchain)",
|
||||
"boundary checker with tested fixtures enforcing module rules",
|
||||
"specs/F-001-scaffold/ SPEC, DESIGN, TASKS, TESTS",
|
||||
"spec/tech.md with dependency justification"
|
||||
],
|
||||
"next_feature_hint": "F-002 (database foundation) - only depends on F-001",
|
||||
"evidence": [
|
||||
"work/artifacts/F-001/architect.md",
|
||||
"work/artifacts/F-001/implementer.md",
|
||||
"work/artifacts/F-001/reviewer.json",
|
||||
"work/artifacts/F-001/security.json",
|
||||
"work/artifacts/F-001/qa.json",
|
||||
"work/artifacts/F-001/documenter.md",
|
||||
"./scripts/verify.sh exit 0 at close"
|
||||
],
|
||||
"timestamp": "2026-08-14T19:52:00Z"
|
||||
}
|
||||
42
work/artifacts/F-001/qa.json
Normal file
42
work/artifacts/F-001/qa.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"feature_id": "F-001",
|
||||
"agent": "qa",
|
||||
"verdict": "APPROVED",
|
||||
"summary": "All 5 acceptance criteria verified with fresh executions against the final tree. No regressions (greenfield baseline).",
|
||||
"traceability": [
|
||||
{
|
||||
"criterion": "AC1: npm install, build, lint, typecheck, test all green in project/",
|
||||
"test": "fresh run of npm run build && typecheck && lint && lint:boundaries && test",
|
||||
"result": "PASS (all exit 0; 3 test files / 7 tests passed)"
|
||||
},
|
||||
{
|
||||
"criterion": "AC2: GET /health returns 200 {\"status\":\"ok\"}",
|
||||
"test": "vitest inject tests (health.test.ts, build-app.test.ts) + live smoke: PORT=3998 node dist/infrastructure/http/server.js; curl /health",
|
||||
"result": "PASS (HTTP 200 {\"status\":\"ok\"} live)"
|
||||
},
|
||||
{
|
||||
"criterion": "AC3: src/modules, src/shared, src/infrastructure, src/app exist",
|
||||
"test": "ls -d over project/src",
|
||||
"result": "PASS (all four directories present)"
|
||||
},
|
||||
{
|
||||
"criterion": "AC4: cross-module internal import fails boundary check",
|
||||
"test": "node scripts/check-module-boundaries.mjs scripts/tests/fixtures/cross-module-internal + boundary-checker.test.ts fixtures",
|
||||
"result": "PASS (exit 1, R1 violation reported)"
|
||||
},
|
||||
{
|
||||
"criterion": "AC5: ./scripts/verify.sh green at repo root",
|
||||
"test": "./scripts/verify.sh",
|
||||
"result": "PASS (exit 0, 'Orquestra verificado')"
|
||||
}
|
||||
],
|
||||
"regressions": "n/a - greenfield ticket, no prior behavior to regress",
|
||||
"evidence": [
|
||||
"project/: npm run build/typecheck/lint/lint:boundaries/test -> all exit 0",
|
||||
"npm test -> Test Files 3 passed (3), Tests 7 passed (7)",
|
||||
"live smoke on PORT=3998 -> 200 {\"status\":\"ok\"}",
|
||||
"boundary checker on violation fixture -> exit 1 with R1 violation message",
|
||||
"./scripts/verify.sh -> exit 0"
|
||||
],
|
||||
"timestamp": "2026-08-14T19:50:00Z"
|
||||
}
|
||||
32
work/artifacts/F-001/reviewer.json
Normal file
32
work/artifacts/F-001/reviewer.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"feature_id": "F-001",
|
||||
"agent": "reviewer",
|
||||
"verdict": "APPROVED",
|
||||
"summary": "Scaffold matches specs/F-001-scaffold DESIGN.md. Layout, composition root, boundary rules, error envelope and toolchain all verified by re-running commands against the tree. No blockers.",
|
||||
"checks": {
|
||||
"layout_matches_design": "PASS: src/{app,infrastructure,modules,shared} present; health exemplar exposes public API only via index.ts",
|
||||
"boundary_rules": "PASS: R1 (module escape) and R2 (deep import from outside) enforced by scripts/check-module-boundaries.mjs, demonstrated by fixture tests (7 file(s) checked clean)",
|
||||
"composition_root": "PASS: only src/app/build-app.ts wires modules; server.ts is a thin entrypoint",
|
||||
"error_handling": "PASS: single error envelope, 404 handler, 500 message generic (no stack leak)",
|
||||
"toolchain_green": "PASS: lint, lint:boundaries, typecheck, build, test all exit 0; 3 test files / 7 tests passed"
|
||||
},
|
||||
"findings": [
|
||||
{
|
||||
"severity": "info",
|
||||
"note": "Boundary checker is regex-based over static import/export specifiers; documented limitation acceptable for TS ESM, revisit only if dynamic imports appear."
|
||||
},
|
||||
{
|
||||
"severity": "info",
|
||||
"note": "Implementer evidence shows the checker caught a real violation during build (test escaping module) and fixed it architecturally - rule is effective, not decorative."
|
||||
}
|
||||
],
|
||||
"evidence": [
|
||||
"npm run lint -> exit 0 (eslint + prettier)",
|
||||
"npm run lint:boundaries -> Boundary check OK: 7 file(s) checked",
|
||||
"npm run typecheck -> exit 0",
|
||||
"npm test -> Test Files 3 passed (3), Tests 7 passed (7)",
|
||||
"files reviewed: project/src/**, project/scripts/**, project/*.json, project/eslint.config.mjs",
|
||||
"specs compared: specs/F-001-scaffold/SPEC.md, DESIGN.md, TASKS.md, TESTS.md"
|
||||
],
|
||||
"timestamp": "2026-08-14T19:46:00Z"
|
||||
}
|
||||
27
work/artifacts/F-001/security.json
Normal file
27
work/artifacts/F-001/security.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"feature_id": "F-001",
|
||||
"agent": "security",
|
||||
"verdict": "APPROVED",
|
||||
"summary": "Security gate passed. Zero audit vulnerabilities, no hardcoded secrets, no dangerous patterns in src/, error envelope prevents stack leakage, new dependencies justified in spec/tech.md per policy.",
|
||||
"checks": {
|
||||
"secrets": "PASS: grep for api_key/secret/password/token patterns in project code found none",
|
||||
"dependencies": "PASS: npm audit --omit=dev -> 0 vulnerabilities; npm audit (full) -> 0 vulnerabilities; dependency table with justification added to spec/tech.md",
|
||||
"sast_basic": "PASS: no eval, no new Function, no child_process in src/ (execFile used only in test harness with fixed, non-user-controlled arguments)",
|
||||
"input_validation": "PASS (n/a scope): no user input surfaces yet beyond GET /health; Fastify default JSON body limits apply; error handler returns generic message for 5xx",
|
||||
"repo_hygiene": "PASS: project/.gitignore excludes node_modules, dist, coverage, logs"
|
||||
},
|
||||
"findings": [
|
||||
{
|
||||
"severity": "info",
|
||||
"note": "No auth, rate limiting or CSRF surface exists yet; tracked in F-005/F-028. Nothing to mitigate in F-001."
|
||||
}
|
||||
],
|
||||
"evidence": [
|
||||
"npm audit --omit=dev -> found 0 vulnerabilities",
|
||||
"npm audit -> found 0 vulnerabilities",
|
||||
"grep secret scan over project/src, project/scripts, configs -> no hardcoded secrets",
|
||||
"grep eval|new Function|child_process over project/src -> none",
|
||||
"dependency justification added to spec/tech.md (fastify, typescript, vitest, eslint stack, prettier)"
|
||||
],
|
||||
"timestamp": "2026-08-14T19:48:00Z"
|
||||
}
|
||||
14
work/current.md
Normal file
14
work/current.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Sesión actual
|
||||
|
||||
- Feature en curso: _ninguna_ (F-001 cerrada DONE el 2026-08-14)
|
||||
- Inicio: —
|
||||
- Orquestador: —
|
||||
|
||||
## Plan
|
||||
- Próxima feature según dependencias: F-002 (Database foundation) o F-003 (HTTP foundation) o F-004 (config/flags) — todas dependen solo de F-001.
|
||||
|
||||
## Bitácora
|
||||
- 2026-08-14: F-001 completada con todos los gates APPROVED y verify.sh verde.
|
||||
|
||||
## Próximo paso
|
||||
- intake de la siguiente feature (sugerida: F-002).
|
||||
9
work/history.md
Normal file
9
work/history.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Historial (append-only)
|
||||
|
||||
> Añadir entradas al final. No reescribir historial previo.
|
||||
|
||||
## 2026-08-14 — F-001 Scaffold modular monolith skeleton — DONE
|
||||
- Gates: reviewer APPROVED, security APPROVED, qa APPROVED, verify.sh exit 0
|
||||
- Entregable: skeleton TypeScript + Fastify en project/ con boundary checker testeado; specs/F-001-scaffold completos; spec/tech.md con justificación de dependencias
|
||||
- Artefactos: work/artifacts/F-001/ (architect.md, implementer.md, reviewer.json, security.json, qa.json, documenter.md, leader-close.json)
|
||||
- Nota: el boundary checker detectó una violación real durante build (test escapando del módulo) y se corrigió moviendo los tests de composición a src/app
|
||||
11
work/runtime-status.json
Normal file
11
work/runtime-status.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"feature_id": null,
|
||||
"stage": "idle",
|
||||
"agent": "leader",
|
||||
"action": "Sin ejecución activa",
|
||||
"state": "waiting",
|
||||
"next_agent": "leader",
|
||||
"waiting_for": "Seleccionar una feature pending y actualizar este estado",
|
||||
"updated_at": "2026-08-14T19:46:48Z",
|
||||
"timeline": []
|
||||
}
|
||||
Reference in New Issue
Block a user