From e7e078ddae1c02c0e989941665c718ecedd882f8 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Sun, 3 May 2026 19:43:43 +0300 Subject: [PATCH 1/4] chore(fixture): enrich fixtures/minimal to exercise every codemap surface --- .../minimal/.codemap/recipes/shop-symbols.md | 8 ++++ .../minimal/.codemap/recipes/shop-symbols.sql | 8 ++++ fixtures/minimal/CODEOWNERS | 10 +++++ fixtures/minimal/README.md | 40 ++++++++++++++++--- fixtures/minimal/src/api/client.ts | 37 ++++++++++++++++- .../src/components/shop/ProductCard.tsx | 19 +++++++++ .../src/components/shop/ShopButton.default.ts | 4 ++ .../src/components/shop/ShopButton.tsx | 9 ++++- fixtures/minimal/src/components/shop/index.ts | 7 ++++ fixtures/minimal/src/consumer.ts | 10 ++++- fixtures/minimal/src/lib/cache.ts | 20 ++++++++++ fixtures/minimal/src/lib/store.ts | 14 +++++++ fixtures/minimal/src/notes.md | 7 ++++ fixtures/minimal/src/utils/date.ts | 14 +++++++ fixtures/minimal/src/utils/format.ts | 15 +++++++ lint-staged.config.js | 16 +++++++- 16 files changed, 228 insertions(+), 10 deletions(-) create mode 100644 fixtures/minimal/.codemap/recipes/shop-symbols.md create mode 100644 fixtures/minimal/.codemap/recipes/shop-symbols.sql create mode 100644 fixtures/minimal/CODEOWNERS create mode 100644 fixtures/minimal/src/components/shop/ProductCard.tsx create mode 100644 fixtures/minimal/src/components/shop/ShopButton.default.ts create mode 100644 fixtures/minimal/src/components/shop/index.ts create mode 100644 fixtures/minimal/src/lib/cache.ts create mode 100644 fixtures/minimal/src/lib/store.ts create mode 100644 fixtures/minimal/src/utils/format.ts diff --git a/fixtures/minimal/.codemap/recipes/shop-symbols.md b/fixtures/minimal/.codemap/recipes/shop-symbols.md new file mode 100644 index 0000000..4c013e2 --- /dev/null +++ b/fixtures/minimal/.codemap/recipes/shop-symbols.md @@ -0,0 +1,8 @@ +--- +actions: + - type: review-shop-surface + auto_fixable: false + description: "Public shop component surface — review when adding/removing components." +--- + +Shop component public surface — every exported function under `src/components/shop/`. Used for change-impact reviews when the shop UI evolves. diff --git a/fixtures/minimal/.codemap/recipes/shop-symbols.sql b/fixtures/minimal/.codemap/recipes/shop-symbols.sql new file mode 100644 index 0000000..b4b1002 --- /dev/null +++ b/fixtures/minimal/.codemap/recipes/shop-symbols.sql @@ -0,0 +1,8 @@ +-- Project-local recipe — exercises `/recipes/.{sql,md}` discovery +-- and the `codemap query --recipe ` path. Frontmatter actions in the +-- sibling `.md` file attach a per-row hint to JSON output. +SELECT name, file_path, line_start, signature +FROM symbols +WHERE file_path LIKE 'src/components/shop/%' + AND kind = 'function' +ORDER BY file_path, line_start; diff --git a/fixtures/minimal/CODEOWNERS b/fixtures/minimal/CODEOWNERS new file mode 100644 index 0000000..cee706b --- /dev/null +++ b/fixtures/minimal/CODEOWNERS @@ -0,0 +1,10 @@ +# Fixture CODEOWNERS — exercises `codemap query --group-by owner`. +# Real ownership lookup uses last-matching-rule semantics; we keep two rules +# so the recipe groups fan-in / fan-out by team. + +src/api/ @backend +src/components/ @frontend +src/lib/ @backend +src/utils/ @platform +src/styles/ @frontend +*.md @docs diff --git a/fixtures/minimal/README.md b/fixtures/minimal/README.md index 947bc8d..d43b84f 100644 --- a/fixtures/minimal/README.md +++ b/fixtures/minimal/README.md @@ -1,10 +1,38 @@ -# Minimal benchmark fixture +# Minimal codemap fixture -Stable tree for **repeatable** `src/benchmark.ts` runs and CI. +Stable tree exercising every codemap surface — used by `src/benchmark.ts`, golden tests, and CI. -- **Index from the codemap repo:** `CODEMAP_ROOT="$(pwd)/fixtures/minimal" bun run dev --full` -- **Benchmark:** same `CODEMAP_ROOT`, then `bun run benchmark` +## What's exercised -Includes intentional symbols (`usePermissions`), a `~/api/client` import, `components/shop/*`, `utils/date`, CSS variables, and a TODO marker. +| Codemap surface | Fixture coverage | +| --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `symbols` (function / const / interface / class) | `usePermissions`, `createClient`, `setupTransport`, `openSocket`, `handshake`, `legacyClient`, `now`, `nanoseconds`, `_epochSeconds`, `_hiResEpoch`, `epochMs`, `nowIso`, `FormatPrice`, `ShopButton`, `ProductCard`, `get`, `invalidate`, `read`, `write`, `run` | +| `imports` / `exports` (named + default + re-export) | `consumer.ts` named imports; `components/shop/index.ts` barrel re-exports; `ShopButton.default.ts` default export | +| `dependencies` (resolved file→file edges) | TS imports across `api/`, `lib/`, `components/shop/`, `utils/`, `usePermissions` | +| `components` (React) | `ShopButton`, `ProductCard` (both call `usePermissions` — fan-in) | +| `calls` (caller→callee, depth >1, with cycle) | `run → createClient → setupTransport → openSocket → handshake`; cycle `cache.get → store.read → cache.invalidate → store.write → cache.get` | +| `markers` (TODO / FIXME / HACK / NOTE) | `notes.md` + `consumer.ts` (`XXX` is not yet a recognised kind) | +| `type_members` | `ClientConfig`, `Transport`, `ProductCardProps` | +| Visibility tags (`@internal` / `@beta` / `@alpha` / `@private`) | `_epochSeconds`, `nowIso`, `nanoseconds`, `_hiResEpoch` | +| `@deprecated` | `now`, `legacyClient`, `epochMs` (3 rows for SARIF / GH-annotations) | +| `css_variables` | `theme.css` (`--color-brand`, `--spacing-md`) | +| `css_classes` | `theme.css` (`.container`), `button.module.css` (`.primary`) | +| `css_keyframes` | `button.module.css` (`fadeIn`) | +| `--group-by owner` | `CODEOWNERS` (4 owners) | +| Project-local recipes | `.codemap/recipes/shop-symbols.{sql,md}` (with frontmatter actions) — file shape valid; loader currently runs at parse time before bootstrap, so `--recipe shop-symbols` is rejected as "unknown" until that's deferred to the runner (known limitation) | +| Self-managed `.gitignore` | `.codemap/.gitignore` (codemap-managed) | -**Editor / `tsc`:** run **`bun install`** here so **`react`** + **`@types/react`** resolve `react/jsx-runtime` for `.tsx` (`jsx: "react-jsx"` in `tsconfig.json`). +## Use + +```bash +# Index the fixture from the codemap repo +CODEMAP_ROOT="$(pwd)/fixtures/minimal" bun run dev --full + +# Benchmark +CODEMAP_ROOT="$(pwd)/fixtures/minimal" bun run benchmark + +# Project-local recipe +CODEMAP_ROOT="$(pwd)/fixtures/minimal" bun src/index.ts query --recipe shop-symbols --json +``` + +**Editor / `tsc`:** run `bun install` here so `react` + `@types/react` resolve `react/jsx-runtime` for `.tsx` (`jsx: "react-jsx"` in `tsconfig.json`). diff --git a/fixtures/minimal/src/api/client.ts b/fixtures/minimal/src/api/client.ts index 44a442f..ec52a6c 100644 --- a/fixtures/minimal/src/api/client.ts +++ b/fixtures/minimal/src/api/client.ts @@ -1,8 +1,43 @@ +// Demonstrates a richer call chain (createClient → setupTransport → openSocket +// → handshake) so `codemap impact` has depth > 1 to walk against, and a +// `@deprecated` method for the deprecated-symbols + SARIF / annotations recipes. + export interface ClientConfig { readonly baseUrl: string; timeout?: number; } +export interface Transport { + readonly socketUrl: string; + readonly handshakeMs: number; +} + export function createClient(config?: ClientConfig) { - return { version: 1, config }; + const transport = setupTransport( + config?.baseUrl ?? "https://api.example.com", + ); + return { version: 1, config, transport }; +} + +export function setupTransport(baseUrl: string): Transport { + const socketUrl = openSocket(baseUrl); + const handshakeMs = handshake(socketUrl); + return { socketUrl, handshakeMs }; +} + +export function openSocket(baseUrl: string): string { + return baseUrl.replace(/^http/, "ws"); +} + +export function handshake(socketUrl: string): number { + // pretend we measured something + return socketUrl.length; +} + +/** + * @deprecated Use `createClient({ baseUrl })` directly. Kept as a fixture for + * `deprecated-symbols` + `--format sarif` / `--format annotations` recipes. + */ +export function legacyClient() { + return createClient(); } diff --git a/fixtures/minimal/src/components/shop/ProductCard.tsx b/fixtures/minimal/src/components/shop/ProductCard.tsx new file mode 100644 index 0000000..9b8a410 --- /dev/null +++ b/fixtures/minimal/src/components/shop/ProductCard.tsx @@ -0,0 +1,19 @@ +import { usePermissions } from "../../usePermissions"; + +interface ProductCardProps { + readonly id: number; + readonly title: string; +} + +// React component fixture — exercises `components` table fan-in to +// `usePermissions` (also used by ShopButton.tsx); pair with the barrel +// re-export in `./index.ts` to surface fan-in via the `dependencies` graph. +export function ProductCard(props: ProductCardProps) { + const perms = usePermissions(); + return ( +
+

{props.title}

+ {perms.canEdit ? : null} +
+ ); +} diff --git a/fixtures/minimal/src/components/shop/ShopButton.default.ts b/fixtures/minimal/src/components/shop/ShopButton.default.ts new file mode 100644 index 0000000..e66c8b3 --- /dev/null +++ b/fixtures/minimal/src/components/shop/ShopButton.default.ts @@ -0,0 +1,4 @@ +// Default-export fixture — exercises `is_default = 1` on the `exports` table. +import { ShopButton } from "./ShopButton"; + +export default ShopButton; diff --git a/fixtures/minimal/src/components/shop/ShopButton.tsx b/fixtures/minimal/src/components/shop/ShopButton.tsx index d8380d7..f0dd05d 100644 --- a/fixtures/minimal/src/components/shop/ShopButton.tsx +++ b/fixtures/minimal/src/components/shop/ShopButton.tsx @@ -1,7 +1,14 @@ +import { usePermissions } from "../../usePermissions"; + export function FormatPrice(cents: number): string { return `$${(cents / 100).toFixed(2)}`; } export function ShopButton() { - return ; + const perms = usePermissions(); + return ( + + ); } diff --git a/fixtures/minimal/src/components/shop/index.ts b/fixtures/minimal/src/components/shop/index.ts new file mode 100644 index 0000000..2179e22 --- /dev/null +++ b/fixtures/minimal/src/components/shop/index.ts @@ -0,0 +1,7 @@ +// Barrel re-export — exercises `re_export_source` on `exports` rows + the +// `barrel-files` recipe (top files by export count). +export { FormatPrice, ShopButton } from "./ShopButton"; +export { ProductCard } from "./ProductCard"; + +// Default re-export shape — for `is_default = 1` coverage on `exports`. +export { default as PrimaryShopButton } from "./ShopButton.default"; diff --git a/fixtures/minimal/src/consumer.ts b/fixtures/minimal/src/consumer.ts index fa33ee0..693a30d 100644 --- a/fixtures/minimal/src/consumer.ts +++ b/fixtures/minimal/src/consumer.ts @@ -1,10 +1,18 @@ import { createClient, type ClientConfig } from "~/api/client"; +import { ProductCard, ShopButton } from "~/components/shop"; +import { get } from "./lib/cache"; import { now } from "./utils/date"; +import { epochMs } from "./utils/format"; // FIXME: handle errors +// HACK: short-circuit shouldn't ship to prod export function run() { const config: ClientConfig = { baseUrl: "https://api.example.com" }; createClient(config); - return now(); + get("session"); + // Surface the new utils + components in the call graph so `impact` walks + // produce non-trivial fan-out from `run`. + const _: unknown = { ShopButton, ProductCard }; + return now() + epochMs(); } diff --git a/fixtures/minimal/src/lib/cache.ts b/fixtures/minimal/src/lib/cache.ts new file mode 100644 index 0000000..d663aee --- /dev/null +++ b/fixtures/minimal/src/lib/cache.ts @@ -0,0 +1,20 @@ +// Pair with `./store.ts` to form a 2-node call cycle: +// cache.get → store.read → cache.invalidate → store.write → cache.get +// Exercises `codemap impact`'s cycle-detection (path-string `instr` check). + +import { read, write } from "./store"; + +const _data: Map = new Map(); + +export function get(key: string): string | undefined { + if (!_data.has(key)) { + const fresh = read(key); + if (fresh !== undefined) _data.set(key, fresh); + } + return _data.get(key); +} + +export function invalidate(key: string): void { + _data.delete(key); + write(key, ""); +} diff --git a/fixtures/minimal/src/lib/store.ts b/fixtures/minimal/src/lib/store.ts new file mode 100644 index 0000000..6b0bd8c --- /dev/null +++ b/fixtures/minimal/src/lib/store.ts @@ -0,0 +1,14 @@ +// See `./cache.ts` for the cycle rationale. + +import { invalidate } from "./cache"; + +const _backing: Map = new Map(); + +export function read(key: string): string | undefined { + return _backing.get(key); +} + +export function write(key: string, value: string): void { + _backing.set(key, value); + invalidate(key); +} diff --git a/fixtures/minimal/src/notes.md b/fixtures/minimal/src/notes.md index 742555b..4c820f7 100644 --- a/fixtures/minimal/src/notes.md +++ b/fixtures/minimal/src/notes.md @@ -1,3 +1,10 @@ # Fixture notes TODO: this file exists so marker scans have a predictable hit. + +HACK: this whole fixture is intentionally tiny — exercise codemap, not real UX. + +NOTE: when adding a new generated artifact under `.codemap/`, also bump the +canonical body in `STATE_GITIGNORE_BODY` so consumers' files self-heal. + +NOTE: codemap's marker scanner recognises `TODO`/`FIXME`/`HACK`/`NOTE` only — `XXX` is not yet a kind. diff --git a/fixtures/minimal/src/utils/date.ts b/fixtures/minimal/src/utils/date.ts index 0c5f897..1a77f4d 100644 --- a/fixtures/minimal/src/utils/date.ts +++ b/fixtures/minimal/src/utils/date.ts @@ -12,3 +12,17 @@ export function now(): number { export function _epochSeconds(): number { return Math.floor(Date.now() / 1000); } + +/** + * @alpha Experimental — fixture for `visibility-tags` four-tag coverage. + */ +export function nanoseconds(): bigint { + return BigInt(Date.now()) * 1_000_000n; +} + +/** + * @private Internal-only utility — fixture for `visibility-tags` four-tag coverage. + */ +export function _hiResEpoch(): number { + return performance.now(); +} diff --git a/fixtures/minimal/src/utils/format.ts b/fixtures/minimal/src/utils/format.ts new file mode 100644 index 0000000..e9aad3f --- /dev/null +++ b/fixtures/minimal/src/utils/format.ts @@ -0,0 +1,15 @@ +/** + * @deprecated Drift detector for the SARIF / GH-annotations golden output. + * Pair with `now()` in `./date.ts` to give recipes >1 row to render. + */ +export function epochMs(): number { + return Date.now(); +} + +/** + * @beta Fixture for the `visibility-tags` recipe — lock-in for the four-tag + * coverage (alongside `@internal`, `@alpha`, `@private`). + */ +export function nowIso(): string { + return new Date().toISOString(); +} diff --git a/lint-staged.config.js b/lint-staged.config.js index 8bdf532..78fc94e 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -25,9 +25,23 @@ function typecheckStagedFiles(filenames) { return `bun run typecheck -p ${TEMP_TSCONFIG}`; } +/** + * `oxlint` exits 1 when every staged file matches an `ignorePatterns` entry + * (e.g. all-fixture commits) — filter out ignored paths before lint-staged + * passes them to oxlint. Same for format:check via oxfmt. + */ +function lintStaged(filenames) { + const lintable = filenames.filter((f) => { + const rel = path.relative(process.cwd(), f).replace(/\\/g, "/"); + return !rel.startsWith("fixtures/"); + }); + if (lintable.length === 0) return "true"; + return `bun run lint ${lintable.map((f) => JSON.stringify(f)).join(" ")}`; +} + /** @type {import('lint-staged').Configuration} */ export default { - "*.{js,jsx,ts,tsx,mjs,mts,cjs,cts}": ["bun run format:check", "bun run lint"], + "*.{js,jsx,ts,tsx,mjs,mts,cjs,cts}": ["bun run format:check", lintStaged], "*.{css,json,md,mdc,html,yaml,yml}": "bun run format:check", "*.{ts,tsx}": typecheckStagedFiles, "*.test.ts": "bun test", From 9d658137b53cd99c017fb74f704571fe43b5c621 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Sun, 3 May 2026 20:58:48 +0300 Subject: [PATCH 2/4] fix(lint): pass --no-error-on-unmatched-pattern to oxlint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the lint-staged wrapper that filtered fixture paths (which broke other CI tasks). The proper fix is at the lint script: oxlint exits 1 when every staged file matches an `ignorePatterns` entry (e.g. all-fixture commits) — `--no-error-on-unmatched-pattern` makes it a no-op instead, mirroring what `format:check` already does for `oxfmt`. Also refreshes golden query fixtures to match the enriched fixtures/minimal corpus. --- fixtures/golden/minimal/barrel-files.json | 28 +++++++- fixtures/golden/minimal/calls-consumer.json | 6 ++ .../components-no-false-positives.json | 3 + .../minimal/dependencies-from-consumer.json | 12 ++++ .../golden/minimal/deprecated-symbols.json | 16 +++++ fixtures/golden/minimal/exports-client.json | 20 ++++++ fixtures/golden/minimal/files-count.json | 2 +- fixtures/golden/minimal/files-hashes.json | 64 +++++++++++++++---- fixtures/golden/minimal/index-summary.json | 10 +-- .../golden/minimal/markers-all-kinds.json | 8 +++ .../golden/minimal/markers-notes-todo.json | 18 ++++++ fixtures/golden/minimal/visibility-tags.json | 27 ++++++++ lint-staged.config.js | 16 +---- package.json | 2 +- 14 files changed, 197 insertions(+), 35 deletions(-) diff --git a/fixtures/golden/minimal/barrel-files.json b/fixtures/golden/minimal/barrel-files.json index d67000f..64d6f36 100644 --- a/fixtures/golden/minimal/barrel-files.json +++ b/fixtures/golden/minimal/barrel-files.json @@ -1,16 +1,40 @@ [ { "file_path": "src/api/client.ts", - "exports": 2 + "exports": 7 + }, + { + "file_path": "src/components/shop/index.ts", + "exports": 4 + }, + { + "file_path": "src/utils/date.ts", + "exports": 4 }, { "file_path": "src/components/shop/ShopButton.tsx", "exports": 2 }, { - "file_path": "src/utils/date.ts", + "file_path": "src/lib/cache.ts", + "exports": 2 + }, + { + "file_path": "src/lib/store.ts", "exports": 2 }, + { + "file_path": "src/utils/format.ts", + "exports": 2 + }, + { + "file_path": "src/components/shop/ProductCard.tsx", + "exports": 1 + }, + { + "file_path": "src/components/shop/ShopButton.default.ts", + "exports": 1 + }, { "file_path": "src/consumer.ts", "exports": 1 diff --git a/fixtures/golden/minimal/calls-consumer.json b/fixtures/golden/minimal/calls-consumer.json index e094c0a..86dd87e 100644 --- a/fixtures/golden/minimal/calls-consumer.json +++ b/fixtures/golden/minimal/calls-consumer.json @@ -2,6 +2,12 @@ { "callee_name": "createClient" }, + { + "callee_name": "epochMs" + }, + { + "callee_name": "get" + }, { "callee_name": "now" } diff --git a/fixtures/golden/minimal/components-no-false-positives.json b/fixtures/golden/minimal/components-no-false-positives.json index dce72f8..5bc142e 100644 --- a/fixtures/golden/minimal/components-no-false-positives.json +++ b/fixtures/golden/minimal/components-no-false-positives.json @@ -1,4 +1,7 @@ [ + { + "name": "ProductCard" + }, { "name": "ShopButton" } diff --git a/fixtures/golden/minimal/dependencies-from-consumer.json b/fixtures/golden/minimal/dependencies-from-consumer.json index 9aecb55..dba1d72 100644 --- a/fixtures/golden/minimal/dependencies-from-consumer.json +++ b/fixtures/golden/minimal/dependencies-from-consumer.json @@ -3,8 +3,20 @@ "from_path": "src/consumer.ts", "to_path": "src/api/client.ts" }, + { + "from_path": "src/consumer.ts", + "to_path": "src/components/shop/index.ts" + }, + { + "from_path": "src/consumer.ts", + "to_path": "src/lib/cache.ts" + }, { "from_path": "src/consumer.ts", "to_path": "src/utils/date.ts" + }, + { + "from_path": "src/consumer.ts", + "to_path": "src/utils/format.ts" } ] diff --git a/fixtures/golden/minimal/deprecated-symbols.json b/fixtures/golden/minimal/deprecated-symbols.json index bd54a77..3cb0802 100644 --- a/fixtures/golden/minimal/deprecated-symbols.json +++ b/fixtures/golden/minimal/deprecated-symbols.json @@ -1,4 +1,12 @@ [ + { + "name": "legacyClient", + "kind": "function", + "file_path": "src/api/client.ts", + "line_start": 41, + "signature": "legacyClient()", + "doc_comment": "@deprecated Use `createClient({ baseUrl })` directly. Kept as a fixture for\n`deprecated-symbols` + `--format sarif` / `--format annotations` recipes." + }, { "name": "now", "kind": "function", @@ -6,5 +14,13 @@ "line_start": 5, "signature": "now(): number", "doc_comment": "@deprecated Use `Date.now()` directly. Kept as a fixture for the\n`deprecated-symbols` recipe golden test." + }, + { + "name": "epochMs", + "kind": "function", + "file_path": "src/utils/format.ts", + "line_start": 5, + "signature": "epochMs(): number", + "doc_comment": "@deprecated Drift detector for the SARIF / GH-annotations golden output.\nPair with `now()` in `./date.ts` to give recipes >1 row to render." } ] diff --git a/fixtures/golden/minimal/exports-client.json b/fixtures/golden/minimal/exports-client.json index 63c2f44..108c4c3 100644 --- a/fixtures/golden/minimal/exports-client.json +++ b/fixtures/golden/minimal/exports-client.json @@ -3,8 +3,28 @@ "name": "ClientConfig", "kind": "type" }, + { + "name": "Transport", + "kind": "type" + }, { "name": "createClient", "kind": "value" + }, + { + "name": "setupTransport", + "kind": "value" + }, + { + "name": "openSocket", + "kind": "value" + }, + { + "name": "handshake", + "kind": "value" + }, + { + "name": "legacyClient", + "kind": "value" } ] diff --git a/fixtures/golden/minimal/files-count.json b/fixtures/golden/minimal/files-count.json index ecc2960..f5f07db 100644 --- a/fixtures/golden/minimal/files-count.json +++ b/fixtures/golden/minimal/files-count.json @@ -1,5 +1,5 @@ [ { - "n": 11 + "n": 18 } ] diff --git a/fixtures/golden/minimal/files-hashes.json b/fixtures/golden/minimal/files-hashes.json index bd85eb8..de84e4e 100644 --- a/fixtures/golden/minimal/files-hashes.json +++ b/fixtures/golden/minimal/files-hashes.json @@ -1,9 +1,15 @@ [ + { + "path": ".codemap/recipes/shop-symbols.md", + "content_hash": "0ef26e4291fd1dbfb60887d79251bfd9eb1af71675811ba40a1932368e6b112c", + "language": "md", + "line_count": 9 + }, { "path": "README.md", - "content_hash": "428564b4ac5e4864ca2292a4195b726be3d4c7c5cac3d19dd01f17714d026c66", + "content_hash": "c5945c11f5119a986a53fcf51c8157f1967d015d1b089a1e3804772c7c5eea07", "language": "md", - "line_count": 11 + "line_count": 39 }, { "path": "package.json", @@ -13,27 +19,57 @@ }, { "path": "src/api/client.ts", - "content_hash": "4d0ec5edad3f758e7f84898dd602dbacaf3ea65ad87614ae9f179bd2383bbf56", + "content_hash": "625df0a0f197f2233506c028f5a4b2022567587de2ecc5e81863c2cb2654e507", "language": "ts", - "line_count": 9 + "line_count": 44 + }, + { + "path": "src/components/shop/ProductCard.tsx", + "content_hash": "4cdd3d8376da664789c075bfdb3eab49adc3460fcd0810eedc1ad456e7c5be9a", + "language": "tsx", + "line_count": 20 + }, + { + "path": "src/components/shop/ShopButton.default.ts", + "content_hash": "105488e5bfb6446c071d2cf2311411aa9e7301d1b75ca72e317fde79c1d35740", + "language": "ts", + "line_count": 5 }, { "path": "src/components/shop/ShopButton.tsx", - "content_hash": "481b9ff2e493fd69b36b67d2738e298999ce55040bce64c4c38a2e736c0573fd", + "content_hash": "cbc7939a134240c0076f76f6499fa893309f131dda53eaef4d946470f42c54af", "language": "tsx", + "line_count": 15 + }, + { + "path": "src/components/shop/index.ts", + "content_hash": "824c09d50df619c90500baad1489075e9fe501090aeeb7afa39e335d41ce52c2", + "language": "ts", "line_count": 8 }, { "path": "src/consumer.ts", - "content_hash": "420ad98f7fedaf07ec07d1a64ef6ade100be4a7973883718a3942c68b1350903", + "content_hash": "ab868f04a0f22a8b132bc7a4ae42b403dad9c20185497f8513c06dd26c848091", "language": "ts", - "line_count": 11 + "line_count": 19 + }, + { + "path": "src/lib/cache.ts", + "content_hash": "25934e14b1b0dfa53f3293ee6fd59fc0fd668439d0c09acc93e49debc668c04e", + "language": "ts", + "line_count": 21 + }, + { + "path": "src/lib/store.ts", + "content_hash": "a23e13e22cecf8051d83659b39f792b6fc592f90d501da83d75ab5fd61dac1a8", + "language": "ts", + "line_count": 15 }, { "path": "src/notes.md", - "content_hash": "1a2bff433137690132077b15032ba3f07f5ed1b576b894816890342155ab640a", + "content_hash": "03ab92544e475626defcaafec97e1169d7940532ca3eeb4a25d28b4816c1ca9a", "language": "md", - "line_count": 4 + "line_count": 11 }, { "path": "src/styles/button.module.css", @@ -55,9 +91,15 @@ }, { "path": "src/utils/date.ts", - "content_hash": "89e6301076caeb9f4c13cee8ea06f7f25a5e1aa239daf921f7ff984236ffa177", + "content_hash": "d42719b407b74b0ebab2d0ed25e004413e6a2231d4ad05aa51e431bea5075396", "language": "ts", - "line_count": 15 + "line_count": 29 + }, + { + "path": "src/utils/format.ts", + "content_hash": "8f4e1b729d1a69f1457a57c0a6ce61740ff27f2119976d2183ca053bf50b2ad6", + "language": "ts", + "line_count": 16 }, { "path": "tsconfig.json", diff --git a/fixtures/golden/minimal/index-summary.json b/fixtures/golden/minimal/index-summary.json index 8b550d6..ed60a41 100644 --- a/fixtures/golden/minimal/index-summary.json +++ b/fixtures/golden/minimal/index-summary.json @@ -1,9 +1,9 @@ [ { - "files": 11, - "symbols": 9, - "imports": 3, - "components": 1, - "dependencies": 2 + "files": 18, + "symbols": 33, + "imports": 11, + "components": 2, + "dependencies": 10 } ] diff --git a/fixtures/golden/minimal/markers-all-kinds.json b/fixtures/golden/minimal/markers-all-kinds.json index db59b2c..7daab66 100644 --- a/fixtures/golden/minimal/markers-all-kinds.json +++ b/fixtures/golden/minimal/markers-all-kinds.json @@ -3,6 +3,14 @@ "kind": "FIXME", "n": 1 }, + { + "kind": "HACK", + "n": 2 + }, + { + "kind": "NOTE", + "n": 2 + }, { "kind": "TODO", "n": 2 diff --git a/fixtures/golden/minimal/markers-notes-todo.json b/fixtures/golden/minimal/markers-notes-todo.json index 619001d..966dfe7 100644 --- a/fixtures/golden/minimal/markers-notes-todo.json +++ b/fixtures/golden/minimal/markers-notes-todo.json @@ -4,5 +4,23 @@ "line_number": 3, "kind": "TODO", "content": "this file exists so marker scans have a predictable hit." + }, + { + "file_path": "src/notes.md", + "line_number": 5, + "kind": "HACK", + "content": "this whole fixture is intentionally tiny — exercise codemap, not real UX." + }, + { + "file_path": "src/notes.md", + "line_number": 7, + "kind": "NOTE", + "content": "when adding a new generated artifact under `.codemap/`, also bump the" + }, + { + "file_path": "src/notes.md", + "line_number": 10, + "kind": "NOTE", + "content": "codemap's marker scanner recognises `TODO`/`FIXME`/`HACK`/`NOTE` only — `XXX` is not yet a kind." } ] diff --git a/fixtures/golden/minimal/visibility-tags.json b/fixtures/golden/minimal/visibility-tags.json index 72cc172..a878627 100644 --- a/fixtures/golden/minimal/visibility-tags.json +++ b/fixtures/golden/minimal/visibility-tags.json @@ -7,5 +7,32 @@ "line_start": 12, "signature": "_epochSeconds(): number", "doc_comment": "@internal Implementation helper — fixture for the `visibility-tags` recipe." + }, + { + "name": "nanoseconds", + "kind": "function", + "visibility": "alpha", + "file_path": "src/utils/date.ts", + "line_start": 19, + "signature": "nanoseconds(): bigint", + "doc_comment": "@alpha Experimental — fixture for `visibility-tags` four-tag coverage." + }, + { + "name": "_hiResEpoch", + "kind": "function", + "visibility": "private", + "file_path": "src/utils/date.ts", + "line_start": 26, + "signature": "_hiResEpoch(): number", + "doc_comment": "@private Internal-only utility — fixture for `visibility-tags` four-tag coverage." + }, + { + "name": "nowIso", + "kind": "function", + "visibility": "beta", + "file_path": "src/utils/format.ts", + "line_start": 13, + "signature": "nowIso(): string", + "doc_comment": "@beta Fixture for the `visibility-tags` recipe — lock-in for the four-tag\ncoverage (alongside `@internal`, `@alpha`, `@private`)." } ] diff --git a/lint-staged.config.js b/lint-staged.config.js index 78fc94e..8bdf532 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -25,23 +25,9 @@ function typecheckStagedFiles(filenames) { return `bun run typecheck -p ${TEMP_TSCONFIG}`; } -/** - * `oxlint` exits 1 when every staged file matches an `ignorePatterns` entry - * (e.g. all-fixture commits) — filter out ignored paths before lint-staged - * passes them to oxlint. Same for format:check via oxfmt. - */ -function lintStaged(filenames) { - const lintable = filenames.filter((f) => { - const rel = path.relative(process.cwd(), f).replace(/\\/g, "/"); - return !rel.startsWith("fixtures/"); - }); - if (lintable.length === 0) return "true"; - return `bun run lint ${lintable.map((f) => JSON.stringify(f)).join(" ")}`; -} - /** @type {import('lint-staged').Configuration} */ export default { - "*.{js,jsx,ts,tsx,mjs,mts,cjs,cts}": ["bun run format:check", lintStaged], + "*.{js,jsx,ts,tsx,mjs,mts,cjs,cts}": ["bun run format:check", "bun run lint"], "*.{css,json,md,mdc,html,yaml,yml}": "bun run format:check", "*.{ts,tsx}": typecheckStagedFiles, "*.test.ts": "bun test", diff --git a/package.json b/package.json index c5d162a..4d22a75 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "fix": "bun run lint:fix && bun run format", "format": "oxfmt", "format:check": "oxfmt --check --no-error-on-unmatched-pattern", - "lint": "oxlint", + "lint": "oxlint --no-error-on-unmatched-pattern", "lint-staged": "lint-staged", "lint:ci": "oxlint --quiet", "lint:fix": "oxlint --fix", From 56bc737afe6d123c2349d397818e730d4e4bcf42 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Sun, 3 May 2026 21:07:00 +0300 Subject: [PATCH 3/4] chore(fixture): apply CodeRabbit fixture corrections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - shop-symbols.sql: add `is_exported = 1` so the recipe matches the `.md` description ("every exported function under src/components/shop/"). - cache.ts header + README cycle row: replace the bogus 4-step path (which referenced edges that don't exist in the code) with the actual 2-node static call cycle `cache.invalidate ↔ store.write`, plus the non-cyclic `cache.get → store.read` edge. - cache.ts invalidate(): guard the `write(key, "")` re-entry behind an unreachable string check. The AST still records the `invalidate → write` call edge needed by the cycle fixture, but runtime execution no longer triggers infinite mutual recursion. - README "Use" section: inline note that `--recipe shop-symbols` is currently rejected (matches the table caveat above), so a reader copying the command knows to expect the known limitation. --- fixtures/golden/minimal/files-hashes.json | 8 ++++---- fixtures/minimal/.codemap/recipes/shop-symbols.sql | 1 + fixtures/minimal/README.md | 6 ++++-- fixtures/minimal/src/lib/cache.ts | 12 ++++++++---- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/fixtures/golden/minimal/files-hashes.json b/fixtures/golden/minimal/files-hashes.json index de84e4e..a64d2d1 100644 --- a/fixtures/golden/minimal/files-hashes.json +++ b/fixtures/golden/minimal/files-hashes.json @@ -7,9 +7,9 @@ }, { "path": "README.md", - "content_hash": "c5945c11f5119a986a53fcf51c8157f1967d015d1b089a1e3804772c7c5eea07", + "content_hash": "185e778a9d1a7247b373dd4c226c9de1d82e18a182e0be6daa76b7f835fcbe86", "language": "md", - "line_count": 39 + "line_count": 41 }, { "path": "package.json", @@ -55,9 +55,9 @@ }, { "path": "src/lib/cache.ts", - "content_hash": "25934e14b1b0dfa53f3293ee6fd59fc0fd668439d0c09acc93e49debc668c04e", + "content_hash": "7503a156d1634e3739cb306c61fc82039178c1d84cbe4ff5c518b1fc439da808", "language": "ts", - "line_count": 21 + "line_count": 25 }, { "path": "src/lib/store.ts", diff --git a/fixtures/minimal/.codemap/recipes/shop-symbols.sql b/fixtures/minimal/.codemap/recipes/shop-symbols.sql index b4b1002..348da57 100644 --- a/fixtures/minimal/.codemap/recipes/shop-symbols.sql +++ b/fixtures/minimal/.codemap/recipes/shop-symbols.sql @@ -5,4 +5,5 @@ SELECT name, file_path, line_start, signature FROM symbols WHERE file_path LIKE 'src/components/shop/%' AND kind = 'function' + AND is_exported = 1 ORDER BY file_path, line_start; diff --git a/fixtures/minimal/README.md b/fixtures/minimal/README.md index d43b84f..f8c90c2 100644 --- a/fixtures/minimal/README.md +++ b/fixtures/minimal/README.md @@ -10,7 +10,7 @@ Stable tree exercising every codemap surface — used by `src/benchmark.ts`, gol | `imports` / `exports` (named + default + re-export) | `consumer.ts` named imports; `components/shop/index.ts` barrel re-exports; `ShopButton.default.ts` default export | | `dependencies` (resolved file→file edges) | TS imports across `api/`, `lib/`, `components/shop/`, `utils/`, `usePermissions` | | `components` (React) | `ShopButton`, `ProductCard` (both call `usePermissions` — fan-in) | -| `calls` (caller→callee, depth >1, with cycle) | `run → createClient → setupTransport → openSocket → handshake`; cycle `cache.get → store.read → cache.invalidate → store.write → cache.get` | +| `calls` (caller→callee, depth >1, with cycle) | `run → createClient → setupTransport → openSocket → handshake`; non-cyclic `cache.get → store.read`; 2-node cycle `cache.invalidate ↔ store.write` | | `markers` (TODO / FIXME / HACK / NOTE) | `notes.md` + `consumer.ts` (`XXX` is not yet a recognised kind) | | `type_members` | `ClientConfig`, `Transport`, `ProductCardProps` | | Visibility tags (`@internal` / `@beta` / `@alpha` / `@private`) | `_epochSeconds`, `nowIso`, `nanoseconds`, `_hiResEpoch` | @@ -31,7 +31,9 @@ CODEMAP_ROOT="$(pwd)/fixtures/minimal" bun run dev --full # Benchmark CODEMAP_ROOT="$(pwd)/fixtures/minimal" bun run benchmark -# Project-local recipe +# Project-local recipe (known limitation: currently rejected as "unknown" — see +# the "Project-local recipes" row above; will work once recipe loading is +# deferred past bootstrap) CODEMAP_ROOT="$(pwd)/fixtures/minimal" bun src/index.ts query --recipe shop-symbols --json ``` diff --git a/fixtures/minimal/src/lib/cache.ts b/fixtures/minimal/src/lib/cache.ts index d663aee..a2492ae 100644 --- a/fixtures/minimal/src/lib/cache.ts +++ b/fixtures/minimal/src/lib/cache.ts @@ -1,6 +1,10 @@ -// Pair with `./store.ts` to form a 2-node call cycle: -// cache.get → store.read → cache.invalidate → store.write → cache.get -// Exercises `codemap impact`'s cycle-detection (path-string `instr` check). +// Pair with `./store.ts` to form a 2-node static call cycle in the `calls` graph: +// cache.invalidate → store.write → cache.invalidate +// `cache.get → store.read` is a separate non-cyclic edge. Exercises +// `codemap impact`'s cycle-detection (path-string `instr` check). +// The `write(key, "")` below is a parse-only sentinel: guarded so it never +// executes (would otherwise stack-overflow), but the AST still records the +// `cache.invalidate → store.write` call edge needed for the cycle fixture. import { read, write } from "./store"; @@ -16,5 +20,5 @@ export function get(key: string): string | undefined { export function invalidate(key: string): void { _data.delete(key); - write(key, ""); + if (key === "__codemap_unreachable__") write(key, ""); } From f770638337535f47a5c87571cb8e99c2472f642d Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Sun, 3 May 2026 21:17:32 +0300 Subject: [PATCH 4/4] chore(fixture): refresh files-hashes golden + slim cache.ts comments - Re-snapshot files-hashes.json against the post-format README.md so CI's golden-query regression matches the on-disk corpus. - Trim the cache.ts header from a 7-line block to a 2-line cycle description + one-line guard explainer (concise-comments rule). --- fixtures/golden/minimal/files-hashes.json | 6 +++--- fixtures/minimal/src/lib/cache.ts | 10 +++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/fixtures/golden/minimal/files-hashes.json b/fixtures/golden/minimal/files-hashes.json index a64d2d1..0f6497f 100644 --- a/fixtures/golden/minimal/files-hashes.json +++ b/fixtures/golden/minimal/files-hashes.json @@ -7,7 +7,7 @@ }, { "path": "README.md", - "content_hash": "185e778a9d1a7247b373dd4c226c9de1d82e18a182e0be6daa76b7f835fcbe86", + "content_hash": "75032b11e435e7f265151787faf72c59a986b491ba4e866856ba77eda6b4deb4", "language": "md", "line_count": 41 }, @@ -55,9 +55,9 @@ }, { "path": "src/lib/cache.ts", - "content_hash": "7503a156d1634e3739cb306c61fc82039178c1d84cbe4ff5c518b1fc439da808", + "content_hash": "a185908afc6dca9552783847c0726686378dcf60e9d5714bfdb0aa24c479a4c1", "language": "ts", - "line_count": 25 + "line_count": 21 }, { "path": "src/lib/store.ts", diff --git a/fixtures/minimal/src/lib/cache.ts b/fixtures/minimal/src/lib/cache.ts index a2492ae..28fb41a 100644 --- a/fixtures/minimal/src/lib/cache.ts +++ b/fixtures/minimal/src/lib/cache.ts @@ -1,10 +1,5 @@ -// Pair with `./store.ts` to form a 2-node static call cycle in the `calls` graph: -// cache.invalidate → store.write → cache.invalidate -// `cache.get → store.read` is a separate non-cyclic edge. Exercises -// `codemap impact`'s cycle-detection (path-string `instr` check). -// The `write(key, "")` below is a parse-only sentinel: guarded so it never -// executes (would otherwise stack-overflow), but the AST still records the -// `cache.invalidate → store.write` call edge needed for the cycle fixture. +// 2-node static call cycle with `./store.ts` (`invalidate ↔ write`) for +// `codemap impact` cycle-detection. `get → read` is a separate non-cyclic edge. import { read, write } from "./store"; @@ -20,5 +15,6 @@ export function get(key: string): string | undefined { export function invalidate(key: string): void { _data.delete(key); + // Parse-only: AST records `invalidate → write` edge; guard prevents runtime recursion. if (key === "__codemap_unreachable__") write(key, ""); }