From 272230539899d5f2514c3d13624485096ea48ba4 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Wed, 13 May 2026 08:47:30 +0200 Subject: [PATCH 1/8] docs(work): scaffold fallow-integration-v1 epic + stories --- docs/work/_state.json | 87 ++++++++++++++++++- .../01-fallow-install/_story.md | 17 ++++ .../02-pnpm-turbo-wiring/_story.md | 17 ++++ .../03-ci-integration/_story.md | 15 ++++ .../04-docs-and-prompts/_story.md | 18 ++++ docs/work/fallow-integration-v1/_epic.md | 28 ++++++ 6 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 docs/work/fallow-integration-v1/01-fallow-install/_story.md create mode 100644 docs/work/fallow-integration-v1/02-pnpm-turbo-wiring/_story.md create mode 100644 docs/work/fallow-integration-v1/03-ci-integration/_story.md create mode 100644 docs/work/fallow-integration-v1/04-docs-and-prompts/_story.md create mode 100644 docs/work/fallow-integration-v1/_epic.md diff --git a/docs/work/_state.json b/docs/work/_state.json index e74ddfa..bfe0403 100644 --- a/docs/work/_state.json +++ b/docs/work/_state.json @@ -1,5 +1,5 @@ { - "updated_at": "2026-05-13T06:32:59.719Z", + "updated_at": "2026-05-13T06:47:32.677Z", "epics": { "agent-workflow-docs-v1": { "status": "done", @@ -202,6 +202,56 @@ } } }, + "fallow-integration-v1": { + "status": "in-progress", + "title": "Fallow integration v1 — whole-codebase static analysis as 5th gate", + "stories": { + "01-fallow-install": { + "status": "in-progress", + "title": "Install fallow + create .fallowrc.json", + "ac_total": 4, + "ac_completed": 0, + "depends_on": [], + "blocks": [ + "02-pnpm-turbo-wiring" + ] + }, + "02-pnpm-turbo-wiring": { + "status": "todo", + "title": "pnpm script + turbo task wiring", + "ac_total": 4, + "ac_completed": 0, + "depends_on": [ + "01-fallow-install" + ], + "blocks": [ + "03-ci-integration" + ] + }, + "03-ci-integration": { + "status": "todo", + "title": "CI workflow runs pnpm fallow", + "ac_total": 2, + "ac_completed": 0, + "depends_on": [ + "02-pnpm-turbo-wiring" + ], + "blocks": [ + "04-docs-and-prompts" + ] + }, + "04-docs-and-prompts": { + "status": "todo", + "title": "Docs + agent prompt updates", + "ac_total": 5, + "ac_completed": 0, + "depends_on": [ + "03-ci-integration" + ], + "blocks": [] + } + } + }, "frontend-conformance-v1": { "status": "done", "title": "Frontend conformance rules v1", @@ -359,6 +409,37 @@ } } }, - "ready": [], - "blocked": [] + "ready": [ + { + "epic": "fallow-integration-v1", + "story": "01-fallow-install", + "title": "Install fallow + create .fallowrc.json" + } + ], + "blocked": [ + { + "epic": "fallow-integration-v1", + "story": "02-pnpm-turbo-wiring", + "title": "pnpm script + turbo task wiring", + "waiting_on": [ + "fallow-integration-v1/01-fallow-install" + ] + }, + { + "epic": "fallow-integration-v1", + "story": "03-ci-integration", + "title": "CI workflow runs pnpm fallow", + "waiting_on": [ + "fallow-integration-v1/02-pnpm-turbo-wiring" + ] + }, + { + "epic": "fallow-integration-v1", + "story": "04-docs-and-prompts", + "title": "Docs + agent prompt updates", + "waiting_on": [ + "fallow-integration-v1/03-ci-integration" + ] + } + ] } diff --git a/docs/work/fallow-integration-v1/01-fallow-install/_story.md b/docs/work/fallow-integration-v1/01-fallow-install/_story.md new file mode 100644 index 0000000..a69c9e9 --- /dev/null +++ b/docs/work/fallow-integration-v1/01-fallow-install/_story.md @@ -0,0 +1,17 @@ +--- +id: 01-fallow-install +epic: fallow-integration-v1 +title: Install fallow + create .fallowrc.json +type: technical-story +status: in-progress +feature: tooling +depends-on: [] +blocks: [02-pnpm-turbo-wiring] +--- + +## Tasks + +- [ ] Story scaffold +- [ ] Add fallow to root devDependencies +- [ ] Create .fallowrc.json with monorepo-aware config +- [ ] Verify `pnpm exec fallow --version` works diff --git a/docs/work/fallow-integration-v1/02-pnpm-turbo-wiring/_story.md b/docs/work/fallow-integration-v1/02-pnpm-turbo-wiring/_story.md new file mode 100644 index 0000000..3016153 --- /dev/null +++ b/docs/work/fallow-integration-v1/02-pnpm-turbo-wiring/_story.md @@ -0,0 +1,17 @@ +--- +id: 02-pnpm-turbo-wiring +epic: fallow-integration-v1 +title: pnpm script + turbo task wiring +type: technical-story +status: todo +feature: tooling +depends-on: [01-fallow-install] +blocks: [03-ci-integration] +--- + +## Tasks + +- [ ] pnpm fallow script (runs all) +- [ ] pnpm fallow:audit script (runs `fallow audit --base main`) +- [ ] turbo.json task entry for fallow +- [ ] Run `pnpm fallow` against current repo, capture findings, tune config diff --git a/docs/work/fallow-integration-v1/03-ci-integration/_story.md b/docs/work/fallow-integration-v1/03-ci-integration/_story.md new file mode 100644 index 0000000..631fcc5 --- /dev/null +++ b/docs/work/fallow-integration-v1/03-ci-integration/_story.md @@ -0,0 +1,15 @@ +--- +id: 03-ci-integration +epic: fallow-integration-v1 +title: CI workflow runs pnpm fallow +type: technical-story +status: todo +feature: ci +depends-on: [02-pnpm-turbo-wiring] +blocks: [04-docs-and-prompts] +--- + +## Tasks + +- [ ] Add `pnpm fallow` step after `pnpm conformance` in ci.yml +- [ ] Use --format annotations for inline GH PR comments diff --git a/docs/work/fallow-integration-v1/04-docs-and-prompts/_story.md b/docs/work/fallow-integration-v1/04-docs-and-prompts/_story.md new file mode 100644 index 0000000..7cd200b --- /dev/null +++ b/docs/work/fallow-integration-v1/04-docs-and-prompts/_story.md @@ -0,0 +1,18 @@ +--- +id: 04-docs-and-prompts +epic: fallow-integration-v1 +title: Docs + agent prompt updates +type: technical-story +status: todo +feature: agent-prompts +depends-on: [03-ci-integration] +blocks: [] +--- + +## Tasks + +- [ ] Update CLAUDE.md conformance-system section to list Fallow as 5th gate +- [ ] Update docs/guides/conformance-quickref.md with Fallow row +- [ ] Update .sandcastle/implementer.prompt.md: run `fallow audit` before commit +- [ ] Update .sandcastle/reviewer.prompt.md: verify implementer ran fallow audit +- [ ] Final verification + closeout diff --git a/docs/work/fallow-integration-v1/_epic.md b/docs/work/fallow-integration-v1/_epic.md new file mode 100644 index 0000000..65c68f7 --- /dev/null +++ b/docs/work/fallow-integration-v1/_epic.md @@ -0,0 +1,28 @@ +--- +id: fallow-integration-v1 +prd: null +title: Fallow integration v1 — whole-codebase static analysis as 5th gate +type: epic +status: in-progress +features: [tooling, ci, agent-prompts] +created: 2026-05-13 +--- + +## Goal + +Wire Fallow as the 5th conformance gate: install, configure, pnpm script, +turbo task, CI step, docs update, agent prompt update. + +## Why + +Fallow catches what file-local lint, type-check, manifest assertions, and +conformance event closure cannot — dead code, dupes, circular deps, +complexity hotspots, and AI-change drift. `fallow audit --base main` is +purpose-built for AI agents to self-validate before submission. + +## Stories + +- [ ] [01 — Install fallow + .fallowrc.json](01-fallow-install/_story.md) +- [ ] [02 — pnpm + turbo wiring](02-pnpm-turbo-wiring/_story.md) +- [ ] [03 — CI workflow integration](03-ci-integration/_story.md) +- [ ] [04 — Docs + agent prompt updates](04-docs-and-prompts/_story.md) From e1d54a1f3e7910041da06034d810a2b8acd276ed Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Wed, 13 May 2026 08:48:08 +0200 Subject: [PATCH 2/8] feat: install fallow + .fallowrc.json config --- .fallowrc.json | 28 +++++++++++++ package.json | 1 + pnpm-lock.yaml | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 .fallowrc.json diff --git a/.fallowrc.json b/.fallowrc.json new file mode 100644 index 0000000..94e66ef --- /dev/null +++ b/.fallowrc.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json", + "ignorePatterns": [ + "**/node_modules/**", + "**/dist/**", + "**/.next/**", + "**/.turbo/**", + "**/storybook-static/**", + "**/__snapshots__/**", + "**/turbo/generators/templates/**", + "**/*.generated.ts", + "**/*.d.ts" + ], + "ignoreDependencies": [], + "ignoreExportsUsedInFile": true, + "rules": { + "unused-files": "warn", + "unused-exports": "warn", + "unused-types": "off", + "circular-dependencies": "error", + "duplicate-code": "warn" + }, + "health": { + "maxCyclomatic": 25, + "maxCognitive": 20, + "maxCrap": 35 + } +} diff --git a/package.json b/package.json index dcd747b..a738a00 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@playwright/test": "^1.49.0", "@turbo/gen": "^2.4.0", "@types/node": "^22.0.0", + "fallow": "^2.73.0", "husky": "^9.0.0", "lint-staged": "^16.0.0", "prettier": "^3.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d8acfa0..9865eed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,6 +19,9 @@ importers: "@types/node": specifier: ^22.0.0 version: 22.19.17 + fallow: + specifier: ^2.73.0 + version: 2.73.0 husky: specifier: ^9.0.0 version: 9.1.7 @@ -2289,6 +2292,70 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + "@fallow-cli/darwin-arm64@2.73.0": + resolution: + { + integrity: sha512-4mo+35ufU3H5HpyYi/UT12EvRJA7ibglIaWTDqjc6TkZoz7fXYKh9PT+KcBBZyMbFC1kvAxJsfJP1J4zKXr5xQ==, + } + cpu: [arm64] + os: [darwin] + + "@fallow-cli/darwin-x64@2.73.0": + resolution: + { + integrity: sha512-bgob14wLLRXTXI+/XMT7OhVIZV1aBw0lvlXJwl/+t8MOUftlG68VoZVRkpzDypWc/dPxsa5J72kUv/5b1F/jXg==, + } + cpu: [x64] + os: [darwin] + + "@fallow-cli/linux-arm64-gnu@2.73.0": + resolution: + { + integrity: sha512-7q058FGiv92VCMikoaNkcJOlhuUeYbu/dvzAyjwBYCt2FBadPdkafz29IRZN2McfLXmCdizV6531TPh2H8Z/XA==, + } + cpu: [arm64] + os: [linux] + + "@fallow-cli/linux-arm64-musl@2.73.0": + resolution: + { + integrity: sha512-rykM9ClOZ7cSUvox0Qesq3Vh4XTdHdgVDqgEo+njs1vJsj9IMINZhENsR7/9PKIEbH5rH/3BsOQ6wwkXwi7Z/w==, + } + cpu: [arm64] + os: [linux] + + "@fallow-cli/linux-x64-gnu@2.73.0": + resolution: + { + integrity: sha512-BOk8LUgb97giUB5gUbH2PDV68EV/KlIKmNe+UyYeb8qsQkuIbwnYIxYQ1SmvagcCJdjkzGnJI4JXswvDB96MJg==, + } + cpu: [x64] + os: [linux] + + "@fallow-cli/linux-x64-musl@2.73.0": + resolution: + { + integrity: sha512-zaHIF7rdNGj7gRJ49EG64nmuco1N074Na1DiOBn/bWg1YZxGlQlJL5d0cq1Kb+EoQ0sTwa+Ya4g4/mQ0Wdb+nA==, + } + cpu: [x64] + os: [linux] + + "@fallow-cli/win32-arm64-msvc@2.73.0": + resolution: + { + integrity: sha512-IlWjJiSPY8j0n7jUEMNaEEdygwI4F2gzulAQmIcTlemZgdzAQCDuI3i8ylePcCc2+1YfD5IwdoQIS+WRkUulBw==, + } + cpu: [arm64] + os: [win32] + + "@fallow-cli/win32-x64-msvc@2.73.0": + resolution: + { + integrity: sha512-pceEJAFpqmlp4gL+ANyhnNLFGtaHCydEaJCUFZ3TL885RWP2nvOIe4TbbdIGRJwAnw0aa6jtM8QKK0DNCSQlxQ==, + } + cpu: [x64] + os: [win32] + "@fastify/otel@0.18.0": resolution: { @@ -8048,6 +8115,14 @@ packages: } engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + fallow@2.73.0: + resolution: + { + integrity: sha512-4qyUIGWhPvmeOnS6tNwB+WyVB92ImVWYEF6wiJT6Khn53SxIBi0+IxTsUT2NVjFR6mL4xRwL2vvpUe/OzvMYcw==, + } + engines: { node: ">=16" } + hasBin: true + fast-check@3.23.2: resolution: { @@ -13574,6 +13649,30 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) + "@fallow-cli/darwin-arm64@2.73.0": + optional: true + + "@fallow-cli/darwin-x64@2.73.0": + optional: true + + "@fallow-cli/linux-arm64-gnu@2.73.0": + optional: true + + "@fallow-cli/linux-arm64-musl@2.73.0": + optional: true + + "@fallow-cli/linux-x64-gnu@2.73.0": + optional: true + + "@fallow-cli/linux-x64-musl@2.73.0": + optional: true + + "@fallow-cli/win32-arm64-msvc@2.73.0": + optional: true + + "@fallow-cli/win32-x64-msvc@2.73.0": + optional: true + "@fastify/otel@0.18.0(@opentelemetry/api@1.9.1)": dependencies: "@opentelemetry/api": 1.9.1 @@ -17879,6 +17978,19 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 + fallow@2.73.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + "@fallow-cli/darwin-arm64": 2.73.0 + "@fallow-cli/darwin-x64": 2.73.0 + "@fallow-cli/linux-arm64-gnu": 2.73.0 + "@fallow-cli/linux-arm64-musl": 2.73.0 + "@fallow-cli/linux-x64-gnu": 2.73.0 + "@fallow-cli/linux-x64-musl": 2.73.0 + "@fallow-cli/win32-arm64-msvc": 2.73.0 + "@fallow-cli/win32-x64-msvc": 2.73.0 + fast-check@3.23.2: dependencies: pure-rand: 6.1.0 From 7c7a65f7e343b6b4835c98b5d596e1ba134819f7 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Wed, 13 May 2026 08:51:45 +0200 Subject: [PATCH 3/8] feat: pnpm fallow + fallow:audit scripts, turbo task entry --- .fallowrc.json | 40 +++++++++++++++++++++++++++++++++++++--- package.json | 2 ++ turbo.json | 11 +++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/.fallowrc.json b/.fallowrc.json index 94e66ef..26024d4 100644 --- a/.fallowrc.json +++ b/.fallowrc.json @@ -11,18 +11,52 @@ "**/*.generated.ts", "**/*.d.ts" ], - "ignoreDependencies": [], + "dynamicallyLoaded": [ + "packages/**/__factories__/**", + "packages/**/__seeds__/**", + "apps/**/instrumentation.ts", + "apps/**/instrumentation-client.ts", + "apps/storybook/test-runner.config.ts", + "scripts/**/*.mjs" + ], + "publicPackages": ["@repo/core-*"], + "ignoreDependencies": [ + "@payloadcms/ui", + "sass", + "sharp", + "@tanstack/react-query", + "@trpc/server", + "superjson", + "@repo/blog", + "@repo/core-api", + "@repo/marketing-pages", + "@repo/navigation", + "@repo/core-testing", + "http-server", + "wait-on", + "@opentelemetry/api-logs", + "@typescript-eslint/eslint-plugin", + "@testing-library/user-event", + "zod", + "@eslint/js", + "@opentelemetry/sdk-node", + "@sentry/opentelemetry" + ], "ignoreExportsUsedInFile": true, "rules": { "unused-files": "warn", "unused-exports": "warn", "unused-types": "off", + "unused-class-members": "warn", + "unused-dependencies": "warn", + "unused-dev-dependencies": "warn", + "unlisted-dependencies": "warn", "circular-dependencies": "error", "duplicate-code": "warn" }, "health": { "maxCyclomatic": 25, - "maxCognitive": 20, - "maxCrap": 35 + "maxCognitive": 30, + "maxCrap": 400 } } diff --git a/package.json b/package.json index a738a00..9506fa0 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "test:visual": "pnpm --filter @repo/storybook exec concurrently -k -s first -n 'SB,VRT' -c 'magenta,blue' 'pnpm --filter @repo/storybook exec http-server storybook-static --port 6006 --silent' 'pnpm --filter @repo/storybook exec wait-on tcp:6006 && pnpm exec playwright test'", "typecheck": "turbo run typecheck", "conformance": "node scripts/conformance.mjs", + "fallow": "fallow", + "fallow:audit": "fallow audit --base main", "work": "node scripts/work/cli.mjs", "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"", "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\"", diff --git a/turbo.json b/turbo.json index c2ec817..51620d1 100644 --- a/turbo.json +++ b/turbo.json @@ -83,6 +83,17 @@ ], "outputs": [] }, + "fallow": { + "inputs": [ + "packages/**/src/**/*.ts", + "packages/**/src/**/*.tsx", + "apps/**/src/**/*.ts", + "apps/**/src/**/*.tsx", + "scripts/**/*.mjs", + ".fallowrc.json" + ], + "outputs": [] + }, "build-storybook": { "outputs": ["storybook-static/**"] }, From 749938e48b8b2534f8afa7848f7f59197bd2eb3b Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Wed, 13 May 2026 08:51:53 +0200 Subject: [PATCH 4/8] ci: add fallow whole-codebase gate after pnpm conformance --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ad5a57..a0a7fd8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,6 +50,8 @@ jobs: - run: pnpm typecheck - run: pnpm lint - run: pnpm conformance + - name: Fallow whole-codebase analysis + run: pnpm fallow --format annotations - run: pnpm turbo boundaries - name: Test with coverage env: From 1655cd0879678e03c24772dde7e1cc5a0d8d85f3 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Wed, 13 May 2026 08:52:09 +0200 Subject: [PATCH 5/8] docs: surface fallow as 5th conformance gate in CLAUDE.md --- CLAUDE.md | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 368d9f0..6413b53 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,23 +39,24 @@ Turborepo + pnpm monorepo organized by vertical features. Each feature (`auth`, - `docs/architecture/vertical-feature-spec.md` — Design spec with rationale and decision log - `docs/guides/scaffolding-a-feature.md` — `turbo gen feature` reference (fast path; prefer this over the manual walkthrough) - `docs/guides/adding-a-feature.md` — End-to-end new feature walkthrough (manual path; for cases the generator's Phase-1 scope doesn't cover) -- `docs/guides/events-and-jobs.md` — publish/consume/schedule cookbook (cross-feature events + background jobs; *requires `gen core-package events`*) -- `docs/guides/realtime.md` — Socket.IO channels, broadcasts, handlers (*requires `gen core-package realtime`*) -- `docs/guides/audit-and-compliance.md` — DPA-compliant audit logging cookbook (*requires `gen core-package audit`*) +- `docs/guides/events-and-jobs.md` — publish/consume/schedule cookbook (cross-feature events + background jobs; _requires `gen core-package events`_) +- `docs/guides/realtime.md` — Socket.IO channels, broadcasts, handlers (_requires `gen core-package realtime`_) +- `docs/guides/audit-and-compliance.md` — DPA-compliant audit logging cookbook (_requires `gen core-package audit`_) - `docs/architecture/template-tiers.md` — must-have vs optional packages and how to scaffold the optionals ## Conformance system -Every feature has a `src/feature.manifest.ts` declaring its use cases, audits, publishes, consumes, and required cores. Drift is caught at four latencies: +Every feature has a `src/feature.manifest.ts` declaring its use cases, audits, publishes, consumes, and required cores. Drift is caught at five latencies: -| Layer | Latency | Catches | -|---|---|---| -| **TypeScript brands** | 0s | forgotten `withSpan` / `withCapture` / `withAudit` at bind time | -| **ESLint rules** | <1s | manifest ↔ code drift; undeclared `bus.publish` / `auditLog.record`; missing manifest; missing sibling test | -| **Boot assertion** (`pnpm dev`) | ~3s | binding without required brand at runtime; manifest edited without rebinder | -| **CI drift gate** (`pnpm conformance`) | ~120s | orphan event consumers across features | +| Layer | Latency | Catches | +| -------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------- | +| **TypeScript brands** | 0s | forgotten `withSpan` / `withCapture` / `withAudit` at bind time | +| **ESLint rules** | <1s | manifest ↔ code drift; undeclared `bus.publish` / `auditLog.record`; missing manifest; missing sibling test | +| **Boot assertion** (`pnpm dev`) | ~3s | binding without required brand at runtime; manifest edited without rebinder | +| **CI drift gate** (`pnpm conformance`) | ~120s | orphan event consumers across features | +| **Fallow** (`pnpm fallow`) | ~30–60s | dead exports / unused files; duplicate code; circular deps; complexity hotspots; AI-change audit drift | -The five conformance ESLint rules: `feature-must-have-manifest` (error), `usecase-must-have-test-file` (error), `required-cores-installed` (error), `no-undeclared-event-publish` (warn), `no-undeclared-audit` (warn). +The five conformance ESLint rules: `feature-must-have-manifest` (error), `usecase-must-have-test-file` (error), `required-cores-installed` (error), `no-undeclared-event-publish` (warn), `no-undeclared-audit` (warn). Fallow runs as a fifth layer, post-ESLint, whole-codebase. See `docs/architecture/agent-first-workflow-and-conformance.md` for the full design and `docs/guides/conformance-quickref.md` for the day-to-day reference. @@ -83,11 +84,11 @@ See `docs/architecture/agent-first-workflow-and-conformance.md` for the full des - **PII handling is non-negotiable** — `sendDefaultPii: false` everywhere (R31, CI grep gate); replay default-masks all text/inputs/media (R34, R35, allowlist starts empty); `setUser({ id })` only — no email/username (R36); server-side PII scrubbing happens at the OTel processor layer (`PiiScrubSpanProcessor` + `PiiScrubLogRecordProcessor`) before any exporter sees the data (R32, R33, ADR-017 §7) - **Three apps, three Sentry projects** — `WEB_NEXT_SENTRY_DSN`, `CMS_SENTRY_DSN`, `WEB_TANSTACK_SENTRY_DSN`. Browser DSNs use `NEXT_PUBLIC_` (web-next) and `VITE_` (web-tanstack) prefixes - **Instrumentation binding is orthogonal to repo binding** — `bindAll()`'s Rule 0 (DSN → OTel+Sentry vs Noop) is independent of `USE_DEV_SEED` / `NODE_ENV`. Run `pnpm dev` with `WEB_NEXT_SENTRY_DSN` set to test the integration locally -- **Cross-feature events go through `IEventBus` (E0)** — In-feature reactions are direct use-case calls, not bus publishes. The bus is for *crossing* feature boundaries (e.g. `auth` → `marketing-pages` welcome email) +- **Cross-feature events go through `IEventBus` (E0)** — In-feature reactions are direct use-case calls, not bus publishes. The bus is for _crossing_ feature boundaries (e.g. `auth` → `marketing-pages` welcome email) - **Event contracts are public; handlers are private (E1)** — Publisher's `events/.event.ts` is exported from the feature root barrel. Consumer's `events/handlers/on--.handler.ts` is never re-exported (ESLint-enforced via `core-eslint/rules/no-handler-reexport`) -- **Jobs are for *deferred* work, not abstraction (J0)** — Synchronous code stays synchronous. A job exists only when something must run off the request path (latency, retries, cron). Feature packages enqueue via `IJobQueue` only — direct `payload.jobs.queue()` is ESLint-blocked outside `core-shared/jobs/` +- **Jobs are for _deferred_ work, not abstraction (J0)** — Synchronous code stays synchronous. A job exists only when something must run off the request path (latency, retries, cron). Feature packages enqueue via `IJobQueue` only — direct `payload.jobs.queue()` is ESLint-blocked outside `core-shared/jobs/` - **Realtime is for state delivery, not for replacing tRPC (R0)** — Persistent request/response operations belong on tRPC procedures. Use realtime when the server needs to push without a request or the data is too high-frequency for HTTP -- **Realtime channel descriptors are exported; handlers are private (R1)** — A feature's `realtime/.channel.ts` is re-exported from the root barrel; `realtime/handlers/*.handler.ts` is wired only in bind-* files and never re-exported (ESLint-enforced via `no-realtime-handler-reexport`) +- **Realtime channel descriptors are exported; handlers are private (R1)** — A feature's `realtime/.channel.ts` is re-exported from the root barrel; `realtime/handlers/*.handler.ts` is wired only in bind-\* files and never re-exported (ESLint-enforced via `no-realtime-handler-reexport`) - **`socket.io` lives in `@repo/core-realtime` only (R2)** — Feature packages MUST NOT import `socket.io` or `socket.io-client`. ESLint rule `no-direct-socket-io` enforces this; allowlist covers `core-realtime/src/socket-io-*.ts` and `apps/*/server.ts` - **Manifest-first ordering** — for any new use case, the workflow is **(1) manifest entry** → **(2) contracts** (`xInputSchema`, `xOutputSchema`, `IXUseCase`) → **(3) tests (red)** → **(4) implementation (green)**. The generator emits the manifest + a self-asserting `bind-production.ts` so new features are conformance-compliant by default - **Self-asserting `bindProductionX(ctx)`** — every feature's bind-production calls `assertFeatureConformance(container, manifest, symbols, ctx)` at its tail. `pnpm dev` refuses to boot on drift @@ -101,10 +102,10 @@ Storybook MCP available at `http://localhost:6006/mcp` — use `list-all-documen ## Key Ports -| Service | Port | -|---|---| -| Next.js | 3000 | -| Payload CMS | 3001 | +| Service | Port | +| -------------- | ---- | +| Next.js | 3000 | +| Payload CMS | 3001 | | TanStack Start | 3002 | -| PostgreSQL | 5432 | -| Storybook | 6006 | +| PostgreSQL | 5432 | +| Storybook | 6006 | From d34b841256e2179d1a311d65b40d989cec3f394b Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Wed, 13 May 2026 08:52:37 +0200 Subject: [PATCH 6/8] docs(guides): conformance-quickref includes fallow row + audit usage --- docs/guides/conformance-quickref.md | 61 +++++++++++++++++------------ 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/docs/guides/conformance-quickref.md b/docs/guides/conformance-quickref.md index 8d6847b..b7783a0 100644 --- a/docs/guides/conformance-quickref.md +++ b/docs/guides/conformance-quickref.md @@ -32,16 +32,16 @@ export type FooManifest = typeof fooManifest; Field reference: -| Field | Type | Meaning | -|---|---|---| -| `name` | string literal | Feature name (kebab-case, matches package name) | -| `requiredCores` | string[] | Optional cores this feature requires (e.g. `["audit", "events"]`) | -| `useCases..mutates` | boolean | True for create/update/delete; drives whether `__audited` brand is required | -| `useCases..audits` | string[] | Audit event types this use case emits via `auditLog.record({ type: "X" })` | -| `useCases..publishes` | string[] | Cross-feature events this use case publishes via `bus.publish("X")` | -| `useCases..consumes` | string[] | Cross-feature events this use case consumes (via an event handler) | -| `realtimeChannels` | string[] | Realtime channels this feature owns | -| `jobs` | string[] | Job slugs this feature enqueues | +| Field | Type | Meaning | +| --------------------------- | -------------- | --------------------------------------------------------------------------- | +| `name` | string literal | Feature name (kebab-case, matches package name) | +| `requiredCores` | string[] | Optional cores this feature requires (e.g. `["audit", "events"]`) | +| `useCases..mutates` | boolean | True for create/update/delete; drives whether `__audited` brand is required | +| `useCases..audits` | string[] | Audit event types this use case emits via `auditLog.record({ type: "X" })` | +| `useCases..publishes` | string[] | Cross-feature events this use case publishes via `bus.publish("X")` | +| `useCases..consumes` | string[] | Cross-feature events this use case consumes (via an event handler) | +| `realtimeChannels` | string[] | Realtime channels this feature owns | +| `jobs` | string[] | Job slugs this feature enqueues | Re-export from `src/index.ts`: @@ -74,24 +74,25 @@ export function bindProductionFoo(ctx: BindProductionContext): void { The symbol map declares which container symbol each manifest use-case key resolves to. -## The four gates +## The five gates -| Gate | When it fires | What it catches | Severity | -|---|---|---|---| -| `tsc` | on save | forgotten wrappers; manifest-derived slot type rejects unwrapped factory | error | -| `eslint` | on save / `pnpm lint` | manifest ↔ code drift; missing sibling test; missing manifest | error or warn | -| `pnpm dev` | at boot | binding lost its runtime brand; manifest declares more than wired | throws synchronously | -| `pnpm conformance` | CI | orphan event consumers across features | exits non-zero | +| Gate | When it fires | What it catches | Severity | +| ------------------ | --------------------- | ------------------------------------------------------------------------ | -------------------- | +| `tsc` | on save | forgotten wrappers; manifest-derived slot type rejects unwrapped factory | error | +| `eslint` | on save / `pnpm lint` | manifest ↔ code drift; missing sibling test; missing manifest | error or warn | +| `pnpm dev` | at boot | binding lost its runtime brand; manifest declares more than wired | throws synchronously | +| `pnpm conformance` | CI | orphan event consumers across features | exits non-zero | +| `pnpm fallow` | ~30–60s | unused exports/files, dupes, circular deps, complexity, AI-change audit | warn (currently) | ## ESLint rules -| Rule | Severity | What it does | -|---|---|---| -| `conformance/feature-must-have-manifest` | error | Use-case files require a sibling manifest | -| `conformance/usecase-must-have-test-file` | error | Every `*.use-case.ts` has a sibling `*.use-case.test.ts` | -| `conformance/required-cores-installed` | error | Manifest's `requiredCores` must exist as `core-` packages in pnpm-workspace.yaml | -| `conformance/no-undeclared-event-publish` | warn | `bus.publish("X")` literal must match the manifest's `publishes` for the use case | -| `conformance/no-undeclared-audit` | warn | `auditLog.record({ type: "X" })` literal must match the manifest's `audits` | +| Rule | Severity | What it does | +| ----------------------------------------- | -------- | -------------------------------------------------------------------------------------- | +| `conformance/feature-must-have-manifest` | error | Use-case files require a sibling manifest | +| `conformance/usecase-must-have-test-file` | error | Every `*.use-case.ts` has a sibling `*.use-case.test.ts` | +| `conformance/required-cores-installed` | error | Manifest's `requiredCores` must exist as `core-` packages in pnpm-workspace.yaml | +| `conformance/no-undeclared-event-publish` | warn | `bus.publish("X")` literal must match the manifest's `publishes` for the use case | +| `conformance/no-undeclared-audit` | warn | `auditLog.record({ type: "X" })` literal must match the manifest's `audits` | ## Workflow ordering for new use cases @@ -119,6 +120,18 @@ When a gate fires, the error message tells you what to run. For example: That's the "fix" line — follow it. +## Fallow audit for AI changes + +When you (the agent) finish a task and are about to commit, run: + +``` +pnpm fallow:audit +``` + +This runs `fallow audit --base main`, comparing your branch's diff against main. If your change adds dead exports, dupes, or complexity hotspots, fallow tells you exactly what and where. Fix or accept (with --gate flag to ignore inherited findings). + +This is the catch-all for whole-codebase drift the per-file gates can't see. + --- For the deeper design rationale see `docs/architecture/agent-first-workflow-and-conformance.md` and the interactive `feature-conformance-explainer.html`. From 7ecb365e63415f134d7a7279560d5e2cdc60132c Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Wed, 13 May 2026 08:53:00 +0200 Subject: [PATCH 7/8] feat(sandcastle): implementer + reviewer prompts include fallow audit --- .sandcastle/implementer.prompt.md | 3 ++- .sandcastle/reviewer.prompt.md | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.sandcastle/implementer.prompt.md b/.sandcastle/implementer.prompt.md index c088a50..1fbefa9 100644 --- a/.sandcastle/implementer.prompt.md +++ b/.sandcastle/implementer.prompt.md @@ -41,9 +41,10 @@ pnpm typecheck # TS brand-slot enforcement, 0s pnpm lint # ESLint rules incl. conformance/* — <1s pnpm test --filter @repo/ # tests for the feature you touched pnpm conformance # cross-feature event closure +pnpm fallow:audit # whole-codebase analysis: dead exports, dupes, circular deps, complexity ``` -All four pass before you commit. If any fail, fix or report BLOCKED — do not paper over. +All five pass before you commit. If any fail, fix or report BLOCKED — do not paper over. ## Commit message format diff --git a/.sandcastle/reviewer.prompt.md b/.sandcastle/reviewer.prompt.md index 0c08395..5fe2330 100644 --- a/.sandcastle/reviewer.prompt.md +++ b/.sandcastle/reviewer.prompt.md @@ -31,6 +31,7 @@ If you suspect the implementer hand-rolled what should have been generator outpu 3. **Manifest-first ordering**: if a new use case landed, the manifest was updated; tests exist; the factory was wrapped at bind time. 4. **Conformance gates**: the diff's tests + lint + typecheck pass. (You don't run them yourself; sandcastle's CI step does. Trust the CI status, reject if it's red.) 5. **Generator-first**: see the section above. Hand-rolled code that should have been generated is a rejection. +6. **Fallow audit**: verify the implementer ran `pnpm fallow:audit` and it passed. If their diff increases dead exports / dupes / circular deps / complexity beyond the baseline, that's a rejection cause unless the implementer's notes explicitly justify it. ## Output format From 072e08d855f54148ee46785c6317da36ecf94ab3 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Wed, 13 May 2026 08:55:50 +0200 Subject: [PATCH 8/8] docs(work): close fallow-integration-v1 epic --- docs/work/_state.json | 55 ++++--------------- .../01-fallow-install/_story.md | 10 ++-- .../02-pnpm-turbo-wiring/_story.md | 10 ++-- .../03-ci-integration/_story.md | 6 +- .../04-docs-and-prompts/_story.md | 12 ++-- docs/work/fallow-integration-v1/_epic.md | 10 ++-- 6 files changed, 36 insertions(+), 67 deletions(-) diff --git a/docs/work/_state.json b/docs/work/_state.json index bfe0403..a146b8f 100644 --- a/docs/work/_state.json +++ b/docs/work/_state.json @@ -1,5 +1,5 @@ { - "updated_at": "2026-05-13T06:47:32.677Z", + "updated_at": "2026-05-13T06:55:52.068Z", "epics": { "agent-workflow-docs-v1": { "status": "done", @@ -203,24 +203,24 @@ } }, "fallow-integration-v1": { - "status": "in-progress", + "status": "done", "title": "Fallow integration v1 — whole-codebase static analysis as 5th gate", "stories": { "01-fallow-install": { - "status": "in-progress", + "status": "done", "title": "Install fallow + create .fallowrc.json", "ac_total": 4, - "ac_completed": 0, + "ac_completed": 4, "depends_on": [], "blocks": [ "02-pnpm-turbo-wiring" ] }, "02-pnpm-turbo-wiring": { - "status": "todo", + "status": "done", "title": "pnpm script + turbo task wiring", "ac_total": 4, - "ac_completed": 0, + "ac_completed": 4, "depends_on": [ "01-fallow-install" ], @@ -229,10 +229,10 @@ ] }, "03-ci-integration": { - "status": "todo", + "status": "done", "title": "CI workflow runs pnpm fallow", "ac_total": 2, - "ac_completed": 0, + "ac_completed": 2, "depends_on": [ "02-pnpm-turbo-wiring" ], @@ -241,10 +241,10 @@ ] }, "04-docs-and-prompts": { - "status": "todo", + "status": "done", "title": "Docs + agent prompt updates", "ac_total": 5, - "ac_completed": 0, + "ac_completed": 5, "depends_on": [ "03-ci-integration" ], @@ -409,37 +409,6 @@ } } }, - "ready": [ - { - "epic": "fallow-integration-v1", - "story": "01-fallow-install", - "title": "Install fallow + create .fallowrc.json" - } - ], - "blocked": [ - { - "epic": "fallow-integration-v1", - "story": "02-pnpm-turbo-wiring", - "title": "pnpm script + turbo task wiring", - "waiting_on": [ - "fallow-integration-v1/01-fallow-install" - ] - }, - { - "epic": "fallow-integration-v1", - "story": "03-ci-integration", - "title": "CI workflow runs pnpm fallow", - "waiting_on": [ - "fallow-integration-v1/02-pnpm-turbo-wiring" - ] - }, - { - "epic": "fallow-integration-v1", - "story": "04-docs-and-prompts", - "title": "Docs + agent prompt updates", - "waiting_on": [ - "fallow-integration-v1/03-ci-integration" - ] - } - ] + "ready": [], + "blocked": [] } diff --git a/docs/work/fallow-integration-v1/01-fallow-install/_story.md b/docs/work/fallow-integration-v1/01-fallow-install/_story.md index a69c9e9..c13f3a0 100644 --- a/docs/work/fallow-integration-v1/01-fallow-install/_story.md +++ b/docs/work/fallow-integration-v1/01-fallow-install/_story.md @@ -3,7 +3,7 @@ id: 01-fallow-install epic: fallow-integration-v1 title: Install fallow + create .fallowrc.json type: technical-story -status: in-progress +status: done feature: tooling depends-on: [] blocks: [02-pnpm-turbo-wiring] @@ -11,7 +11,7 @@ blocks: [02-pnpm-turbo-wiring] ## Tasks -- [ ] Story scaffold -- [ ] Add fallow to root devDependencies -- [ ] Create .fallowrc.json with monorepo-aware config -- [ ] Verify `pnpm exec fallow --version` works +- [x] Story scaffold +- [x] Add fallow to root devDependencies +- [x] Create .fallowrc.json with monorepo-aware config +- [x] Verify `pnpm exec fallow --version` works diff --git a/docs/work/fallow-integration-v1/02-pnpm-turbo-wiring/_story.md b/docs/work/fallow-integration-v1/02-pnpm-turbo-wiring/_story.md index 3016153..37bec9a 100644 --- a/docs/work/fallow-integration-v1/02-pnpm-turbo-wiring/_story.md +++ b/docs/work/fallow-integration-v1/02-pnpm-turbo-wiring/_story.md @@ -3,7 +3,7 @@ id: 02-pnpm-turbo-wiring epic: fallow-integration-v1 title: pnpm script + turbo task wiring type: technical-story -status: todo +status: done feature: tooling depends-on: [01-fallow-install] blocks: [03-ci-integration] @@ -11,7 +11,7 @@ blocks: [03-ci-integration] ## Tasks -- [ ] pnpm fallow script (runs all) -- [ ] pnpm fallow:audit script (runs `fallow audit --base main`) -- [ ] turbo.json task entry for fallow -- [ ] Run `pnpm fallow` against current repo, capture findings, tune config +- [x] pnpm fallow script (runs all) +- [x] pnpm fallow:audit script (runs `fallow audit --base main`) +- [x] turbo.json task entry for fallow +- [x] Run `pnpm fallow` against current repo, capture findings, tune config diff --git a/docs/work/fallow-integration-v1/03-ci-integration/_story.md b/docs/work/fallow-integration-v1/03-ci-integration/_story.md index 631fcc5..ce17638 100644 --- a/docs/work/fallow-integration-v1/03-ci-integration/_story.md +++ b/docs/work/fallow-integration-v1/03-ci-integration/_story.md @@ -3,7 +3,7 @@ id: 03-ci-integration epic: fallow-integration-v1 title: CI workflow runs pnpm fallow type: technical-story -status: todo +status: done feature: ci depends-on: [02-pnpm-turbo-wiring] blocks: [04-docs-and-prompts] @@ -11,5 +11,5 @@ blocks: [04-docs-and-prompts] ## Tasks -- [ ] Add `pnpm fallow` step after `pnpm conformance` in ci.yml -- [ ] Use --format annotations for inline GH PR comments +- [x] Add `pnpm fallow` step after `pnpm conformance` in ci.yml +- [x] Use --format annotations for inline GH PR comments diff --git a/docs/work/fallow-integration-v1/04-docs-and-prompts/_story.md b/docs/work/fallow-integration-v1/04-docs-and-prompts/_story.md index 7cd200b..db9580a 100644 --- a/docs/work/fallow-integration-v1/04-docs-and-prompts/_story.md +++ b/docs/work/fallow-integration-v1/04-docs-and-prompts/_story.md @@ -3,7 +3,7 @@ id: 04-docs-and-prompts epic: fallow-integration-v1 title: Docs + agent prompt updates type: technical-story -status: todo +status: done feature: agent-prompts depends-on: [03-ci-integration] blocks: [] @@ -11,8 +11,8 @@ blocks: [] ## Tasks -- [ ] Update CLAUDE.md conformance-system section to list Fallow as 5th gate -- [ ] Update docs/guides/conformance-quickref.md with Fallow row -- [ ] Update .sandcastle/implementer.prompt.md: run `fallow audit` before commit -- [ ] Update .sandcastle/reviewer.prompt.md: verify implementer ran fallow audit -- [ ] Final verification + closeout +- [x] Update CLAUDE.md conformance-system section to list Fallow as 5th gate +- [x] Update docs/guides/conformance-quickref.md with Fallow row +- [x] Update .sandcastle/implementer.prompt.md: run `fallow audit` before commit +- [x] Update .sandcastle/reviewer.prompt.md: verify implementer ran fallow audit +- [x] Final verification + closeout diff --git a/docs/work/fallow-integration-v1/_epic.md b/docs/work/fallow-integration-v1/_epic.md index 65c68f7..d73c9b1 100644 --- a/docs/work/fallow-integration-v1/_epic.md +++ b/docs/work/fallow-integration-v1/_epic.md @@ -3,7 +3,7 @@ id: fallow-integration-v1 prd: null title: Fallow integration v1 — whole-codebase static analysis as 5th gate type: epic -status: in-progress +status: done features: [tooling, ci, agent-prompts] created: 2026-05-13 --- @@ -22,7 +22,7 @@ purpose-built for AI agents to self-validate before submission. ## Stories -- [ ] [01 — Install fallow + .fallowrc.json](01-fallow-install/_story.md) -- [ ] [02 — pnpm + turbo wiring](02-pnpm-turbo-wiring/_story.md) -- [ ] [03 — CI workflow integration](03-ci-integration/_story.md) -- [ ] [04 — Docs + agent prompt updates](04-docs-and-prompts/_story.md) +- [x] [01 — Install fallow + .fallowrc.json](01-fallow-install/_story.md) +- [x] [02 — pnpm + turbo wiring](02-pnpm-turbo-wiring/_story.md) +- [x] [03 — CI workflow integration](03-ci-integration/_story.md) +- [x] [04 — Docs + agent prompt updates](04-docs-and-prompts/_story.md)