Merge branch 'worktree-fallow-integration-v1': fallow v1 — 5th gate (whole-codebase)
Some checks failed
Some checks failed
This commit is contained in:
62
.fallowrc.json
Normal file
62
.fallowrc.json
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"$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"
|
||||
],
|
||||
"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": 30,
|
||||
"maxCrap": 400
|
||||
}
|
||||
}
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -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:
|
||||
|
||||
@@ -41,9 +41,10 @@ pnpm typecheck # TS brand-slot enforcement, 0s
|
||||
pnpm lint # ESLint rules incl. conformance/* — <1s
|
||||
pnpm test --filter @repo/<feature> # 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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
41
CLAUDE.md
41
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/<x>.event.ts` is exported from the feature root barrel. Consumer's `events/handlers/on-<publisher>-<event>.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/<name>.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/<name>.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 |
|
||||
|
||||
@@ -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.<name>.mutates` | boolean | True for create/update/delete; drives whether `__audited` brand is required |
|
||||
| `useCases.<name>.audits` | string[] | Audit event types this use case emits via `auditLog.record({ type: "X" })` |
|
||||
| `useCases.<name>.publishes` | string[] | Cross-feature events this use case publishes via `bus.publish("X")` |
|
||||
| `useCases.<name>.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.<name>.mutates` | boolean | True for create/update/delete; drives whether `__audited` brand is required |
|
||||
| `useCases.<name>.audits` | string[] | Audit event types this use case emits via `auditLog.record({ type: "X" })` |
|
||||
| `useCases.<name>.publishes` | string[] | Cross-feature events this use case publishes via `bus.publish("X")` |
|
||||
| `useCases.<name>.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-<name>` 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-<name>` 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`.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"updated_at": "2026-05-13T06:32:59.719Z",
|
||||
"updated_at": "2026-05-13T06:55:52.068Z",
|
||||
"epics": {
|
||||
"agent-workflow-docs-v1": {
|
||||
"status": "done",
|
||||
@@ -202,6 +202,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"fallow-integration-v1": {
|
||||
"status": "done",
|
||||
"title": "Fallow integration v1 — whole-codebase static analysis as 5th gate",
|
||||
"stories": {
|
||||
"01-fallow-install": {
|
||||
"status": "done",
|
||||
"title": "Install fallow + create .fallowrc.json",
|
||||
"ac_total": 4,
|
||||
"ac_completed": 4,
|
||||
"depends_on": [],
|
||||
"blocks": [
|
||||
"02-pnpm-turbo-wiring"
|
||||
]
|
||||
},
|
||||
"02-pnpm-turbo-wiring": {
|
||||
"status": "done",
|
||||
"title": "pnpm script + turbo task wiring",
|
||||
"ac_total": 4,
|
||||
"ac_completed": 4,
|
||||
"depends_on": [
|
||||
"01-fallow-install"
|
||||
],
|
||||
"blocks": [
|
||||
"03-ci-integration"
|
||||
]
|
||||
},
|
||||
"03-ci-integration": {
|
||||
"status": "done",
|
||||
"title": "CI workflow runs pnpm fallow",
|
||||
"ac_total": 2,
|
||||
"ac_completed": 2,
|
||||
"depends_on": [
|
||||
"02-pnpm-turbo-wiring"
|
||||
],
|
||||
"blocks": [
|
||||
"04-docs-and-prompts"
|
||||
]
|
||||
},
|
||||
"04-docs-and-prompts": {
|
||||
"status": "done",
|
||||
"title": "Docs + agent prompt updates",
|
||||
"ac_total": 5,
|
||||
"ac_completed": 5,
|
||||
"depends_on": [
|
||||
"03-ci-integration"
|
||||
],
|
||||
"blocks": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"frontend-conformance-v1": {
|
||||
"status": "done",
|
||||
"title": "Frontend conformance rules v1",
|
||||
|
||||
17
docs/work/fallow-integration-v1/01-fallow-install/_story.md
Normal file
17
docs/work/fallow-integration-v1/01-fallow-install/_story.md
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
id: 01-fallow-install
|
||||
epic: fallow-integration-v1
|
||||
title: Install fallow + create .fallowrc.json
|
||||
type: technical-story
|
||||
status: done
|
||||
feature: tooling
|
||||
depends-on: []
|
||||
blocks: [02-pnpm-turbo-wiring]
|
||||
---
|
||||
|
||||
## Tasks
|
||||
|
||||
- [x] Story scaffold
|
||||
- [x] Add fallow to root devDependencies
|
||||
- [x] Create .fallowrc.json with monorepo-aware config
|
||||
- [x] Verify `pnpm exec fallow --version` works
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
id: 02-pnpm-turbo-wiring
|
||||
epic: fallow-integration-v1
|
||||
title: pnpm script + turbo task wiring
|
||||
type: technical-story
|
||||
status: done
|
||||
feature: tooling
|
||||
depends-on: [01-fallow-install]
|
||||
blocks: [03-ci-integration]
|
||||
---
|
||||
|
||||
## Tasks
|
||||
|
||||
- [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
|
||||
15
docs/work/fallow-integration-v1/03-ci-integration/_story.md
Normal file
15
docs/work/fallow-integration-v1/03-ci-integration/_story.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: 03-ci-integration
|
||||
epic: fallow-integration-v1
|
||||
title: CI workflow runs pnpm fallow
|
||||
type: technical-story
|
||||
status: done
|
||||
feature: ci
|
||||
depends-on: [02-pnpm-turbo-wiring]
|
||||
blocks: [04-docs-and-prompts]
|
||||
---
|
||||
|
||||
## Tasks
|
||||
|
||||
- [x] Add `pnpm fallow` step after `pnpm conformance` in ci.yml
|
||||
- [x] Use --format annotations for inline GH PR comments
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
id: 04-docs-and-prompts
|
||||
epic: fallow-integration-v1
|
||||
title: Docs + agent prompt updates
|
||||
type: technical-story
|
||||
status: done
|
||||
feature: agent-prompts
|
||||
depends-on: [03-ci-integration]
|
||||
blocks: []
|
||||
---
|
||||
|
||||
## Tasks
|
||||
|
||||
- [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
|
||||
28
docs/work/fallow-integration-v1/_epic.md
Normal file
28
docs/work/fallow-integration-v1/_epic.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
id: fallow-integration-v1
|
||||
prd: null
|
||||
title: Fallow integration v1 — whole-codebase static analysis as 5th gate
|
||||
type: epic
|
||||
status: done
|
||||
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
|
||||
|
||||
- [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)
|
||||
@@ -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}\"",
|
||||
@@ -25,6 +27,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",
|
||||
|
||||
112
pnpm-lock.yaml
generated
112
pnpm-lock.yaml
generated
@@ -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
|
||||
|
||||
11
turbo.json
11
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/**"]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user