From 123515c6e48cf53b8aaa55542c4c71d3c6462cb7 Mon Sep 17 00:00:00 2001 From: alfredorubin96 Date: Sat, 25 Jul 2026 03:15:37 +0200 Subject: [PATCH 1/3] chore(docs): correct false claims in CLAUDE.md and ARCHITECTURE.md (#1235) CLAUDE.md is loaded as ground truth by agent sessions, so stale claims there become wrong assumptions in generated code. - chart-registry -> chart-plugin-registry (app/src/lib/chart-registry.ts does not exist; the registry is lib/plugin/chart-plugin-registry.ts + plugins/) - multi-tenancy: describe the actual per-query enforcement instead of claiming ORM/middleware-level enforcement that does not exist; link #1226 - migrations: MIGRATE_ON_START=0, not a nonexistent --skip-migrations flag - ARCHITECTURE.md counts: 38 ui primitives (was 33), BaseChart + 14 chart modules (was 12) Adds app/src/lib/__tests__/docs-accuracy.test.ts to guard against recurrence: path references must resolve, stated counts must match the filesystem, and a reworded claim fails loudly rather than silently going unchecked. Co-Authored-By: Claude Fable 5 --- .claude/launch.json | 14 +++ ARCHITECTURE.md | 4 +- CLAUDE.md | 7 +- app/src/lib/__tests__/docs-accuracy.test.ts | 97 +++++++++++++++++++++ 4 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 .claude/launch.json create mode 100644 app/src/lib/__tests__/docs-accuracy.test.ts diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 00000000..ac922a6a --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,14 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "storybook", + "runtimeExecutable": "bash", + "runtimeArgs": [ + "-c", + "export PATH=\"$HOME/.nvm/versions/node/v22.21.1/bin:$PATH\"; exec npm run storybook" + ], + "port": 6006 + } + ] +} diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 74947a43..40504614 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -197,9 +197,9 @@ app/src/ └── proxy.ts # Edge middleware (auth guard) component/src/ -├── charts/ # ECharts wrappers (BaseChart + 12 types) +├── charts/ # ECharts wrappers (BaseChart + 14 types) ├── components/ -│ ├── ui/ # 33 shadcn/ui primitives +│ ├── ui/ # 38 shadcn/ui primitives │ └── composed/ # 42 higher-order components ├── hooks/ # useWidgetSize, useContainerSize └── lib/ # Utilities, design tokens, Cypher language diff --git a/CLAUDE.md b/CLAUDE.md index 86cce60d..c1b91d52 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,7 +52,7 @@ Rules: | Layer | Tool | Examples | | -------------------- | ----------------------- | -------------------------------------------------------------------------------- | -| Pure functions/utils | Vitest (no DOM) | chart-registry, normalize-value, date-utils, query-hash, wrap-with-preview-limit | +| Pure functions/utils | Vitest (no DOM) | chart-plugin-registry, normalize-value, date-utils, query-hash, wrap-with-preview-limit | | API routes | Vitest (mocked DB/auth) | Validation, permissions, error handling | | Zustand stores | Vitest (no mocks) | State transitions, cascading logic | | Store orchestration | Vitest (no DOM) | parameter-widget-renderer interactions, type coercion | @@ -125,7 +125,8 @@ Playwright E2E with **server-side coverage collection** (`collectServer: true` i ## Multi-Tenancy -- `tenant_id` column on ALL tables. Every DB query MUST include tenant filter at ORM/middleware level. +- `tenant_id` column on ALL tables. Every DB query MUST include an explicit tenant filter — `eq(table.tenantId, session.tenantId)` — written **per query, in the route**. There is no ORM-level or middleware-level enforcement today (`lib/db/index.ts` is a plain Drizzle client), so a forgotten filter is a cross-tenant leak that nothing catches. Adding a guard is tracked in #1226. +- Take `tenantId` from `requireSession()`, NEVER from the request body. - JWT tokens include `tenantId` claim. Validate before ANY DB or API access. - SaaS vs on-prem: env vars only, never code branches. @@ -144,7 +145,7 @@ Includes: SSO, Custom Roles, Connector Labels, Bulk Import, Connector CRUD API, ## Migrations Forward-only. Idempotent. Advisory lock prevents concurrent runs. -Test version-skip paths. `--skip-migrations` flag exists for emergency debugging. +Test version-skip paths. Boot migrations are controlled by `MIGRATE_ON_START` (`1`/`true` to run; set `0` to skip for emergency debugging) — there is no `--skip-migrations` CLI flag. ## Automated Guardrails (Hooks) diff --git a/app/src/lib/__tests__/docs-accuracy.test.ts b/app/src/lib/__tests__/docs-accuracy.test.ts new file mode 100644 index 00000000..a75bbd78 --- /dev/null +++ b/app/src/lib/__tests__/docs-accuracy.test.ts @@ -0,0 +1,97 @@ +import { describe, it, expect } from "vitest"; +import { readFileSync, existsSync, readdirSync } from "node:fs"; +import { resolve, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +/** + * Guards the repo's own documentation against drift (#1235). + * + * CLAUDE.md is loaded as ground truth by agent sessions, so a stale path or + * count there becomes a wrong assumption in generated code. These tests fail + * loudly instead. + */ + +const REPO_ROOT = resolve( + dirname(fileURLToPath(import.meta.url)), + "../../../..", +); + +const readDoc = (name: string) => + readFileSync(resolve(REPO_ROOT, name), "utf8"); + +/** Top-level dirs that make a backticked token a repo path rather than an npm specifier. */ +const REPO_PREFIXES = [ + "app/", + "component/", + "connection/", + "connector-sdk/", + "cli/", + "docs/", + "scripts/", + ".claude/", + ".github/", + "docker/", +]; + +function referencedPaths(markdown: string): string[] { + const backticked = markdown.match(/`[^`\s]+`/g) ?? []; + return [ + ...new Set( + backticked + .map((t) => t.slice(1, -1)) + .filter((t) => REPO_PREFIXES.some((p) => t.startsWith(p))), + ), + ]; +} + +const countFiles = (dir: string, ext = ".tsx") => + readdirSync(resolve(REPO_ROOT, dir)).filter((f) => f.endsWith(ext)).length; + +describe("documentation accuracy", () => { + describe.each(["CLAUDE.md", "ARCHITECTURE.md"])("%s", (docName) => { + it("references only file paths that exist", () => { + const missing = referencedPaths(readDoc(docName)).filter( + (p) => !existsSync(resolve(REPO_ROOT, p)), + ); + expect(missing).toEqual([]); + }); + }); + + describe("ARCHITECTURE.md component counts match the filesystem", () => { + // Each regex must match: a reworded claim should fail here rather than + // silently stop being checked. + it.each([ + [ + "shadcn/ui primitives", + /(\d+) shadcn\/ui primitives/, + () => countFiles("component/src/components/ui"), + ], + [ + "composed components", + /(\d+) higher-order components/, + () => countFiles("component/src/components/composed"), + ], + [ + "chart modules", + /BaseChart \+ (\d+) types/, + // charts/ holds base-chart.tsx plus one module per chart type. + () => countFiles("component/src/charts") - 1, + ], + ])("%s", (_label, pattern, actual) => { + const match = readDoc("ARCHITECTURE.md").match(pattern); + expect( + match, + `claim matching ${pattern} not found — was it reworded?`, + ).not.toBeNull(); + expect(Number(match![1])).toBe(actual()); + }); + }); + + it("CLAUDE.md documents MIGRATE_ON_START, not a --skip-migrations flag", () => { + // Naming the flag to debunk it is fine (readers search for it); asserting + // it exists is not. The real escape hatch is MIGRATE_ON_START=0 (#1222). + const doc = readDoc("CLAUDE.md"); + expect(doc).toContain("MIGRATE_ON_START"); + expect(doc).not.toMatch(/`--skip-migrations`\s+flag\s+exists/); + }); +}); From b0a4861330934caf95180ca4a87572f4a756f0da Mon Sep 17 00:00:00 2001 From: alfredorubin96 Date: Sat, 25 Jul 2026 03:38:59 +0200 Subject: [PATCH 2/3] chore: drop stray .claude/launch.json from this PR Local editor config swept in by git add -A; unrelated to the docs fix and not previously tracked. Co-Authored-By: Claude Fable 5 --- .claude/launch.json | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .claude/launch.json diff --git a/.claude/launch.json b/.claude/launch.json deleted file mode 100644 index ac922a6a..00000000 --- a/.claude/launch.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": "0.0.1", - "configurations": [ - { - "name": "storybook", - "runtimeExecutable": "bash", - "runtimeArgs": [ - "-c", - "export PATH=\"$HOME/.nvm/versions/node/v22.21.1/bin:$PATH\"; exec npm run storybook" - ], - "port": 6006 - } - ] -} From b56c97dfd040404076a40aa5ddc1568d65e365e1 Mon Sep 17 00:00:00 2001 From: alfredorubin96 Date: Sat, 25 Jul 2026 13:17:28 +0200 Subject: [PATCH 3/3] chore(docs): address CodeRabbit review on #1235 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CLAUDE.md: use the repo-relative `app/src/lib/db/index.ts` so the new path scanner actually validates it (bare `lib/` is outside the checked prefixes — the guard was silently skipping its own reference) - docs-accuracy: assert the canonical debunk wording is present instead of blocklisting one phrasing; "use --skip-migrations" would have slipped past the negative regex Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 2 +- app/src/lib/__tests__/docs-accuracy.test.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index c1b91d52..888e2f0a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -125,7 +125,7 @@ Playwright E2E with **server-side coverage collection** (`collectServer: true` i ## Multi-Tenancy -- `tenant_id` column on ALL tables. Every DB query MUST include an explicit tenant filter — `eq(table.tenantId, session.tenantId)` — written **per query, in the route**. There is no ORM-level or middleware-level enforcement today (`lib/db/index.ts` is a plain Drizzle client), so a forgotten filter is a cross-tenant leak that nothing catches. Adding a guard is tracked in #1226. +- `tenant_id` column on ALL tables. Every DB query MUST include an explicit tenant filter — `eq(table.tenantId, session.tenantId)` — written **per query, in the route**. There is no ORM-level or middleware-level enforcement today (`app/src/lib/db/index.ts` is a plain Drizzle client), so a forgotten filter is a cross-tenant leak that nothing catches. Adding a guard is tracked in #1226. - Take `tenantId` from `requireSession()`, NEVER from the request body. - JWT tokens include `tenantId` claim. Validate before ANY DB or API access. - SaaS vs on-prem: env vars only, never code branches. diff --git a/app/src/lib/__tests__/docs-accuracy.test.ts b/app/src/lib/__tests__/docs-accuracy.test.ts index a75bbd78..d19e307e 100644 --- a/app/src/lib/__tests__/docs-accuracy.test.ts +++ b/app/src/lib/__tests__/docs-accuracy.test.ts @@ -92,6 +92,8 @@ describe("documentation accuracy", () => { // it exists is not. The real escape hatch is MIGRATE_ON_START=0 (#1222). const doc = readDoc("CLAUDE.md"); expect(doc).toContain("MIGRATE_ON_START"); - expect(doc).not.toMatch(/`--skip-migrations`\s+flag\s+exists/); + // Assert the canonical debunk is present rather than blocklisting one + // phrasing — "use `--skip-migrations`" would slip past a negative regex. + expect(doc).toContain("there is no `--skip-migrations` CLI flag"); }); });