diff --git a/.agents/skills/ln-review/references/contract-lenses.md b/.agents/skills/ln-review/references/contract-lenses.md index 7cb71bbbb..d61ac0ddb 100644 --- a/.agents/skills/ln-review/references/contract-lenses.md +++ b/.agents/skills/ln-review/references/contract-lenses.md @@ -38,6 +38,10 @@ Each finding routes to one of three repairs: **enforce it loudly** (fail on viol - A **hand-maintained test-double inventory shadowing a canonical registry** — a test stub (`getAllTools()`, capability lists, route tables) that transcribes the members of a live registry by hand instead of deriving from the canonical constant. Every add/retire in the registry silently drifts the stub: retired members linger (masking regressions the inventory-consuming logic should catch), new members are omitted (the code under test never sees them), duplicates creep in. Point-fixing the stub after a review comment repairs one snapshot, not the fault (graduated 2026-07-09 from the PR #304 induction: three fixture sites — `getAllTools()` listing `ask` twice, retaining retired `REQUEST_RESPONSE_TOOL`, and omitting active `present_digest` — were each corrected by hand while remaining transcribed). **Discriminator:** does a canonical exported constant/registry enumerate the same membership the stub hand-lists? If the double intentionally diverges (a minimal-capability harness, a degraded-context probe), the divergence must be *derived too* (canonical set minus named exclusions) or explicitly commented; a bare hand-list with no derivation and no divergence note is the finding. Repair class **thread the real value**: build the stub from the canonical constant (spread it, filter it with named exclusions); the double should express its *difference* from canon, never re-state canon. Search seam: grep test files for inventory-shaped stub methods and array literals of registry member names; cross against the exported canonical constants for that registry. +- A **deadness verdict computed by reference scan, applied to an artifact with out-of-graph consumers** — an "unused, delete it" claim derived from grepping the repo's import graph, aimed at an artifact whose consumption seam the scan cannot see: environment provisioning consumed by spawned runtimes (a CI apt package that agents under test shell out to), canonical intent registers (`TOPOLOGY.md`, `treedocs.yaml`, PLAN-parked items, intentional topology stubs), the published-package surface (runtime vs `devDependencies` decided by what consumers install, not what `src/` imports), and gitignored data planes. Both faces are silent: the delete lands green locally (import graph clean, default tests pass) and breaks only in a CI lane or consumer install — or the delete proceeds and the canonical docs that named the seam rot unreconciled (graduated 2026-08-03 from the reduction-review induction: CI `ripgrep` was flagged "nothing invokes it" while FE-1241's commit message provisioned it so "the Linux full gate can exercise bounded grep reads" by spawned agents; four deletion targets — `run-auto-replan-policy`, `row-schemas`, `drawer-card`, `validate-fixture` — were named by `TOPOLOGY.md` files the review never mentioned; a deps→`devDependencies` move was argued from repo imports rather than the published surface). **Discriminator:** does the artifact's consumption run through text the scan can see? If it was provisioned or declared *for* an out-of-graph consumer, reference-count zero is not evidence of deadness; a repo-code-only artifact with zero references and no canonical-doc mention is genuinely dead and not this lens. Repair class **name the contract**: annotate the provisioning site with its out-of-graph consumer (a comment on the CI install line naming the spawned-agent need and its decision/issue id), and gate every deletion on canonical-doc reconciliation (update or challenge the `TOPOLOGY.md`/PLAN entry in the same change). Search seam: for each delete claim, run `git log -S ` for stated purpose at introduction, and cross the target against `src/**/TOPOLOGY.md`, `src/treedocs.yaml`, and `memory/PLAN.md` parked items; for dependency reclassification, inspect what the published package (`files`/`prepack` output) actually needs at install time, and enumerate gitignored data planes with `git ls-files` rather than ignore-respecting globs. + +- A **contract-bearing predicate duplicated by copy instead of import** — a function whose result feeds an identity, parity, equality, or authority decision (`canonicalPath`, `markingsEqual`, terminal-summary classification, run-id derivation) defined independently in two or more production files rather than imported from one owner. The copies are typically byte-identical at introduction, so review sees "just a small helper"; the fault is latent — the first divergent edit makes seams that must agree (writer vs reader projection, git ports vs execution authority) silently disagree about "equal", "same path", or "terminal", with no error, while each copy's local tests stay green (graduated 2026-08-03 from two consecutive review-batch inductions: `stringArraysEqual` defined in four executor files with terminal-summary/marking semantics spread across `orchestrate.ts`, `observer-read.ts`, `petri-events.ts`, `petri-replay.ts` — a writer/reader-authority pair sharing state semantics by copy; `canonicalPath` defined identically in the three `src/app/git-*-port.ts` files plus `executor/worktree.ts` and `executor/run-execution-authority.ts` — the exact seams that must agree on path identity for promotion/landing writes; `pathExists` ×22 and `defaultRunId` ×7 as the same family at lower stakes). **Discriminator: does the predicate's result feed an equality/identity/parity/authority decision shared across seams?** If yes → this lens; a trivial shim with stable, decision-free semantics (`isRecord` ×32, `parseJson` ×11 at graduation time) is tolerated duplication, not the finding — do not flag it. Repair class **name the contract**: one imported owner per predicate family (an executor state-predicates module imported by writer, reader authority, and replay; one shared canonical-path helper for the git/authority seams); the copies then delete. Search seam: scan duplicate function definitions — `rg -n --no-heading '^(export )?(async )?function (\w+)\(' src -o -r '$3'` over production files, count names appearing in ≥2 files — then apply the discriminator to each hit; pay first attention to names containing `equal`, `canonical`, `terminal`, `default*Id`, `sanitize`, `classif`. + ## Catalog governance This catalog is the stabilized lens library. `ln-induct` is the generator that induces fresh lenses from review-bot evidence and proposes graduating recurring ones into this list. diff --git a/.changeset/trim-published-install-surface.md b/.changeset/trim-published-install-surface.md new file mode 100644 index 000000000..01abfd138 --- /dev/null +++ b/.changeset/trim-published-install-surface.md @@ -0,0 +1,9 @@ +--- +"@hashintel/brunch": patch +--- + +Trim the published install surface without changing runtime behavior: + +- Production dependencies 23 → 15. `stringify-tree` is deleted with its last consumer, and seven packages move to `devDependencies`: the six web-only ones (`react`, `react-dom`, `@tanstack/react-query`, `@tanstack/react-router`, `@fontsource-variable/inter`, `@fontsource-variable/geist-mono`) and the build-time `drizzle-typebox`. None of the eight is reachable from either published entry point (`bin/brunch.js` or `main`); the React and TanStack code ships inlined in the prebuilt `dist-web` Vite bundle and the fonts ship as `.woff2` assets beside it. +- The package `files` list excludes `dist/probes` and the unbundled `dist/web` tree. Probe scripts still build locally, while the shipped browser application remains the self-contained `dist-web` bundle. +- Executor internals consolidate: the two inert execute-plan artifact tools and their report writers are removed, the execute-tool authority map is type-coupled to the `tool-names.ts` roster union so roster drift becomes a compile error, and the duplicated path-existence predicate collapses to one executor-owned helper. diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e34fbb7e5..19d436140 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,6 +44,9 @@ jobs: git config --global init.defaultBranch main - name: Install native test dependencies + # ripgrep is consumed out-of-graph: FE-1241 provisioned it for the Grep tool of the + # comparison-lane agents this gate spawns, so a reference scan of src/ will always + # report zero consumers. expect/zsh are named in src/dev/tui-driver; ripgrep is not. run: | sudo apt-get update sudo apt-get install --yes expect ripgrep zsh diff --git a/.oxfmtrc.json b/.oxfmtrc.json index 67a7438f1..0e3ec721c 100644 --- a/.oxfmtrc.json +++ b/.oxfmtrc.json @@ -15,7 +15,6 @@ "docs/**", "memory/**", ".fixtures/**", - "@types/**", "tmp/**", "dist-web/**", "bin/**", diff --git a/.oxlintrc.json b/.oxlintrc.json index 9f1a29efd..3a2bd79a4 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -84,7 +84,6 @@ "docs/**", "memory/**", ".fixtures/**", - "@types/**", "tmp/**", "src/**/_suspended/**", "dist-web/**", diff --git a/.pi/settings.json b/.pi/settings.json index 9619dd26d..eef62d9e5 100644 --- a/.pi/settings.json +++ b/.pi/settings.json @@ -2,4 +2,4 @@ "packages": [ "npm:pi-interactive-shell@0.13.0" ] -} \ No newline at end of file +} diff --git a/@types/oxfmt_configuration_schema.json b/@types/oxfmt_configuration_schema.json deleted file mode 100644 index ee3ded8a6..000000000 --- a/@types/oxfmt_configuration_schema.json +++ /dev/null @@ -1,648 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Oxfmtrc", - "description": "Configuration options for the Oxfmt.\n\nMost options are the same as Prettier's options, but not all of them.\nIn addition, some options are our own extensions.", - "type": "object", - "properties": { - "arrowParens": { - "description": "Include parentheses around a sole arrow function parameter.\n\n- Default: `\"always\"`", - "allOf": [ - { - "$ref": "#/definitions/ArrowParensConfig" - } - ], - "markdownDescription": "Include parentheses around a sole arrow function parameter.\n\n- Default: `\"always\"`" - }, - "bracketSameLine": { - "description": "Put the `>` of a multi-line HTML (HTML, JSX, Vue, Angular) element at the end of the last line,\ninstead of being alone on the next line (does not apply to self closing elements).\n\n- Default: `false`", - "type": "boolean", - "markdownDescription": "Put the `>` of a multi-line HTML (HTML, JSX, Vue, Angular) element at the end of the last line,\ninstead of being alone on the next line (does not apply to self closing elements).\n\n- Default: `false`" - }, - "bracketSpacing": { - "description": "Print spaces between brackets in object literals.\n\n- Default: `true`", - "type": "boolean", - "markdownDescription": "Print spaces between brackets in object literals.\n\n- Default: `true`" - }, - "embeddedLanguageFormatting": { - "description": "Control whether to format embedded parts (For example, CSS-in-JS, or JS-in-Vue, etc.) in the file.\n\nNOTE: XXX-in-JS support is incomplete.\n\n- Default: `\"auto\"`", - "allOf": [ - { - "$ref": "#/definitions/EmbeddedLanguageFormattingConfig" - } - ], - "markdownDescription": "Control whether to format embedded parts (For example, CSS-in-JS, or JS-in-Vue, etc.) in the file.\n\nNOTE: XXX-in-JS support is incomplete.\n\n- Default: `\"auto\"`" - }, - "endOfLine": { - "description": "Which end of line characters to apply.\n\nNOTE: `\"auto\"` is not supported.\n\n- Default: `\"lf\"`\n- Overrides `.editorconfig.end_of_line`", - "allOf": [ - { - "$ref": "#/definitions/EndOfLineConfig" - } - ], - "markdownDescription": "Which end of line characters to apply.\n\nNOTE: `\"auto\"` is not supported.\n\n- Default: `\"lf\"`\n- Overrides `.editorconfig.end_of_line`" - }, - "htmlWhitespaceSensitivity": { - "description": "Specify the global whitespace sensitivity for HTML, Vue, Angular, and Handlebars.\n\n- Default: `\"css\"`", - "allOf": [ - { - "$ref": "#/definitions/HtmlWhitespaceSensitivityConfig" - } - ], - "markdownDescription": "Specify the global whitespace sensitivity for HTML, Vue, Angular, and Handlebars.\n\n- Default: `\"css\"`" - }, - "ignorePatterns": { - "description": "Ignore files matching these glob patterns.\nPatterns are based on the location of the Oxfmt configuration file.\n\n- Default: `[]`", - "type": "array", - "items": { - "type": "string" - }, - "markdownDescription": "Ignore files matching these glob patterns.\nPatterns are based on the location of the Oxfmt configuration file.\n\n- Default: `[]`" - }, - "insertFinalNewline": { - "description": "Whether to insert a final newline at the end of the file.\n\n- Default: `true`\n- Overrides `.editorconfig.insert_final_newline`", - "type": "boolean", - "markdownDescription": "Whether to insert a final newline at the end of the file.\n\n- Default: `true`\n- Overrides `.editorconfig.insert_final_newline`" - }, - "jsxSingleQuote": { - "description": "Use single quotes instead of double quotes in JSX.\n\n- Default: `false`", - "type": "boolean", - "markdownDescription": "Use single quotes instead of double quotes in JSX.\n\n- Default: `false`" - }, - "objectWrap": { - "description": "How to wrap object literals when they could fit on one line or span multiple lines.\n\nBy default, formats objects as multi-line if there is a newline prior to the first property.\nAuthors can use this heuristic to contextually improve readability, though it has some downsides.\n\n- Default: `\"preserve\"`", - "allOf": [ - { - "$ref": "#/definitions/ObjectWrapConfig" - } - ], - "markdownDescription": "How to wrap object literals when they could fit on one line or span multiple lines.\n\nBy default, formats objects as multi-line if there is a newline prior to the first property.\nAuthors can use this heuristic to contextually improve readability, though it has some downsides.\n\n- Default: `\"preserve\"`" - }, - "overrides": { - "description": "File-specific overrides.\nWhen a file matches multiple overrides, the later override takes precedence (array order matters).\n\n- Default: `[]`", - "type": "array", - "items": { - "$ref": "#/definitions/OxfmtOverrideConfig" - }, - "markdownDescription": "File-specific overrides.\nWhen a file matches multiple overrides, the later override takes precedence (array order matters).\n\n- Default: `[]`" - }, - "printWidth": { - "description": "Specify the line length that the printer will wrap on.\n\nIf you don't want line wrapping when formatting Markdown, you can set the `proseWrap` option to disable it.\n\n- Default: `100`\n- Overrides `.editorconfig.max_line_length`", - "type": "integer", - "format": "uint16", - "minimum": 0.0, - "markdownDescription": "Specify the line length that the printer will wrap on.\n\nIf you don't want line wrapping when formatting Markdown, you can set the `proseWrap` option to disable it.\n\n- Default: `100`\n- Overrides `.editorconfig.max_line_length`" - }, - "proseWrap": { - "description": "How to wrap prose.\n\nBy default, formatter will not change wrapping in markdown text since some services use a linebreak-sensitive renderer, e.g. GitHub comments and BitBucket.\nTo wrap prose to the print width, change this option to \"always\".\nIf you want to force all prose blocks to be on a single line and rely on editor/viewer soft wrapping instead, you can use \"never\".\n\n- Default: `\"preserve\"`", - "allOf": [ - { - "$ref": "#/definitions/ProseWrapConfig" - } - ], - "markdownDescription": "How to wrap prose.\n\nBy default, formatter will not change wrapping in markdown text since some services use a linebreak-sensitive renderer, e.g. GitHub comments and BitBucket.\nTo wrap prose to the print width, change this option to \"always\".\nIf you want to force all prose blocks to be on a single line and rely on editor/viewer soft wrapping instead, you can use \"never\".\n\n- Default: `\"preserve\"`" - }, - "quoteProps": { - "description": "Change when properties in objects are quoted.\n\n- Default: `\"as-needed\"`", - "allOf": [ - { - "$ref": "#/definitions/QuotePropsConfig" - } - ], - "markdownDescription": "Change when properties in objects are quoted.\n\n- Default: `\"as-needed\"`" - }, - "semi": { - "description": "Print semicolons at the ends of statements.\n\n- Default: `true`", - "type": "boolean", - "markdownDescription": "Print semicolons at the ends of statements.\n\n- Default: `true`" - }, - "singleAttributePerLine": { - "description": "Enforce single attribute per line in HTML, Vue, and JSX.\n\n- Default: `false`", - "type": "boolean", - "markdownDescription": "Enforce single attribute per line in HTML, Vue, and JSX.\n\n- Default: `false`" - }, - "singleQuote": { - "description": "Use single quotes instead of double quotes.\n\nFor JSX, you can set the `jsxSingleQuote` option.\n\n- Default: `false`", - "type": "boolean", - "markdownDescription": "Use single quotes instead of double quotes.\n\nFor JSX, you can set the `jsxSingleQuote` option.\n\n- Default: `false`" - }, - "sortImports": { - "description": "Sort import statements.\n\nUsing the similar algorithm as [eslint-plugin-perfectionist/sort-imports](https://perfectionist.dev/rules/sort-imports).\nFor details, see each field's documentation.\n\n- Default: Disabled", - "allOf": [ - { - "$ref": "#/definitions/SortImportsConfig" - } - ], - "markdownDescription": "Sort import statements.\n\nUsing the similar algorithm as [eslint-plugin-perfectionist/sort-imports](https://perfectionist.dev/rules/sort-imports).\nFor details, see each field's documentation.\n\n- Default: Disabled" - }, - "sortPackageJson": { - "description": "Sort `package.json` keys.\n\nThe algorithm is NOT compatible with [prettier-plugin-sort-packagejson](https://github.com/matzkoh/prettier-plugin-packagejson).\nBut we believe it is clearer and easier to navigate.\nFor details, see each field's documentation.\n\n- Default: `true`", - "allOf": [ - { - "$ref": "#/definitions/SortPackageJsonUserConfig" - } - ], - "markdownDescription": "Sort `package.json` keys.\n\nThe algorithm is NOT compatible with [prettier-plugin-sort-packagejson](https://github.com/matzkoh/prettier-plugin-packagejson).\nBut we believe it is clearer and easier to navigate.\nFor details, see each field's documentation.\n\n- Default: `true`" - }, - "sortTailwindcss": { - "description": "Sort Tailwind CSS classes.\n\nUsing the same algorithm as [prettier-plugin-tailwindcss](https://github.com/tailwindlabs/prettier-plugin-tailwindcss).\nOption names omit the `tailwind` prefix used in the original plugin (e.g., `config` instead of `tailwindConfig`).\nFor details, see each field's documentation.\n\n- Default: Disabled", - "allOf": [ - { - "$ref": "#/definitions/SortTailwindcssConfig" - } - ], - "markdownDescription": "Sort Tailwind CSS classes.\n\nUsing the same algorithm as [prettier-plugin-tailwindcss](https://github.com/tailwindlabs/prettier-plugin-tailwindcss).\nOption names omit the `tailwind` prefix used in the original plugin (e.g., `config` instead of `tailwindConfig`).\nFor details, see each field's documentation.\n\n- Default: Disabled" - }, - "tabWidth": { - "description": "Specify the number of spaces per indentation-level.\n\n- Default: `2`\n- Overrides `.editorconfig.indent_size`", - "type": "integer", - "format": "uint8", - "minimum": 0.0, - "markdownDescription": "Specify the number of spaces per indentation-level.\n\n- Default: `2`\n- Overrides `.editorconfig.indent_size`" - }, - "trailingComma": { - "description": "Print trailing commas wherever possible in multi-line comma-separated syntactic structures.\n\nA single-line array, for example, never gets trailing commas.\n\n- Default: `\"all\"`", - "allOf": [ - { - "$ref": "#/definitions/TrailingCommaConfig" - } - ], - "markdownDescription": "Print trailing commas wherever possible in multi-line comma-separated syntactic structures.\n\nA single-line array, for example, never gets trailing commas.\n\n- Default: `\"all\"`" - }, - "useTabs": { - "description": "Indent lines with tabs instead of spaces.\n\n- Default: `false`\n- Overrides `.editorconfig.indent_style`", - "type": "boolean", - "markdownDescription": "Indent lines with tabs instead of spaces.\n\n- Default: `false`\n- Overrides `.editorconfig.indent_style`" - }, - "vueIndentScriptAndStyle": { - "description": "Whether or not to indent the code inside `