Commit Graph
946 Commits
Author SHA1 Message Date
Lucas Berger bc83495946 chore(16-04): commit gitleaks full-history baseline
- 613 commits scanned, ~23 MB of git history
- Zero findings after allowlisting crypto.test.ts AES test fixture
- Human-verified: single baseline finding (TEST_KEY) was a Vitest fixture, not a real credential
- Baseline is empty JSON array [] — PR-diff scans in 16-05 start from clean known state
2026-06-13 08:14:54 -04:00
Lucas Berger fba22b445b chore(16-04): allowlist crypto.test.ts synthetic AES test key in gitleaks config
- Add 4th [[allowlists]] block for apps/api/tests/broker/crypto.test.ts
- Human-verified: TEST_KEY in Vitest beforeAll is a synthetic AES-256-GCM fixture, not a real credential
- Original 3 allowlists (VAPID fixture, .env.example, .env.spike) intact
2026-06-13 08:14:26 -04:00
Lucas Berger 5819247a01 chore(16-04): add .dockerignore (secrets/dev/bulk, preserve builder inputs)
- Exclude .env/.env.* with !.env.example un-ignore so template survives
- Exclude apps/api/scripts/seed-credential.mjs (defense-in-depth)
- Exclude .git, dist/, node_modules/, tests/, Playwright artifacts
- Exclude .planning/, docs/, editor/OS, CI config, SQL dumps
- Do NOT exclude apps/api/src or apps/pwa/src (builder stage needs them)
- Add NOTE comment on migration .sql files and multi-stage COPY semantics
2026-06-13 05:28:29 -04:00
Lucas Berger 2f1592cc45 chore(16-04): add gitleaks config with fixture + env allowlists
- Add .gitleaks.toml inheriting default ruleset via [extend] useDefault = true
- Allowlist apps/api/tests/fixtures/vapid.ts (test-only VAPID keypair)
- Allowlist .env.example (intentional placeholder template)
- Allowlist apps/api/.env.spike (dev/spike values)
2026-06-13 05:27:54 -04:00
Lucas Berger ab42794c1c docs(16-03): complete eslint-plugin-security plan — add SUMMARY, advance state to plan 4 2026-06-13 05:26:15 -04:00
Lucas Berger 59e49ec3da chore(16-03): triage eslint-plugin-security findings to green
- Disable detect-object-injection globally in eslint.config.js: all hits were
  numeric loop array indices (ranks[i]) — not user-controlled keys; zod guards
  real API input boundaries; justification comment added (T-16-09)
- Add inline eslint-disable for detect-non-literal-fs-filename at 2 sites:
  - apps/api/src/index.ts: realpathSync(process.argv[1]) — runtime entry path, not user input
  - apps/api/tests/broker/expand.test.ts: readFileSync of test fixture path — test-controlled
- pnpm lint exits 0 across both apps with --max-warnings 0
- 14 of 15 security rules remain active at error; no blanket file disables
2026-06-13 05:24:02 -04:00
Lucas Berger 826a23a16c chore(16-03): add eslint-plugin-security to root flat config (D-03)
- Install eslint-plugin-security@3.0.1 to root devDependencies (pinned exact version; 3.0.1 chosen over 4.0.1 for bake time per RESEARCH)
- Add pluginSecurity import to eslint.config.js
- Insert security config block (files: apps/**/*.{ts,tsx}) with configs.recommended spread, placed before prettierConfig which stays last
- ESLint version unchanged at 9.39.4 (D-13-ESLint-PIN)
2026-06-13 05:22:32 -04:00
Lucas Berger 069fe53523 docs(16-02): complete dependency-audit-gate plan — add SUMMARY, advance state to plan 3 2026-06-13 05:20:43 -04:00
Lucas Berger baf2e3ad1b feat(16-02): add check-outdated.mjs advisory-only tiered report
- Classifies outdated packages into four tiers: AUDIT-ADVISORY, MAJOR-BEHIND-INTENTIONAL,
  MAJOR-BEHIND-UNPINNED, ROUTINE-DRIFT
- Reads outdated-pins.json for intentional pin reasons (eslint, @eslint/js, zod, @types/node)
- Cross-checks pnpm audit --json to flag pinned versions with active advisories
- Always exits 0 — never gates the build (D-06)
2026-06-13 05:19:06 -04:00
Lucas Berger 6eb51078e3 feat(16-02): add check-audit.mjs blocking wrapper + unit tests
- Exports selectBlocking() and partitionAdvisories() as pure functions for unit testing
- Main body guarded by import.meta.url check (only runs when invoked directly)
- Uses pnpm audit --json (no --audit-level — Pitfall 1 honored)
- Exits 1 on unwaived High/Critical; exits 0 with advisory report on moderate/low
- All 5 unit tests pass (node --test)
2026-06-13 05:18:19 -04:00
Lucas Berger 7ac8b19a76 test(16-02): add failing tests for check-audit.mjs filter logic
- Tests four behavioral cases: unwaived High → blocking, waived High → not blocking,
  moderate/low only → not blocking, no advisories → not blocking
- Uses node:test + node:assert (no extra dependencies)
- Fails at RED: check-audit.mjs does not yet exist
2026-06-13 05:17:36 -04:00
Lucas Berger 0f101bdd39 chore(16-02): seed audit allowlist (esbuild GHSA waiver) + outdated pin reasons
- audit-allowlist.json: GHSA-gv7w-rqvm-qjhr waiver (esbuild dev transitive via drizzle-kit/vitest/vite; not in production runtime)
- outdated-pins.json: intentional pin reasons for eslint, @eslint/js, zod, @types/node
2026-06-13 05:17:13 -04:00
Lucas Berger b67d757f87 docs(16-01): complete boot-guard plan — add SUMMARY, advance state to plan 2 2026-06-13 05:15:35 -04:00
Lucas Berger 5b4f32a649 chore(16-01): bake ENV NODE_ENV=production into production Dockerfile stage
- Add ENV NODE_ENV=production in production stage after WORKDIR /app/apps/api
- Engages the devBypass.ts hard guard so DEV_AUTH_BYPASS can never inject
  DEV_USER in the shipped image, even if accidentally set (D-07)
- No other stage (base/builder/dev/pwa-builder) is affected
2026-06-13 05:14:28 -04:00
Lucas Berger c2ffd1c1b2 feat(16-01): add boot-time refuse-to-boot guard for dev-bypass in production
- Create apps/api/src/lib/bootGuards.ts with assertNotDevBypassInProduction()
- Guard exits non-zero when NODE_ENV=production AND DEV_AUTH_BYPASS=true (D-08)
- Wire import + call as first statement in isMainModule() block in index.ts
- 3/3 unit tests pass, typecheck green
2026-06-13 05:14:08 -04:00
Lucas Berger 8414e891b3 test(16-01): add failing tests for boot-time dev-bypass guard
- Three test cases: prod+bypass=exit(1), dev+bypass=no-exit, prod+unset=no-exit
- Fails with Cannot find module (src/lib/bootGuards.ts absent) — RED confirmed
2026-06-13 05:12:33 -04:00
Lucas Berger 0d6366cd56 docs(16): create phase plan 2026-06-12 23:25:48 -04:00
Lucas Berger e039c85a22 docs(16): create phase plan — 6 plans, 2 waves (dep audit, security checks, image hygiene)
SEC-01/02, DEP-01/02, IMG-01/02/03, CI-03. Wave 1: image-hygiene runtime guard (TDD), audit+outdated wrappers (TDD), eslint-plugin-security fold, gitleaks config+baseline+.dockerignore. Wave 2: ci.yml security job + gate wiring, publish.yml hygiene assertions + boot-smoke. esbuild GHSA-gv7w-rqvm-qjhr waivered in 16-02 before the gate goes live.
2026-06-12 23:23:18 -04:00
Lucas Berger bfc93584d7 docs(16): add validation strategy 2026-06-12 23:08:05 -04:00
Lucas Berger 4929a79eee docs(16): research phase — gitleaks, eslint-plugin-security, pnpm audit/outdated, image hygiene 2026-06-12 23:06:20 -04:00
Lucas Berger 7d30e0bfab docs(state): record phase 16 context session 2026-06-12 22:50:57 -04:00
Lucas Berger 75b82f472c docs(16): capture phase context 2026-06-12 22:50:52 -04:00
Lucas Berger 2d50c7b515 docs(roadmap): add Phase 16 (CI dep audit, security & image hygiene); fold in + remove backlog 999.17
Publish / publish (push) Successful in 6s
2026-06-12 22:08:29 -04:00
Lucas Berger 656da005d6 docs: fix backlog 999.17 dir name (drop spurious null- prefix) 2026-06-12 21:41:15 -04:00
Lucas Berger f742583842 docs: add backlog item 999.17 — define & enforce dev/prod image boundary 2026-06-12 21:41:02 -04:00
Lucas Berger 17c1f63d4c docs(state): record phase 10 context session 2026-06-12 21:37:32 -04:00
Lucas Berger 52506cdc2d docs(10): capture phase context 2026-06-12 21:37:26 -04:00
luckberg b119f11cf2 Merge pull request 'Phase 9: Faster Write-Back (CAL-15) — event-driven outbox drain' (#14) from gsd/phase-09-faster-write-back into main
Publish / publish (push) Successful in 15s
Reviewed-on: #14
2026-06-12 21:19:16 -04:00
Lucas Berger 89411ce44b style(09): prettier-format outboxWorker.test.ts (fix CI format check)
CI / changes (pull_request) Successful in 2s
CI / fast-checks (pull_request) Successful in 1m20s
CI / api (pull_request) Successful in 58s
CI / harness (pull_request) Successful in 3m52s
CI / gate (pull_request) Successful in 1s
2026-06-12 21:12:17 -04:00
Lucas Berger 777910c86a docs(09): add code review fix report + refresh review after fixes
CI / changes (pull_request) Successful in 2s
CI / fast-checks (pull_request) Failing after 54s
CI / api (pull_request) Successful in 58s
CI / harness (pull_request) Successful in 3m51s
CI / gate (pull_request) Failing after 0s
2026-06-12 21:01:55 -04:00
Lucas Berger b7767af825 fix(09): IN-01 guard __resetDrainState against production use 2026-06-12 20:59:53 -04:00
Lucas Berger b724b3e932 fix(09): WR-03/IN-03/IN-04 add drain-listener teardown, test-only __resetDrainState, and remove stale RED @ts-ignore 2026-06-12 20:51:49 -04:00
Lucas Berger e1ffddf8dc fix(09): WR-02 make initOutboxTrigger idempotent and retain the unsubscribe handle via stopOutboxTrigger 2026-06-12 20:51:32 -04:00
Lucas Berger d3163f2281 fix(09): IN-02 extract shared resolveFinalRrule helper to deduplicate the update/create RRULE decision tree 2026-06-12 20:50:52 -04:00
Lucas Berger b8bb6e7671 fix(09): IN-01 add ordering comments cementing signalOutboxDrain() stays after the enqueue commit 2026-06-12 20:50:26 -04:00
Lucas Berger 8307a7b713 fix(09): WR-01 defer outbox drain emit to a microtask so a throwing listener cannot corrupt the enqueue route response 2026-06-12 20:50:21 -04:00
Lucas Berger 1bc1f134a7 docs(09): refresh code review report
CI / changes (pull_request) Successful in 2s
CI / fast-checks (pull_request) Failing after 56s
CI / api (pull_request) Successful in 59s
CI / harness (pull_request) Successful in 3m55s
CI / gate (pull_request) Failing after 1s
2026-06-12 17:24:08 -04:00
Lucas Berger ffc1f52f2b docs(phase-09): evolve PROJECT.md after phase completion
CI / changes (pull_request) Successful in 2s
CI / fast-checks (pull_request) Failing after 56s
CI / api (pull_request) Successful in 1m0s
CI / harness (pull_request) Successful in 4m0s
CI / gate (pull_request) Failing after 1s
2026-06-12 17:09:34 -04:00
Lucas Berger 03f02a1207 docs(phase-09): complete phase execution 2026-06-12 17:08:54 -04:00
Lucas Berger 551b751b81 docs(09): add code review report 2026-06-12 17:04:55 -04:00
Lucas Berger fff5789a5f docs(09-02): complete enqueue-site signal wiring plan
- 09-02-SUMMARY.md: 4 signalOutboxDrain() sites + initOutboxTrigger() startup wiring
- STATE.md: plan position advanced, metrics recorded
- ROADMAP.md: plan progress updated
- REQUIREMENTS.md: CAL-15 marked complete
2026-06-12 16:59:27 -04:00
Lucas Berger 0ebdf4864d feat(09-02): wire initOutboxTrigger() at startup under isMainModule() in index.ts
- extend outboxWorker import to include initOutboxTrigger alongside startOutboxWorker
- call initOutboxTrigger() immediately after startOutboxWorker() inside isMainModule() guard
- gated by isMainModule() so tests importing app never register the drain listener (Pitfall 4)
2026-06-12 16:57:52 -04:00
Lucas Berger 30eff3dcdb feat(09-02): add signalOutboxDrain() after each of four enqueue sites in events.ts
- import signalOutboxDrain from '../lib/outboxTrigger.js'
- Site 1 (POST /create): fire-and-forget signal after outbox insert, before 202
- Site 2 (PATCH edit-as-move): signal after await db.transaction() resolves, not inside callback (D-03)
- Site 3 (PATCH same-calendar update): signal after outbox insert, before 202
- Site 4 (DELETE): signal after outbox insert, before 202
2026-06-12 16:57:21 -04:00
Lucas Berger 9164619ccd docs(09-01): complete outbox drain trigger wiring plan 2026-06-12 16:54:20 -04:00
Lucas Berger 2b113045f7 feat(09-01): add scheduleOutboxDrain, drainRequested, initOutboxTrigger; route setInterval through wrapper
- Add import { onOutboxDrain } from outboxTrigger.js
- Add let drainRequested = false (D-05 trailing-re-drain flag)
- Export scheduleOutboxDrain(): void — isDraining guard + drainRequested loop (D-05/T-09-01)
  drainRequested=false reset precedes recursive call (Pitfall 3)
  errors caught via .catch to prevent crash (D-02/T-09-03)
- Export initOutboxTrigger(): void — registers onOutboxDrain(() => scheduleOutboxDrain())
- startOutboxWorker setInterval body: scheduleOutboxDrain() replaces runOutboxDrain().catch()
  15 * 1000 interval unchanged (D-08)
- runOutboxDrain body/isDraining guard/finally unchanged (D-02/D-07)
- Fix trigger-wiring tests: add beforeAll(initOutboxTrigger) to wire EventEmitter listener;
  fix Test C mock to return empty rows on trailing drain (correct D-07 behaviour)
- 30/30 outboxWorker tests GREEN; tsc --noEmit clean
2026-06-12 16:52:36 -04:00
Lucas Berger bcde073729 test(09-01): add failing trigger-wiring tests for SC-1, D-05, D-07
- Import scheduleOutboxDrain (not yet exported — causes RED)
- Import signalOutboxDrain from outboxTrigger.ts
- Add describe block 'scheduleOutboxDrain — trigger wiring (D-09)' with 3 tests:
  Test A SC-1: signalOutboxDrain() fires drain promptly without timer advance
  Test B D-05: two mid-drain signals collapse to exactly one trailing re-drain
  Test C D-07: concurrent scheduleOutboxDrain() calls dispatch exactly once via isDraining guard
- 27 pre-existing tests unmodified and passing; 3 new tests failing (RED)
2026-06-12 16:48:41 -04:00
Lucas Berger 1e12d702a1 feat(09-01): create outboxTrigger.ts zero-dependency EventEmitter signal module
- Module-level singleton EventEmitter, single subscriber, no setMaxListeners
- Export signalOutboxDrain(): void — fire-and-forget drain signal (D-04)
- Export onOutboxDrain(handler): () => void — register/unsubscribe listener
- Only imports node:events; zero internal dependencies (no circular import risk)
2026-06-12 16:47:39 -04:00
Lucas Berger a78c7241d6 docs(09): add pattern map 2026-06-12 14:28:32 -04:00
Lucas Berger 5fdbd11475 docs(09): create phase plan 2026-06-12 14:28:09 -04:00
Lucas Berger b22c92ae92 docs(09): create phase plan (2 plans, 2 waves) — outboxTrigger + drain signal wiring 2026-06-12 14:21:37 -04:00