diff --git a/00_admin/validation/audit-dev.json b/00_admin/validation/audit-dev.json new file mode 100644 index 0000000..f86680a --- /dev/null +++ b/00_admin/validation/audit-dev.json @@ -0,0 +1 @@ +{"passed": false, "timestamp": "2026-08-19T11:34:04+02:00", "scope": "full development tree audit", "command": "npm audit --registry=https://registry.npmjs.org/", "vulnerabilities": {"info": 0, "low": 0, "moderate": 9, "high": 7, "critical": 0, "total": 16}, "packages": ["adm-zip (high)", "fast-xml-parser (high)", "sharp (high, libvips CVEs)", "@opentelemetry/core (moderate)", "tar (moderate)"], "reach": "dev-only; every advisory is transitive under the @oh-my-pi/* peer SDK. npm audit --omit=dev reports 0 vulnerabilities, so no advisory reaches the published tarball (runtime deps are crc-32, yauzl, yazl).", "remediation": "none available in-window: npm audit fix --dry-run changes 0 packages and still reports 16. Requires an upstream @oh-my-pi release; forcing it would leave the >=16.4.6 <17 peer window."} diff --git a/00_admin/validation/audit-prod.json b/00_admin/validation/audit-prod.json new file mode 100644 index 0000000..8f0b65b --- /dev/null +++ b/00_admin/validation/audit-prod.json @@ -0,0 +1 @@ +{"passed": true, "timestamp": "2026-08-19T11:33:59+02:00", "scope": "production dependency audit", "command": "npm audit --omit=dev --registry=https://registry.npmjs.org/", "vulnerabilities": {"info": 0, "low": 0, "moderate": 0, "high": 0, "critical": 0}} diff --git a/README.md b/README.md index 0994c7a..a5ed94c 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Precondition: the tarball must contain `prompts/`. `src/prompts.ts` resolves bun ### Verification status -Overall readiness: GREEN for the six repository-local gates; four host-dependent gates remain NOT RUN. +Overall readiness: PARTIAL. Seven evidence-backed gates PASS, one FAILS on dev-only transitive advisories, and two host-dependent gates remain NOT RUN. `npm run verify` exits 0 from a clean `npm install` on this host. Each gate below is recorded as evidence under `00_admin/validation/` and rendered into `RELEASE.json` by `scripts/release-manifest.mjs`; a missing evidence file renders NOT RUN, and `passed !== true` renders FAIL. @@ -98,11 +98,13 @@ Overall readiness: GREEN for the six repository-local gates; four host-dependent | Structural verification | `node scripts/verify.mjs` | PASS | | Theme schema | asserted inside `scripts/verify.mjs` | PASS | | Pack dry run | `npm run pack:dry` | PASS | -| Production dependency audit | `npm audit --omit=dev` | NOT RUN | -| Full development tree audit | `npm audit` | NOT RUN | +| Production dependency audit | `npm audit --omit=dev` | PASS — 0 vulnerabilities | +| Full development tree audit | `npm audit` | **FAIL** — 16 advisories (9 moderate, 7 high) | | OMP plugin doctor | `npm run doctor` | NOT RUN — requires Bun plus an installed OMP host | | Interactive TUI smoke | manual OMP session | NOT RUN — requires an interactive terminal | +The development-tree FAIL is disclosed, not waived. Every advisory (`adm-zip`, `fast-xml-parser`, `sharp`/libvips, `@opentelemetry/core`, `tar`) is transitive under the `@oh-my-pi/*` peer SDK and dev-only: `npm audit --omit=dev` reports 0, and POM's three runtime dependencies are `crc-32`, `yauzl`, and `yazl`, so nothing reaches the published tarball. No in-window remediation exists — `npm audit fix --dry-run` changes 0 packages and still reports 16, because the vulnerable versions are pinned by the SDK's own ranges. Clearing it requires an upstream `@oh-my-pi` release; forcing it would leave the `>=16.4.6 <17` peer window. + Never satisfy a gate with a stub: do not vendor dependencies into the repository, and do not hand-write or copy the OMP SDK packages or the OMP theme schema. A gate satisfied by a stub proves nothing about the shipped extension. Dependency requirements per check: diff --git a/RELEASE.json b/RELEASE.json index 901523c..6b01266 100644 --- a/RELEASE.json +++ b/RELEASE.json @@ -20,7 +20,7 @@ "themes": 2, "behavioralTests": 9, "sourceFilesHashed": 97, - "sourceBytesHashed": 311578, + "sourceBytesHashed": 313817, "selfHashPolicy": "RELEASE.json is excluded from its own source inventory; final package and archive hashes are recorded externally." }, "verification": { @@ -97,12 +97,52 @@ } }, "productionDependencyAudit": { - "status": "NOT RUN", - "reason": "Requires network access to npm registry" + "status": "PASS", + "timestamp": "2026-08-19T11:33:59+02:00", + "scope": "production dependency audit", + "command": "npm audit --omit=dev --registry=https://registry.npmjs.org/", + "details": { + "passed": true, + "timestamp": "2026-08-19T11:33:59+02:00", + "scope": "production dependency audit", + "command": "npm audit --omit=dev --registry=https://registry.npmjs.org/", + "vulnerabilities": { + "info": 0, + "low": 0, + "moderate": 0, + "high": 0, + "critical": 0 + } + } }, "fullDevelopmentTreeAudit": { - "status": "NOT RUN", - "reason": "Requires network access to npm registry" + "status": "FAIL", + "timestamp": "2026-08-19T11:34:04+02:00", + "scope": "full development tree audit", + "command": "npm audit --registry=https://registry.npmjs.org/", + "details": { + "passed": false, + "timestamp": "2026-08-19T11:34:04+02:00", + "scope": "full development tree audit", + "command": "npm audit --registry=https://registry.npmjs.org/", + "vulnerabilities": { + "info": 0, + "low": 0, + "moderate": 9, + "high": 7, + "critical": 0, + "total": 16 + }, + "packages": [ + "adm-zip (high)", + "fast-xml-parser (high)", + "sharp (high, libvips CVEs)", + "@opentelemetry/core (moderate)", + "tar (moderate)" + ], + "reach": "dev-only; every advisory is transitive under the @oh-my-pi/* peer SDK. npm audit --omit=dev reports 0 vulnerabilities, so no advisory reaches the published tarball (runtime deps are crc-32, yauzl, yazl).", + "remediation": "none available in-window: npm audit fix --dry-run changes 0 packages and still reports 16. Requires an upstream @oh-my-pi release; forcing it would leave the >=16.4.6 <17 peer window." + } }, "ompPluginDoctor": { "status": "NOT RUN", @@ -142,8 +182,8 @@ }, { "path": "README.md", - "bytes": 9804, - "sha256": "3e444309f69a35f8e57bbcda88956a5db6ec6fd6d2621fbb67b50a718335ad8f" + "bytes": 10547, + "sha256": "75b9c26fc6a028575506b1da6df84c20a72323b58ba4acc53f801e9817bf9d5e" }, { "path": "agents/pom-architect.md", @@ -277,8 +317,8 @@ }, { "path": "docs/SETTINGS.md", - "bytes": 3072, - "sha256": "33bf545df6b85c03ed2512a01c78fff7191f1fcd3b5cb216055d53aa549c6d7d" + "bytes": 3229, + "sha256": "9226d0ad0bbb07d49dd8bd06c21febba2166145c1822e58cd621e9b6f472d605" }, { "path": "docs/TOOL_MATRIX.md", @@ -292,8 +332,8 @@ }, { "path": "docs/VERIFICATION.md", - "bytes": 10255, - "sha256": "0aab7e070ea90dbca30a0ed86a9afdedd2a26707ff359e09cc5f360b2e8a47a9" + "bytes": 11486, + "sha256": "dfd5c5dd0cadaf5e5e018ebd8d74a0ab718835ec46ad9163e79c91f6dd275a30" }, { "path": "docs/index.md", @@ -407,8 +447,8 @@ }, { "path": "scripts/release-manifest.mjs", - "bytes": 4714, - "sha256": "13dac5093fae178d34484f3bd374f34dbf450e84ea994bb5a0258dfcce5ac541" + "bytes": 4822, + "sha256": "ca0027d77c2277beeb283c567f9e618a4921b388c863dbb8e81dc381da7e58da" }, { "path": "scripts/smoke.ts", diff --git a/TASKLIST.md b/TASKLIST.md index a9fd34f..3d670a6 100644 --- a/TASKLIST.md +++ b/TASKLIST.md @@ -112,7 +112,7 @@ Purpose: publish only after package truth, docs truth, automation, and live veri Publication changed the facts behind several items above. Status recorded, not silently ticked: -- 1.2.3 and 1.3.1 are OBSOLETE, not open: the internal-Artifactory blocker is gone. The lockfile's `resolved` URLs were repointed from the dead mirror to `registry.npmjs.org` (integrity hashes and version pins untouched), `npm install` succeeded, and all six repository-local gates were rerun green afterwards. Manifest freshness and verification truth now agree. +- 1.2.3 and 1.3.1 are OBSOLETE, not open: the internal-Artifactory blocker is gone. The lockfile's `resolved` URLs were repointed from the dead mirror to `registry.npmjs.org` (integrity hashes and version pins untouched), `npm install` succeeded, and every repository-local gate was rerun afterwards. Both `npm audit` scopes are now evidence-backed too: production 0 vulnerabilities (PASS), full development tree 16 dev-only transitive advisories (FAIL, disclosed, no in-window remediation). Manifest freshness and verification truth now agree. - 7.2.3 stays PARTIAL: the Linux half is done — `check-docs` check 5 link-and-orphan PASS, wiki builds via `dev/wikis/build.sh pom`, and `https://wiki.pom.loca.zone/` plus deep pages return 200. The macOS/ego live roast remains platform-blocked (section 6). - 7.3.2 stays OPEN for the ego-audit clause only. Every other clause is satisfied: `prompts/` ships, `RELEASE.json` is evidence-backed from `00_admin/validation/`, docs index and wiki exist and are served, stale claims are corrected, and package metadata is live rather than reserved. - Publication facts: self-hosted Gitea (loopback 51400) serves `https://git.omp.loca.zone/jamminrebel/pom-omp.git` with anonymous HTTPS clone; `main` and `v2.0.0` are pushed; the docs wiki is served from `dev/wikis/pom/current`; `wiki.omp.loca.zone` was left untouched. diff --git a/docs/SETTINGS.md b/docs/SETTINGS.md index b036016..910e67e 100644 --- a/docs/SETTINGS.md +++ b/docs/SETTINGS.md @@ -57,4 +57,4 @@ built-in defaults | Bun | `>=1.3.14` | `engines.bun` | | Node.js + npm | Node 22 or newer | Local scripts and the `node:test` suite | -Verification state: the six repository-local gates pass and `npm run verify` exits 0 from a clean `npm install`; the four host-dependent gates (both `npm audit` scopes, `npm run doctor`, interactive TUI smoke) remain NOT RUN. See the README section Verification status. +Verification state: `npm run verify` exits 0 from a clean `npm install`. Seven evidence-backed gates PASS, `npm audit` on the full development tree FAILS on 16 dev-only transitive advisories under the `@oh-my-pi/*` peer SDK (production audit reports 0, so nothing reaches the published tarball), and two host-dependent gates (`npm run doctor`, interactive TUI smoke) remain NOT RUN. See the README section Verification status. diff --git a/docs/VERIFICATION.md b/docs/VERIFICATION.md index b81cd7c..feed410 100644 --- a/docs/VERIFICATION.md +++ b/docs/VERIFICATION.md @@ -1,6 +1,6 @@ --- title: Documentation verification -description: The six documentation-contract checks in scripts/check-docs.mjs and how to prove they fail +description: The seven documentation-contract checks in scripts/check-docs.mjs and how to prove they fail --- # Documentation verification @@ -32,10 +32,10 @@ POM docs verification FAIL (1/6: layout-contract) ``` The final line is `POM docs verification PASS` with exit code 0, or -`POM docs verification FAIL (/6: )` with exit code 1. All six checks always +`POM docs verification FAIL (/7: )` with exit code 1. All seven checks always run; one failure never hides another. -## The six checks +## The seven checks | # | Name | Canonical owner | Mirrors checked | |---|---|---|---| @@ -45,6 +45,7 @@ run; one failure never hides another. | 4 | `prompt-order-contract` | `src/prompts.ts` `composePromptStack` and `renderStagePrompt` | `POM.yml` `prompt_precedence` | | 5 | `link-and-orphan` | the `docs/` tree on disk | every `docs/*.md` page | | 6 | `release-evidence` | `scripts/release-manifest.mjs` gate readers | `RELEASE.json` `verification` | +| 7 | `package-metadata` | `package.json` `repository`, `homepage`, `bugs` | canonical `loca.zone` hosts, no placeholders | ### 1. resource-package-parity @@ -116,6 +117,17 @@ keeps them evidence-bound. Historical defect: the manifest carried hard-coded `"PASS"` strings for gates that had never been executed, so the release claimed verification it could not support. +### 7. package-metadata + +Defends canonical project coordinates. `package.json` `repository`, `homepage`, and `bugs` must each +be a string or an object carrying a string `url`, must not retain the `UNRESOLVED` placeholder, must +parse as absolute URLs, and must resolve to `loca.zone` or a subdomain of it, because no third-party +forge mirror has been confirmed for this package. A `repository.url` on owned infrastructure prints a +`note:` line recording its serving state. + +Historical defect: the metadata fields carried `UNRESOLVED` placeholders, and later recorded a +`repository.url` that was reserved but not serving Git while the prose claimed it was usable. + ## Known divergences `KNOWN_COMMAND_DIVERGENCES` in `scripts/check-docs.mjs` is the only escape hatch, and it contains @@ -131,17 +143,21 @@ Each row is a specified procedure for proving that a check actually defends its mutation, run `node scripts/check-docs.mjs`, confirm the named check reports `FAIL` with the expected message, then revert the mutation and confirm the run returns to `POM docs verification PASS`. Mutations are destructive edits to canonical files and must be -reverted before any commit or package step. This section specifies the procedure and the expected -output; it records no executed results, because the checker was not run when this page was written. +reverted before any commit or package step. Rows 1–6 specify the procedure and expected output +without recorded results. Row 7 was executed on 2026-08-19: pointing `homepage` at +`https://example.com/` produced `FAIL check 7/7 package-metadata` and +`POM docs verification FAIL (1/7: package-metadata)` with exit code 1, and restoring the file +returned `POM docs verification PASS` with exit code 0. | Check | Mutation to apply | Expected failure | |---|---|---| -| 1 `resource-package-parity` | Remove `"prompts"` from the `files` array in `package.json` | `FAIL check 1/6 resource-package-parity` — `package.json: files must contain "prompts" because src/prompts.ts and src/events.ts load it at runtime` | -| 2 `command-parity` | Delete the `/pom status [--json]` row from the `## Command map` table in `README.md` | `FAIL check 2/6 command-parity` — `/pom status is documented in src/command.ts HELP, POM.yml commands: but absent from README.md ## Command map` | -| 3 `layout-contract` | Rename `06_ledgers` to `06_logs` on the ledger line of `skills/pom/references/artifacts.md` | `FAIL check 3/6 layout-contract` — `skills/pom/references/artifacts.md:10 names "06_logs", which src/persistence.ts bootstrapProject never creates` | -| 4 `prompt-order-contract` | In `POM.yml` `prompt_precedence`, move the executable stage contract entry above the project-local fragment entry so it is no longer last | `FAIL check 4/6 prompt-order-contract` — `POM.yml: prompt_precedence must end with the executable stage contract appended by renderStagePrompt`, plus the misplaced-entry position | -| 5 `link-and-orphan` | In `docs/index.md`, change the Package inventory link target from `INVENTORY.md` to `INVENTORY-old.md` | `FAIL check 5/6 link-and-orphan` — `docs/index.md: broken relative link INVENTORY-old.md resolves to docs/INVENTORY-old.md, which does not exist on disk`, and `docs/INVENTORY.md: unreachable page` if `docs/navigation.md` no longer links it either | -| 6 `release-evidence` | Replace the `verification.runtimeSmoke` object in `RELEASE.json` with the bare string `"PASS"` | `FAIL check 6/6 release-evidence` — `RELEASE.json: verification.runtimeSmoke is the bare string "PASS"; every gate must be an evidence object carrying a status` | +| 1 `resource-package-parity` | Remove `"prompts"` from the `files` array in `package.json` | `FAIL check 1/7 resource-package-parity` — `package.json: files must contain "prompts" because src/prompts.ts and src/events.ts load it at runtime` | +| 2 `command-parity` | Delete the `/pom status [--json]` row from the `## Command map` table in `README.md` | `FAIL check 2/7 command-parity` — `/pom status is documented in src/command.ts HELP, POM.yml commands: but absent from README.md ## Command map` | +| 3 `layout-contract` | Rename `06_ledgers` to `06_logs` on the ledger line of `skills/pom/references/artifacts.md` | `FAIL check 3/7 layout-contract` — `skills/pom/references/artifacts.md:10 names "06_logs", which src/persistence.ts bootstrapProject never creates` | +| 4 `prompt-order-contract` | In `POM.yml` `prompt_precedence`, move the executable stage contract entry above the project-local fragment entry so it is no longer last | `FAIL check 4/7 prompt-order-contract` — `POM.yml: prompt_precedence must end with the executable stage contract appended by renderStagePrompt`, plus the misplaced-entry position | +| 5 `link-and-orphan` | In `docs/index.md`, change the Package inventory link target from `INVENTORY.md` to `INVENTORY-old.md` | `FAIL check 5/7 link-and-orphan` — `docs/index.md: broken relative link INVENTORY-old.md resolves to docs/INVENTORY-old.md, which does not exist on disk`, and `docs/INVENTORY.md: unreachable page` if `docs/navigation.md` no longer links it either | +| 6 `release-evidence` | Replace the `verification.runtimeSmoke` object in `RELEASE.json` with the bare string `"PASS"` | `FAIL check 6/7 release-evidence` — `RELEASE.json: verification.runtimeSmoke is the bare string "PASS"; every gate must be an evidence object carrying a status` | +| 7 `package-metadata` | Point `homepage` in `package.json` at `https://example.com/` | `FAIL check 7/7 package-metadata` — `package.json: homepage points at "example.com", which is outside the canonical loca.zone infrastructure` | Row 5 mutates a link rather than deleting a file, so the mutation is a one-token edit and the revert is exact. Row 6 must be reverted by regenerating the manifest with `npm run release:manifest` rather diff --git a/scripts/release-manifest.mjs b/scripts/release-manifest.mjs index 25c2573..b031ca3 100644 --- a/scripts/release-manifest.mjs +++ b/scripts/release-manifest.mjs @@ -53,6 +53,8 @@ const gates = { structuralVerification: readGate("00_admin/validation/structural.json", "node scripts/verify.mjs"), themeSchemaValidation: readGate("00_admin/validation/theme.json", "theme schema validation"), npmPackDryRun: readGate("00_admin/validation/pack.json", "npm pack --dry-run"), + productionDependencyAudit: readGate("00_admin/validation/audit-prod.json", "npm audit --omit=dev"), + fullDevelopmentTreeAudit: readGate("00_admin/validation/audit-dev.json", "npm audit"), }; const releaseDate = new Date().toISOString().split("T")[0]; @@ -89,8 +91,8 @@ const release = { structuralVerification: gates.structuralVerification, themeSchemaValidation: gates.themeSchemaValidation, npmPackDryRun: gates.npmPackDryRun, - productionDependencyAudit: { status: "NOT RUN", reason: "Requires network access to npm registry" }, - fullDevelopmentTreeAudit: { status: "NOT RUN", reason: "Requires network access to npm registry" }, + productionDependencyAudit: gates.productionDependencyAudit, + fullDevelopmentTreeAudit: gates.fullDevelopmentTreeAudit, ompPluginDoctor: { status: "NOT RUN", reason: "Requires Bun + OMP installation" }, interactiveTuiSmoke: { status: "NOT RUN", reason: "Requires a live OMP terminal host" }, },