FN-5927: categorize pnpm build scripts for install
Document and enforce pnpm build-script review decisions to prevent ignored-script install warnings. - add reviewed ignoredBuiltDependencies entries to the root pnpm config and mirror the effective policy in pnpm-workspace.yaml - add a regression test that verifies reviewed dependencies are categorized exactly once and stay aligned across both config files - document the pnpm build-script approval policy in contributing docs and link plugin authoring guidance from AGENTS.md and PLUGIN_AUTHORING.md Files changed: AGENTS.md | 5 ++ docs/PLUGIN_AUTHORING.md | 3 +- docs/contributing.md | 14 ++++ package.json | 9 +++ pnpm-workspace.yaml | 14 ++++ scripts/__tests__/pnpm-build-scripts-config.test.mjs | 74 ++++++++++++++++++++++ 6 files changed, 118 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-5927 Fusion-Task-Lineage: 192bbed9-c5ed-45cb-b4bd-fb18514e2783
This commit is contained in:
@@ -93,6 +93,10 @@ pnpm verify:workspace
|
||||
- Motivating incidents: streamed-response spacing was fixed three times before the invariant was fully covered (FN-5787, FN-5789, FN-5803), the usage "Show hidden" button regressed three times before broader coverage stuck (FN-5797, FN-5875, FN-5919), and the auto-merge blank-dashboard fix re-opened after desktop-only coverage missed mobile Android (FN-5751).
|
||||
- If a regression test only proves the exact reported case, it is incomplete; extend it until the invariant holds across all known surfaces.
|
||||
|
||||
### STANDING DIRECTIVE: Buttons Are Frozen
|
||||
|
||||
- Buttons Are Frozen (2026-05-13): when touching dashboard button styling or behavior, preserve the existing sizing/layout contract unless the task explicitly changes it and the affected docs/tests are updated together.
|
||||
|
||||
### Port 4040 is Reserved
|
||||
|
||||
Never kill processes on port 4040 and never start test servers on 4040. Use `--port 0` or another free port.
|
||||
@@ -161,6 +165,7 @@ Scoped exception (FN-5819): shared-branch-group members (`branchContext.assignme
|
||||
- `./docs/architecture.md` — lifecycle invariants, self-healing rules, reliability interaction backstops, run-audit internals.
|
||||
- `./docs/testing.md` — full testing lanes, worker fanout guidance, test taxonomy, and file organization.
|
||||
- `./docs/dashboard-guide.md` — dashboard behavior and **Styling Guide** details. User-facing docs for Merge Advance Notice and Smart Pull live here.
|
||||
- `./docs/PLUGIN_AUTHORING.md` — plugin authoring guide, lifecycle hooks, routes, tools, and dashboard-extension surfaces.
|
||||
- `./docs/agents.md` — pi extension scope, coordination tools, checkout leasing, runtime config.
|
||||
- `./docs/settings-reference.md` — model-selection hierarchy, mock provider mode, token budget precedence, presets.
|
||||
- `./docs/storage.md` — hybrid storage model details.
|
||||
|
||||
@@ -5,7 +5,6 @@ A comprehensive guide to creating Fusion plugins that extend the task board with
|
||||
## Table of Contents
|
||||
|
||||
1. [Getting Started](#1-getting-started)
|
||||
- [External authoring guide](./plugins/external-authoring.md)
|
||||
2. [Plugin Manifest Reference](#2-plugin-manifest-reference)
|
||||
3. [Plugin Settings Schema](#3-plugin-settings-schema)
|
||||
4. [Available Hooks and Signatures](#4-available-hooks-and-signatures)
|
||||
@@ -26,6 +25,8 @@ A comprehensive guide to creating Fusion plugins that extend the task board with
|
||||
|
||||
---
|
||||
|
||||
See also: [External Plugin Authoring guide](./plugins/external-authoring.md)
|
||||
|
||||
## 1. Getting Started
|
||||
|
||||
### What Are Fusion Plugins?
|
||||
|
||||
@@ -19,6 +19,20 @@ Thanks for contributing to Fusion.
|
||||
pnpm install --frozen-lockfile
|
||||
```
|
||||
|
||||
### pnpm build-scripts approval policy
|
||||
|
||||
pnpm v10 blocks dependency `preinstall`/`install`/`postinstall` scripts by default and reports any uncategorized packages in the install output.
|
||||
|
||||
When a package is flagged:
|
||||
|
||||
- Approve it only when this repo genuinely needs that dependency's build script for supported runtime/build paths.
|
||||
- Ignore it when prebuilt artifacts, optional-native fallbacks, or release-only tooling make the script unnecessary for normal workspace verification.
|
||||
- Record every reviewed package in exactly one bucket: `onlyBuiltDependencies` or `ignoredBuiltDependencies`.
|
||||
|
||||
Fusion currently keeps the reviewed decision set documented in the root `package.json` `pnpm` block and mirrored in `pnpm-workspace.yaml`, which is the effective pnpm v10.33 install-time config read by `pnpm install`.
|
||||
|
||||
The guard test `node --test scripts/__tests__/pnpm-build-scripts-config.test.mjs` (also covered by `pnpm test:scripts`) enforces that the reviewed dependencies stay categorized, deduped, and non-overlapping so the ignored-build-scripts warning cannot silently return.
|
||||
|
||||
### Build workspace packages
|
||||
|
||||
```bash
|
||||
|
||||
@@ -60,6 +60,15 @@
|
||||
"dist:desktop:win": "pnpm --filter @fusion/desktop build && pnpm --filter @fusion/desktop dist:win"
|
||||
},
|
||||
"pnpm": {
|
||||
"ignoredBuiltDependencies": [
|
||||
"@google/genai",
|
||||
"better-sqlite3",
|
||||
"cpu-features",
|
||||
"electron-winstaller",
|
||||
"keytar",
|
||||
"sharp",
|
||||
"ssh2"
|
||||
],
|
||||
"onlyBuiltDependencies": [
|
||||
"@homebridge/node-pty-prebuilt-multiarch",
|
||||
"electron",
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
ignoredBuiltDependencies:
|
||||
- '@google/genai'
|
||||
- better-sqlite3
|
||||
- cpu-features
|
||||
- electron-winstaller
|
||||
- keytar
|
||||
- sharp
|
||||
- ssh2
|
||||
onlyBuiltDependencies:
|
||||
- '@homebridge/node-pty-prebuilt-multiarch'
|
||||
- electron
|
||||
- esbuild
|
||||
- koffi
|
||||
- protobufjs
|
||||
packages:
|
||||
- "packages/*"
|
||||
- "plugins/examples/*"
|
||||
|
||||
74
scripts/__tests__/pnpm-build-scripts-config.test.mjs
Normal file
74
scripts/__tests__/pnpm-build-scripts-config.test.mjs
Normal file
@@ -0,0 +1,74 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { parse as parseYaml } from "yaml";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const repoRoot = path.resolve(__dirname, "../..");
|
||||
|
||||
const decidedDeps = [
|
||||
"@google/genai",
|
||||
"better-sqlite3",
|
||||
"cpu-features",
|
||||
"electron-winstaller",
|
||||
"keytar",
|
||||
"sharp",
|
||||
"ssh2",
|
||||
];
|
||||
|
||||
function readPackagePnpmConfig() {
|
||||
const packageJson = JSON.parse(readFileSync(path.join(repoRoot, "package.json"), "utf8"));
|
||||
return packageJson.pnpm ?? {};
|
||||
}
|
||||
|
||||
function readWorkspaceConfig() {
|
||||
return parseYaml(readFileSync(path.join(repoRoot, "pnpm-workspace.yaml"), "utf8")) ?? {};
|
||||
}
|
||||
|
||||
function assertUniqueArray(values, label) {
|
||||
assert.ok(Array.isArray(values), `${label} must be an array`);
|
||||
const duplicates = values.filter((value, index) => values.indexOf(value) !== index);
|
||||
assert.deepEqual(duplicates, [], `${label} must not contain duplicates`);
|
||||
}
|
||||
|
||||
function assertDisjoint(left, right, label) {
|
||||
const overlap = left.filter((value) => right.includes(value));
|
||||
assert.deepEqual(overlap, [], `${label} must be disjoint`);
|
||||
}
|
||||
|
||||
function assertDecisionCoverage(config, label) {
|
||||
const ignored = config.ignoredBuiltDependencies ?? [];
|
||||
const approved = config.onlyBuiltDependencies ?? [];
|
||||
|
||||
assertUniqueArray(ignored, `${label}.ignoredBuiltDependencies`);
|
||||
assertUniqueArray(approved, `${label}.onlyBuiltDependencies`);
|
||||
assertDisjoint(ignored, approved, `${label} build-script arrays`);
|
||||
|
||||
for (const dep of decidedDeps) {
|
||||
const membershipCount = Number(ignored.includes(dep)) + Number(approved.includes(dep));
|
||||
assert.equal(
|
||||
membershipCount,
|
||||
1,
|
||||
`${label} must categorize ${dep} in exactly one build-script array`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
test("package.json records the reviewed ignored-build decisions", () => {
|
||||
assertDecisionCoverage(readPackagePnpmConfig(), "package.json#pnpm");
|
||||
});
|
||||
|
||||
test("pnpm-workspace.yaml keeps the effective install-time build-script policy aligned", () => {
|
||||
const workspaceConfig = readWorkspaceConfig();
|
||||
assertDecisionCoverage(workspaceConfig, "pnpm-workspace.yaml");
|
||||
|
||||
const packageConfig = readPackagePnpmConfig();
|
||||
assert.deepEqual(
|
||||
workspaceConfig.ignoredBuiltDependencies,
|
||||
packageConfig.ignoredBuiltDependencies,
|
||||
"workspace ignoredBuiltDependencies should match the documented package.json decisions",
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user