Skip to content

Improve contributor onboarding and policy docs#47

Merged
dimaMachina merged 5 commits into
mainfrom
update-contributing
Jun 26, 2026
Merged

Improve contributor onboarding and policy docs#47
dimaMachina merged 5 commits into
mainfrom
update-contributing

Conversation

@dimaMachina

@dimaMachina dimaMachina commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR refreshes the public contributor onboarding and policy docs for OpenKnowledge's public mirror. It explains the public-to-internal PR review flow, adds CLA, Code of Conduct, and security-reporting guidance, and tightens the PR checklist so contributors know what maintainers expect before review.

Key decisions

  • Keep the contributor workflow self-contained in CONTRIBUTING.md, including the internal mirror flow, stale PR handling, CLA signing, local setup, changeset expectations, and public export boundary.
  • Keep changeset bump guidance in the three places that serve different readers: .changeset/README.md for changeset authors, AGENTS.md for agents, and CONTRIBUTING.md for external contributors.
  • Preserve the existing PR-template structure from main while expanding the checklist with docs, dependency, secrets, verification, and CLA reminders.

Verification

  • git diff --check origin/main...HEAD -- .changeset/README.md .github/PULL_REQUEST_TEMPLATE.md AGENTS.md CLA.md CODE_OF_CONDUCT.md CONTRIBUTING.md SECURITY.md passed.
  • No manual QA performed. This PR only changes contributor and policy documentation, not runtime behavior.

@vercel

vercel Bot commented Jun 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
open-knowledge-docs Ready Ready Preview, Comment Jun 5, 2026 4:28pm

Request Review

@dimaMachina
dimaMachina marked this pull request as ready for review June 10, 2026 20:49

@inkeep-internal-ci inkeep-internal-ci Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(7) Total Issues | Risk: Low

🟡 Minor (1) 🟡

🟡 1) .changeset/README.md:23-28 · AGENTS.md:62 · CONTRIBUTING.md:105-106 Ambiguous bump category for mid-sized features

Issue: The old changeset table had an explicit row: "New feature, additive API surface → minor." The new table replaces it with "Large new surface → minor" and "Bug fix, UI improvement, or small user-visible addition → patch." This leaves an uncovered middle ground: a contributor adding a new CLI flag, a new MCP tool, or a new editor command can't confidently determine whether it qualifies as "large" (→ minor) or "small" (→ patch). The same ambiguity is consistently present in all three locations where bump rules are documented.

Why: The old "additive API surface" row was a catch-all for features; now, the distinction relies on a subjective size judgment ("large" vs. "small") with no examples or heuristic to guide the decision. This is especially impactful for external contributors who lack team context about what counts as "large."

Fix: Add a brief clarifying parenthetical or examples in .changeset/README.md (the most authoritative source) to define the boundary. For example:

| Large new surface (new editor feature, new MCP tool, new CLI command) | `minor` |
| Bug fix, UI improvement, or small addition to an existing surface     | `patch` |

Then propagate the same clarifying language to AGENTS.md and CONTRIBUTING.md so all three stay aligned.

Refs:

💭 Consider (5) 💭

💭 1) CLA.md:33 · CONTRIBUTING.md:28 Corporate CLA paragraph verbatim-duplicated across two files

Issue: The corporate CLA next-step sentence appears identically in both CLA.md and CONTRIBUTING.md, with a minor phrasing divergence ("your PR" vs. "your pull request"). Two homes for the same policy text means a future process change must be made in both places.
Why: CONTRIBUTING.md already links to CLA.md as the source of truth for signing instructions (line 26). Repeating the corporate-CLA paragraph rather than cross-referencing creates a drift hazard.
Fix: In CONTRIBUTING.md, replace the verbatim sentence with a cross-reference: "For corporate CLAs, see CLA.md."
Refs: CLA.md:33 · CONTRIBUTING.md:28

💭 2) CODE_OF_CONDUCT.md:25 Enforcement section lacks a concrete reporting channel

Issue: The enforcement section says "contact an Inkeep maintainer privately when possible" but provides no email, GitHub username, or link. SECURITY.md names a concrete channel (incidents@inkeep.com); the CoC does not.
Why: A contributor trying to report harassment cannot act on this instruction without guessing how to reach a maintainer privately. The more sensitive report type (conduct) has a less specific path than vulnerability reports.
Fix: Add a contact mechanism — either reuse incidents@inkeep.com or add a dedicated conduct email. Alternatively, cross-reference SECURITY.md's email if that channel is intentionally shared.
Refs: CODE_OF_CONDUCT.md:25 · SECURITY.md:7

💭 3) SECURITY.md:14 Acknowledgement SLA is vague

Issue: "We will acknowledge reports as soon as practical" gives no fallback timeframe. Reporters who receive no response will often escalate publicly or assume the report was lost.
Why: A soft commitment like "within 5 business days" (common for projects this size) significantly improves reporter confidence without binding to a hard SLA.
Fix: Consider adding: "We aim to acknowledge within 5 business days. If you have not heard back, reply to your original email."
Refs: SECURITY.md:14

Inline Comments:

  • 💭 Consider: .github/pull_request_template.md:5-7 "Verification" section has a bare placeholder with no guidance on expected content
  • 💭 Consider: AGENTS.md:62 Dropped "most changesets are patch" frequency heuristic useful for agent decision-making

🧹 While You're Here (1) 🧹

🧹 1) .changeset/README.md:36 bun changeset vs bun run changeset inconsistency

Issue: .changeset/README.md line 36 says "run bun changeset" while AGENTS.md line 61 and CONTRIBUTING.md line 99 both use bun run changeset. Both forms work, but the inconsistency may confuse contributors who consult multiple docs.
Why: This PR just aligned changeset bump rules across all three files — normalizing the command form would complete the alignment.
Fix: Change .changeset/README.md line 36 from bun changeset to bun run changeset.
Refs: .changeset/README.md:36 · AGENTS.md:61 · CONTRIBUTING.md:99


💡 APPROVE WITH SUGGESTIONS

Summary: Clean, well-scoped docs PR that adds valuable contributor onboarding infrastructure (SECURITY.md, CODE_OF_CONDUCT.md, PR template) and aligns changeset rules across three documents. The main actionable item is clarifying the boundary between minor and patch for mid-sized features — adding examples to the bump-rules table would eliminate the ambiguity the old "New feature" row previously covered. The remaining items are small consistency and discoverability improvements.

Discarded (4)
Location Issue Reason Discarded
CODE_OF_CONDUCT.md Custom CoC vs Contributor Covenant Deliberate design decision; the custom CoC is materially adequate and the choice is a maintainer preference, not a defect.
CLA.md:1 Header change from "Based on" to "Adapted from" removes transparency about verbatim Apache text LOW confidence — "Adapted from" is arguably more accurate given the additions, and the body text is unchanged so contributors can still compare.
.github/pull_request_template.md Missing Code of Conduct acknowledgement checkbox CoC is a general behavior expectation, not a per-PR action like CLA signing. Adding it would over-checklist the template.
multi-file Changeset rules in 3 places should have a single source of truth Intentional information architecture — each doc serves a different audience (agents, contributors, changeset authors). The 3 copies are in sync as of this PR.
Reviewers (4)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-consistency 5 1 1 1 0 0 2
pr-review-product 5 0 2 0 1 0 2
pr-review-precision 3 0 1 0 1 0 1
pr-review-docs 0 0 0 0 0 0 0
Total 13 1 4 1 2 0 5

Note: pr-review-docs correctly scoped out — all changed files are internal contributor/policy docs, not customer-facing documentation in the docs site roots. Findings were deduplicated across consistency/product/precision (corporate CLA duplication and CoC contact gap raised by multiple reviewers).

Comment thread .github/pull_request_template.md Outdated
Comment on lines +5 to +7
## Verification

-

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 Consider — "Verification" section purpose is unclear

Issue: The "Verification" section is a bare bullet placeholder (-) with no guidance on what content it expects. First-time contributors will likely skip it or duplicate their Summary text, since the two sections look identical.

Why: The checklist item on line 12 ("I added tests or a manual verification note") implies this is where that note should go — but the section header alone doesn't convey that. A brief HTML comment would make the section self-documenting without adding visual noise.

Fix: (1-click apply)

Suggested change
## Verification
-
## Verification
<!-- What did you run or observe to confirm the change works? (test command, screenshot, manual steps) -->
-

Refs:

Comment thread AGENTS.md Outdated

- Create one with `bun run changeset`, or hand-write a file named `.changeset/<descriptive-kebab-slug>.md`.
- Front-matter: at minimum `'@inkeep/open-knowledge': patch`. Open Knowledge follows semver with a **pre-1.0 shift-down**: while we're below `1.0.0`, what semver would call a major (breaking API change) is encoded as `minor`, and what semver would call a minor (new feature) is encoded as `patch`. Most changesets are `patch`. `minor` is rare — reserve it for large API contract changes or large feature additions. **Never declare `major` pre-1.0** (see the `"//"` line in `.changeset/config.json`).
- Bump type: use `patch` for bug fixes, UI improvements, and small user-visible additions; use `minor` for breaking API, schema, or CLI changes, and for large new surfaces. **Never declare `major` pre-1.0** (see the `"//"` line in `.changeset/config.json`).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 Consider — Dropped frequency heuristic useful for agent decision-making

Issue: The rewrite removed the operational heuristic "Most changesets are patch. minor is rare" that was in the previous text. AGENTS.md is the primary doc parsed by AI agents choosing a bump type — the frequency prior ("reach for patch by default") is arguably the most decision-useful signal for an agent resolving ambiguous cases.

Why: The mapping rules tell you what each level means; the frequency heuristic tells you which to reach for under uncertainty. Dropping it slightly raises the chance an agent over-classifies a change as minor.

Fix: (1-click apply)

Suggested change
- Bump type: use `patch` for bug fixes, UI improvements, and small user-visible additions; use `minor` for breaking API, schema, or CLI changes, and for large new surfaces. **Never declare `major` pre-1.0** (see the `"//"` line in `.changeset/config.json`).
- Bump type: use `patch` for bug fixes, UI improvements, and small user-visible additions; use `minor` for breaking API, schema, or CLI changes, and for large new surfaces — most changesets are `patch`. **Never declare `major` pre-1.0** (see the `"//"` line in `.changeset/config.json`).

Refs:

@github-actions

Copy link
Copy Markdown

This pull request has been automatically marked as stale because it has not had recent activity.
It will be closed in 7 days if no further activity occurs.

If this PR is still relevant:

  • Rebase it on the latest main branch
  • Add a comment explaining its current status
  • Request a review if it's ready

Thank you for your contributions!

@github-actions github-actions Bot added the stale label Jun 18, 2026
@github-actions

Copy link
Copy Markdown

This pull request has been automatically closed due to inactivity.

If you'd like to continue working on this, please:

  1. Create a new branch from the latest main
  2. Cherry-pick your commits or rebase your changes
  3. Open a new pull request

Thank you for your understanding!

@github-actions github-actions Bot closed this Jun 26, 2026
@github-actions
github-actions Bot deleted the update-contributing branch June 26, 2026 00:44
@dimaMachina
dimaMachina restored the update-contributing branch June 26, 2026 17:34
@dimaMachina dimaMachina reopened this Jun 26, 2026
@github-actions github-actions Bot deleted a comment from inkeep-internal-ci Bot Jun 26, 2026

@inkeep-internal-ci inkeep-internal-ci Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(7) Total Issues | Risk: Low

Re-review note: The branch sync (31ea8c7 — merge main into update-contributing) introduced no content changes to the 7 PR files. All file blob SHAs match the prior review. The prior review's findings remain unresolved and are carried forward below.

🕐 Pending Recommendations (7)

  • 🟡 .changeset/README.md:23-28 · AGENTS.md:62 · CONTRIBUTING.md:105-106 Ambiguous bump category for mid-sized features — the "Large new surface" vs "small user-visible addition" boundary lacks examples or a heuristic for contributors to decide between minor and patch.
  • 💭 CLA.md:33 · CONTRIBUTING.md:28 Corporate CLA paragraph duplicated verbatim across two files — a drift hazard; CONTRIBUTING.md could cross-reference CLA.md instead.
  • 💭 CODE_OF_CONDUCT.md:25 Enforcement section says "contact an Inkeep maintainer privately" but provides no email, username, or link — less specific than SECURITY.md's incidents@inkeep.com.
  • 💭 SECURITY.md:14 Acknowledgement SLA "as soon as practical" is vague — a soft target like "within 5 business days" would improve reporter confidence.
  • 💭 .github/pull_request_template.md:5-7 "How this was verified" section has a bare placeholder with no guidance on expected content.
  • 💭 AGENTS.md:62 Dropped "most changesets are patch" frequency heuristic — useful prior for agent decision-making under ambiguity.
  • 🧹 .changeset/README.md:36 bun changeset vs bun run changeset inconsistency across docs — this PR aligned bump rules but the command form still diverges.

💡 APPROVE WITH SUGGESTIONS

Summary: Clean, well-scoped docs PR — the contributor onboarding additions (SECURITY.md, CODE_OF_CONDUCT.md, PR template) and changeset-rule alignment are solid. This sync only merged main into the branch with no content changes, so the 7 prior findings still stand. The most actionable item remains clarifying the minor vs patch boundary with concrete examples. The remaining items are small consistency and discoverability improvements worth considering before merge.

Discarded (0)

No new findings to discard — re-review of unchanged content.

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
(none dispatched) 0 0 0 0 0 0 0
Total 0 0 0 0 0 0 0

Note: No sub-reviewers dispatched — this sync event (pull request synchronize) merged main into the PR branch without modifying any of the 7 PR files. All file blob SHAs match the prior review commit (4a659df). The 7 prior findings are carried forward as Pending Recommendations.

@github-actions github-actions Bot deleted a comment from inkeep-internal-ci Bot Jun 26, 2026

@inkeep-internal-ci inkeep-internal-ci Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(0) Total Issues | Risk: Low

Re-review note: Commit 0caaa49 (docs(contributing): clarify review feedback) addresses all 7 findings from the prior review. Verified each fix below.

Prior Finding Status Verification
🟡 Ambiguous bump category for mid-sized features ✅ Resolved Examples added to all 3 files: (new CLI command, MCP tool, or editor feature)
💭 Corporate CLA duplicated across files ✅ Resolved CONTRIBUTING.md now cross-references: For corporate CLAs, see CLA.md
💭 CoC enforcement lacks reporting channel ✅ Resolved Changed to email incidents@inkeep.com privately when possible
💭 SECURITY.md SLA vague ✅ Resolved Changed to within 5 business days + follow-up guidance
💭 PR template verification section unclear ✅ Resolved HTML comment now describes expected content
💭 Dropped "most changesets are patch" heuristic ✅ Resolved Re-added Most changesets are patch. to AGENTS.md
🧹 bun changeset vs bun run changeset ✅ Resolved Normalized to bun run changeset in .changeset/README.md

✅ APPROVE

Summary: All 7 prior review findings have been addressed cleanly. The changeset bump rules are now consistent across .changeset/README.md, AGENTS.md, and CONTRIBUTING.md with concrete examples. The new policy files (SECURITY.md, CODE_OF_CONDUCT.md) have actionable reporting channels and SLA commitments. The PR template checklist is well-structured with clear expectations. Ship it! 🚀

Discarded (0)

No findings to discard.

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
(none dispatched) 0 0 0 0 0 0 0
Total 0 0 0 0 0 0 0

Note: No sub-reviewers dispatched for this re-review — the latest commit (0caaa49) is a targeted response to the 7 prior findings. Manual verification of each fix confirmed all issues resolved. The prior review (which dispatched 4 sub-reviewers) provided the baseline findings.

@github-actions github-actions Bot deleted a comment from inkeep-internal-ci Bot Jun 26, 2026
@dimaMachina
dimaMachina force-pushed the update-contributing branch from 149683a to 9f560ba Compare June 26, 2026 18:18
@github-actions github-actions Bot deleted a comment from inkeep-internal-ci Bot Jun 26, 2026

@inkeep-internal-ci inkeep-internal-ci Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(1) Total Issues | Risk: Low

Re-review note: Commit 9f560ba (revert) partially reverts the prior fix commit 0caaa49 — reverting .changeset/README.md's bump table to simpler wording and reverting .github/PULL_REQUEST_TEMPLATE.md back to its main state (no net diff). The 5 non-changeset findings from the prior review remain resolved (CLA cross-ref, CoC email, SECURITY.md SLA, AGENTS.md heuristic, bun run changeset normalization).

🟡 Minor (1) 🟡

Inline Comments:

  • 🟡 Minor: .changeset/README.md:25-27 Bump table wording ("New feature, additive API surface" → minor) diverges from AGENTS.md / CONTRIBUTING.md ("large new surfaces" → minor, "small additions" → patch)

💡 APPROVE WITH SUGGESTIONS

Summary: Clean docs PR — the new policy files (SECURITY.md, CODE_OF_CONDUCT.md), CLA improvements, and contributor flow documentation are solid. The only open item is that the revert re-introduced a wording divergence in the changeset bump table: .changeset/README.md classifies all new features as minor, while AGENTS.md and CONTRIBUTING.md only classify large new surfaces as minor. Since aligning these three docs is the PR's stated goal, it's worth a quick sync before merging. See the inline comment for a 1-click suggestion.

Discarded (0)

No findings to discard.

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
(none dispatched) 0 0 0 0 0 0 0
Total 0 0 0 0 0 0 0

Note: No sub-reviewers dispatched — this synchronize event is a small 2-file partial revert (9f560ba). The orchestrator assessed the revert's impact directly against the prior review baseline. The prior review (which dispatched 4 sub-reviewers across 3 rounds) provided the baseline findings; 6 of 7 remain resolved, 1 was re-introduced.

@dimaMachina
dimaMachina force-pushed the update-contributing branch from 9f560ba to 9278373 Compare June 26, 2026 18:28
@github-actions github-actions Bot deleted a comment from inkeep-internal-ci Bot Jun 26, 2026
@inkeep-internal-ci

inkeep-internal-ci Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Claude finished @dimaMachina's task in 4m 54s —— View job


PR Review Complete ✅

  • Phase 1: Analyze context & load workflow reference
  • Phase 2: Assess scope — no sub-reviewers needed (re-review of resolved findings)
  • Phase 3-4: Triage & validate — all 8 findings across 4 review rounds verified resolved
  • Phase 5-6: Submit review — APPROVED

Result: All prior findings have been addressed. The latest force-push (9278373) narrowed the revert to only the PR template (removing it from the diff), while preserving all .changeset/README.md fixes. The changeset bump rules, policy docs, and contributor onboarding flow are clean and consistent.

@inkeep-internal-ci inkeep-internal-ci Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(0) Total Issues | Risk: Low

Re-review note: Commit 9278373 (revert) replaces the prior 9f560ba revert via force-push. This version reverts only .github/PULL_REQUEST_TEMPLATE.md (removing it from the PR diff), while preserving all .changeset/README.md fixes from 0caaa49. This resolves the bump-table wording divergence flagged in the last review. All 7 original findings remain resolved.

Prior Finding Status Verification
🟡 Ambiguous bump category for mid-sized features ✅ Resolved Examples (new CLI command, MCP tool, or editor feature) present in all 3 files
💭 Corporate CLA duplicated across files ✅ Resolved CONTRIBUTING.md cross-references: For corporate CLAs, see CLA.md
💭 CoC enforcement lacks reporting channel ✅ Resolved email incidents@inkeep.com privately when possible
💭 SECURITY.md SLA vague ✅ Resolved within 5 business days + follow-up guidance
💭 PR template verification section unclear ✅ N/A File reverted — no longer in PR diff
💭 Dropped "most changesets are patch" heuristic ✅ Resolved Most changesets are patch. restored in AGENTS.md
🧹 bun changeset vs bun run changeset ✅ Resolved Normalized to bun run changeset in .changeset/README.md
🟡 Bump table wording divergence (Review 4) ✅ Resolved Force-push preserved .changeset/README.md fix; all 3 files now aligned

✅ APPROVE

Summary: All 8 findings across 4 review rounds have been addressed. The changeset bump rules are consistent across .changeset/README.md, AGENTS.md, and CONTRIBUTING.md with concrete examples. The new policy files (SECURITY.md, CODE_OF_CONDUCT.md) have actionable reporting channels and SLA commitments. The contributor onboarding flow in CONTRIBUTING.md is clear and self-contained. Clean work — ship it! 🚀

Discarded (0)

No findings to discard.

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
(none dispatched) 0 0 0 0 0 0 0
Total 0 0 0 0 0 0 0

Note: No sub-reviewers dispatched — this synchronize event is a force-push that replaced the prior revert (9f560ba) with a narrower revert (9278373) affecting only the PR template. The 6 remaining PR files are unchanged from the previously APPROVED state (0caaa49). Manual verification confirmed all 8 prior findings resolved.

@dimaMachina
dimaMachina merged commit 1292dc0 into main Jun 26, 2026
2 of 3 checks passed
@github-actions

Copy link
Copy Markdown

This public PR was merged directly in the public repo. The matching monorepo PR was left open for manual follow-up because agents-private remains the source of truth.

Matching internal PR: #1656

inkeep-oss-sync Bot pushed a commit that referenced this pull request Jul 17, 2026
* spec(open-knowledge): remove Bun, Node as sole execution engine

Full /spec workflow output: problem framing, 7-worker research wave
(toolchain, test-runner, TS-exec scan, public mirror, gate semantics,
CI timings, vitest spike on packages/core), decisions D1-D16, phased
rollout, 26-seam verification plan, and two cold-reader passes
(design review + claim audit) with corrections applied.

Headline decisions (agent-derived, pending ratification):
- pnpm 10.x replaces bun as installer/script-runner (D7)
- Vitest >=4.1 replaces bun test for all tiers via bun:test shim (D8);
  spike: core suite green at 0.64x bun wall-clock on Node 24
- tsx for scripts day one; native type-stripping ratchet as future work (D9)
- three-phase rollout: hygiene -> per-package test flips -> atomic
  toolchain cutover -> release-continuity proof (D15)

* [US-001] chore(open-knowledge): phase-0 import hygiene and suite re-census

Extensionless-import .ts hygiene across app perf fixtures/scenarios, the
load-bearing core/constants/upload.ts re-export, and the core list.test.ts
CJS-require-to-ESM codemod. Adds the phase-0 per-package suite census as
migration evidence for later parity checks. No runtime behavior change.

* [US-002] test(open-knowledge): land vitest infra (shim, facade, shared config) + dual-vocabulary meta-guard

Add vitest 4.1 plus the bun:test compatibility shim, the Bun.* facade, and a
shared vitest base config as test infrastructure (no package flipped yet), and
open the CI meta-guard's dual-vocabulary window so per-package flips become
mechanical script swaps.

- test-support/bun-test-shim.ts: the 14-symbol bun:test surface (hooks, expect,
  describe, test, it, spyOn, mock, jest, vi, Mock, setDefaultTimeout) with a
  vi.doMock-based mock.module facade
- test-support/bun-global-shim.ts: Bun.* facade (file, sleep, which, spawnSync
  via node:child_process, Glob, TOML, gc, resolveSync, CryptoHasher)
- test-support/vitest.base.ts: shared config pinning ssr.resolve.conditions
  ['development'], testTimeout 30000, and the import.meta.dir transform plugin
- vitest.config.ts and self-tests proving bun:test imports resolve, the mock
  facade mocks a dynamic import, and the Bun facade behaves on Node
- scripts/check-ok-ci-test-coverage.mjs: accept both bun --timeout and vitest
  config-based testTimeout; truth-table test extended in lock-step

Deps: vitest and smol-toml (backs the TOML facade). No package test script is
flipped; that is US-004 onward.

* [US-003] docs(open-knowledge): mock.module retro-patch taxonomy

* [US-004] test(open-knowledge): flip core test tiers to vitest + SB-1 resolved-path proof

* [US-005] test(open-knowledge): flip md-conformance test tier to vitest

* [US-006] test(open-knowledge): flip docs test tier to vitest

Flip docs test script (bun test src -> bunx vitest run src) and add
docs/vitest.config.ts spreading the shared base with the docs @->src alias.
15 files / 197 tests, exact parity, zero skipped/deleted. cli flip split to
a follow-up iteration (inventory in the remove-bun mock-taxonomy evidence).

* [SRC-COMPANION] docs(open-knowledge): production-src bun-ism sweep for companion PR

* [SRC-COMPANION] fix(open-knowledge): sha256 doc-name hashing in bundle-redact (Node-compat; was blake2b512+outputLength which throws on Node)

ok diagnose --redact crashed on the published Node CLI with ERR_OSSL_EVP_NOT_XOF_OR_INVALID_LENGTH because createHash('blake2b512', { outputLength: 32 }) rejects the outputLength option on Node's OpenSSL (bun returns a 32-byte digest). The doc-name token is ephemeral per bundle, so switching to sha256 truncated to 8 hex is safe. Rides a companion bug-fix PR, split from the test-runner migration.

* [US-006] test(open-knowledge): flip cli test tiers to vitest

Flip packages/cli off bun test: `test` -> `bun run build:schema && bunx vitest run`, `test:e2e:cli` -> `bunx vitest run --config vitest.e2e.config.ts` (the black-box spec omits the .test infix, so a bare positional collects nothing and a dedicated config pins include). Add vitest.config.ts (base spread) and vitest.e2e.config.ts. Exact census parity: 118 files / 1861 tests, zero skipped or deleted.

Six test-infra fix classes: spyOn on an ESM namespace -> mock.module + dynamic import (open-browser); a bun ?query dynamic-import cache-buster -> vi.resetModules (published-schema); dev-mode argv[1] stubs (pi-extension, project-integration-writers); a real fakeHome default + importActual factory (config/loader module-load crash); a Bun.Transpiler facade backed by node:module stripTypeScriptTypes (pi-extension); async Bun.spawn -> node:child_process spawn (project-anchor) and worker execPath -> explicit bun (keepalive-orphan-reaping emitted parent script).

The two suspected src-mechanism differences were both test-harness readiness races, not src behavior: pi-extension's rejects.toThrow was unawaited and lost to the finally shutdown; start.ts read degraded before awaiting booted.ready, so the async shadow-repo git init ran after PATH was restored. Both fixed by awaiting. No cli/src change (the bundle-redact Node-compat fix rode the separate SRC-COMPANION commit). Extends test-support/bun-global-shim.ts with Bun.Transpiler plus a self-test. cli test bodies are not copybara headDriftCheck anchors, so no manifest change.

* [SRC-COMPANION] fix(open-knowledge): duplicate-path returns 409 on Node when destination is occupied

Node's fs.cpSync throws ERR_FS_CP_DIR_TO_NON_DIR (or ERR_FS_CP_NON_DIR_TO_DIR)
when a directory copy collides with an existing file (or vice versa), where bun
throws EEXIST. isAlreadyExistsError only matched EEXIST / ERR_FS_CP_EEXIST, so on
the published Node server POST /api/duplicate-path returned 500 instead of 409
when the destination path was already taken by an incompatible type. Recognize
all four codes; behavior-neutral for same-type collisions.

* [US-007] test(open-knowledge): flip server test tier to vitest

Flip packages/server test from bun to vitest at exact census parity (316 files /
5098 tests). Add packages/server/vitest.config.ts. Rewrite the 21 Bun.serve test
servers to node:http via a shared fetch-test-server helper, and fix the Node-idiom
failures the flip surfaced: ESM-namespace spyOn on node builtins to mock.module +
dynamic import, require(biome.jsonc) to a JSONC-strip helper, Glob-from-bun to the
Bun.Glob facade, an SDK subpath specifier, import.meta.path and describe.if. Adjust
the collab close-code and handoff Host-header tests for Node runtime behavior. Add
Bun.file().stat() to the facade. build:skill-bundles stays on bun (US-014).

* [US-008] test(open-knowledge): flip app unit/conversion/fidelity tiers to vitest

* [US-009] test(open-knowledge): flip app integration tier (sharded) to vitest

* [US-010] test(open-knowledge): flip app DOM tier to a dedicated jsdom vitest project

* [US-011] test(open-knowledge): flip desktop test tier to vitest

* [US-012] test(open-knowledge): flip scripts and .github test tiers to a vitest .mjs project

* [US-013] chore(open-knowledge): knip vitest-aware, retire bun-test-blind allowlist rows

* [US-014] chore(open-knowledge): tsx script/shebang rewrites ahead of pnpm cutover (bun still installer)

* [US-014] feat(open-knowledge): cut toolchain over to pnpm 10 (workspace, lockfile, supply-chain posture; delete bun.lock/bunfig/.bun-version)

* [US-015] chore: retire bun from root OK integration (wrapper, guards, cross-harness) with D10 hybrid

* [US-016] ci(open-knowledge): swap validation + gate workflows to setup-node + pnpm (retry loop, shards, D12)

* [US-017] ci(open-knowledge): swap release spine to pnpm in-place (release.yml/desktop/promote/main-reset/compute-next-beta), preserve provenance

* [US-018] chore(open-knowledge): repoint public mirror to pnpm (manifest include/strip, overlay docs, regen)

* [US-019] docs(open-knowledge): rewrite governance docs + hooks to pnpm/vitest (CLAUDE/AGENTS/PRECEDENTS, root AGENTS/CI, .claude hooks)

* [US-020] chore(open-knowledge): final bun-artifact sweep, residual token cleanup, migration changeset

* [US-014] fix(open-knowledge): hoist electron-builder native deps for pnpm-isolated packaging (keyring, native-config)

* ci(open-knowledge): correct stale bun references in validation workflow comments

* fix(open-knowledge): resolve @types/mdast via createRequire in md-audit enumerator (pnpm-isolated layout)

The catalog enumerator hardcoded a flat node_modules/@types/mdast path that
pnpm's isolated node_modules does not produce; resolve it through Node module
resolution instead. Same pnpm-isolation class as the catalog-fresh check fix.
Surfaced by the uninterrupted full-gate QA run.

* fix(open-knowledge): resolve remove-bun review-gate findings

Phase-6 local-review fixes for the Bun->pnpm migration:

- CI: restore the dropped space in `pnpm exec turbo run ${{ matrix.task }}`
  (validation.yml) so the 6 gated matrix cells stop hard-failing on
  `turbo runtypecheck` / `runtest:dom` / etc.
- Scripts-vitest gate: exclude measure-scripts-contract + cluster-strict-gate
  tests (both spawn bun, ENOENT on the bun-less Node runner) and include the
  test-support Bun-compat shim self-tests that were orphaned from the gate.
- resolved-path-conditions: plant a toBeDefined so the referential-identity
  fence cannot pass vacuously if a symbol resolves to undefined.
- parcel-watcher asarUnpack guard: resolve deps via createRequire so the walk
  descends pnpm's isolated node_modules instead of guessing nested/hoisted
  paths (which pnpm does not populate). No packaging gap surfaced.
- Copybara: convert the 5 check:full:parallel strip anchors to pnpm form and
  add headDriftCheck so the mirrored script stops referencing privatized
  scripts and future drift fails loud; regenerate .bara.sky.
- Clean remaining stale bun references in migration-touched scripts/comments.

* docs(open-knowledge): fix stale bun run check comment in check-no-major-changeset.sh

No-skimp do-now: completes the operative-path bun-comment sweep for a
mirror-shipped guard script (metric-1 residual).

* revert(open-knowledge): keep check-subtree-cwd.sh hook off the migration diff

Per maintainer request: the Bun->pnpm string update in the shared-body
check-subtree-cwd.sh hook forced a change into the public/agents colocated
copy (check-claude-hook-sync requires body-identity across subtrees). Revert
all 3 copies to origin/main so public/agents is untouched; the OK UI-primitives
reminder keeps its origin/main wording.

* fix(open-knowledge): regenerate markdownlint rule-catalog after main merge

The merge brought in main's markdownlint content-rules feature; its committed
rule-catalog.generated.ts was stale against a fresh regeneration from the merged
schema. Regenerated via generate:lint-catalog (tsx) so core's drift-check passes.

* fix(open-knowledge): resolve post-merge CI gate failures

- Monorepo Structural (headDriftCheck 'anchors exactly once'): remove the
  headDriftCheck:true I added in Phase 6 to the 5 check:full:parallel md-audit/
  conformance strips. Their before-anchors appear in multiple scripts (check,
  check:full:parallel) by design, so they can't be headDriftCheck rules; the
  bun->pnpm anchor conversion stays. Regenerated .bara.sky.
- cap-verdict-staleness gate: the gate hard-failed on bun.lock present in only
  one ref (the migration deleted it). Recognize the Bun->pnpm transition
  (bun.lock gone in HEAD + pnpm-lock.yaml present) and skip the dep major-bump
  arm for it, while still fail-closing on a genuine accidental rm.

* fix(open-knowledge): import prosemirror Plugin/PluginKey via @tiptap/pm/state

frozen-table-headers.ts (from main #2548) imported raw 'prosemirror-state', a
phantom dependency the app never declares. It resolves locally via hoisting but
not in the strict pnpm mirror export, breaking the app build there. Align to the
OK convention (69 other files use @tiptap/pm/state, which re-exports these).

* fix(open-knowledge): dedup @types/node and fix node-pty install flag for pnpm CI

Pin @types/node to ^24.7.0 via a pnpm override so a frozen install resolves
node's ambient ChildProcess and EventEmitter types consistently. The workspace
declared a mix (cli/desktop ^24.7.0, core/md-conformance ^25.6.0); under pnpm's
strict tree a fresh frozen resolution split ChildProcess from its EventEmitter
base, so child.on(...) failed to typecheck in the server src the app checks via
the development condition. It was green under bun's flat dedup but red on CI
test:dom, test, typecheck, and the mirror export.

Also drop the unsupported --config.save=false flag from the node-pty rebuild
step in the validation workflow; pnpm add rejects it with
ERR_PNPM_OPTION_NOT_SUPPORTED.

* fix(open-knowledge): resolve pnpm-strict CI failures (phantom deps, tooling scope)

Five migration-completion fixes for the OK validation matrix, all failures
that bun's flat node_modules hid and pnpm's strict tree surfaces:

- typecheck: the node-pty re-materialize step used a bare `pnpm add` at the
  workspace root, which pnpm refuses (ERR_PNPM_ADDING_TO_ROOT). Add -w.
- lint: scripts/comment-identity.mjs and cluster-strict-gate.mjs import ts-morph
  from the OK root, but ts-morph was declared only in app/server/md-conformance.
  Declare it at the root.
- a11y and playwright: `pnpm exec playwright install` from the root cannot
  resolve the app-scoped @playwright/test bin. Scope the install to the app.
- test: the native-config load probe used a bare workspace specifier the root
  cannot resolve under pnpm strict. Use a relative require.
- desktop-smoke: the desktop electron-vite config transitively imports culori
  (app chrome-tokens plugin into core chrome-resolver); culori was only core's
  dep. Declare it in desktop so the externalized config .mjs resolves it.

The size cell (bundle drift) and preflight-macos are still being investigated.

* fix(open-knowledge): clear lint (knip), mirror-runnable, and structural gates

Three more pnpm-strict migration fixes:

- mirror-runnable: packages/core/src/y-prosemirror-patch.test.ts resolves
  @tiptap/y-tiptap via require.resolve to verify the destructive-delete patch,
  but core did not declare it (only app/server do). Declare it in core.
- lint (knip): declaring ts-morph at the root lets comment-identity.mjs resolve
  it, so ts-morph is now a used and declared dep and its knip ignore is removed.
  The two new resolution-only deps (@tiptap/y-tiptap in core via require.resolve,
  culori in desktop via the electron-vite build config) have no static specifier
  knip can trace, so they move to their workspaces' ignoreDependencies.
- Monorepo Structural Validation: the lockfile regen changed md-conformance's
  resolved @types/node (25.9.5 to 24.13.3, from the new @types/node override),
  so the copybara manifest literal strip anchor drifted. Re-sync the anchor and
  regenerate public-open-knowledge.bara.sky.

* fix(open-knowledge): clear comment-leak, size, and preflight-macos gates

- lint (comment-leak): the @types/node override comment in pnpm-workspace.yaml
  named the private, mirror-excluded md-conformance package, which the export
  leak check flags on the public mirror. Reword to not name any private package.
- size: a fresh pnpm resolution floats several bundled deps to newer patches
  within their existing caret ranges than the bun lockfile had pinned (dep
  declarations unchanged, no duplicate versions). Raise the combined-chunks
  budget 3.17 to 3.3 MB (measured 3.28) and main CSS 54 to 55 kB (measured 54.4);
  diffuse migration drift with no single reclaimable source.
- preflight (macos-latest): git-preflight-boot.test.ts seeds contentDir with a
  bare mkdtemp under /var (a macOS symlink to /private/var). When @parcel/watcher
  falls back to chokidar on CI, chokidar realpaths its root and feeds /private/var
  paths to the ignore filter, so relative(contentDir, ...) yields a ".." string
  the ignore lib rejects. realpath the temp dir, matching the precedent in
  file-watcher.test.ts and file-watcher-allfiles.test.ts.

* fix(open-knowledge): clear a11y/playwright, desktop-smoke, mirror-runnable, AC-1.8

Five more pnpm-migration fixes across the remaining OK validation cells:

- a11y and playwright: the per-worker dev-server fixtures spawned `bun run dev`,
  but the pnpm-migrated runners have no bun (spawn bun ENOENT), so the dev server
  never booted and every test timed out on "did not become ready". Spawn
  `pnpm run dev` instead (drops bun's --silent; NO_COLOR already quiets output).
- desktop-smoke: the runner image sets ELECTRON_SKIP_BINARY_DOWNLOAD, so
  electron's install.js exits 0 without fetching the runtime binary and
  _electron.launch fails; the packaging steps then skip and the floor gate reds
  "no .app bundle". Clear the skip, force the download, and assert path.txt.
- mirror-runnable (core-tests): core passes 2243 tests, but turbo colorizes the
  vitest summary it wraps, so the guard's /Tests N passed/ regex missed the
  ANSI-wrapped line and reported "tests did not actually run". Strip ANSI in the
  leg output before the shape assertions.
- mirror-runnable (core y-prosemirror): the R13 patch test import.meta.resolves
  y-prosemirror@1.3.7 (a patched dep) alongside @tiptap/y-tiptap; core declared
  neither. Declare y-prosemirror in core + knip ignore.
- lint (AC-1.8 perf gate): the comment-discipline perf gate ran under bun's test
  runner (~1.0-1.2s warm); Node plus vitest roughly doubles it (~2.3s on a loaded
  CI runner), exceeding the 2s budget. Raise warm 2->4s and cold 5->7s.

* fix(open-knowledge): pin Playwright + fix tsx PATH and remaining bun dev-spawns

- lint: check-schema-snapshot-clean.sh called a bare `tsx`, which is not on PATH
  in a plain CI shell under pnpm (bun provided a global shim). Use `pnpm exec tsx`.
- playwright (spawn bun ENOENT): two more per-spec dev-server boots in the
  init-load-byte-stable and single-file-ephemeral private e2e specs still spawned
  `bun run dev`; switch to `pnpm run dev` like the shared fixtures.
- playwright (QA-037 spoken-accelerator aria-label): a fresh pnpm resolution
  floated @playwright/test 1.59.1 to 1.61.1, whose newer Chromium no longer
  derives navigator.platform from a spoofed Mac userAgent. @tiptap/core's
  isMacOS() reads navigator.platform, so the toggle aria-labels flipped to the
  windows/linux accelerator form. Pin Playwright to the bun-resolved 1.59.1.

* fix(open-knowledge): harden electron binary install for desktop-smoke

The step-level empty-string ELECTRON_SKIP_BINARY_DOWNLOAD override did not take
effect on the mac runner (install.js still exited without fetching the binary
and no path.txt was written). Use `env -u` to remove the var entirely for the
node child, rm -rf dist so isInstalled() returns false and re-fetches, and add
set -x plus an echo of the var so a residual skip is diagnosable. desktop-smoke
is an advisory cell (not in the required gate needs).

* fix(open-knowledge): fix playwright browser-cache key + QA-037 platform spoof

The a11y and playwright cells derived their Playwright browser-cache key from
`grep -oE 'playwright@X.Y.Z' pnpm-lock.yaml | head -1`, which matched
@axe-core/playwright@4.12.1 before the real @playwright/test entry. The cache
was keyed on 4.12.1, so a stale entry hit exactly, the browser install was
skipped, and the tests could not find the chromium_headless_shell build for the
actual Playwright version. Anchor the grep on @playwright/test.

Also make QA-037 (spoken-accelerator aria-label) robust: @tiptap/core's
isMacOS() reads navigator.platform, which the describe-level Mac userAgent spoof
does not set, so the assertion depended on the bundled Chromium deriving
platform from the UA. Spoof navigator.platform=MacIntel in the test.

Drop the temporary @playwright/test override; the minimumReleaseAge cooldown
already holds it at the bun-resolved 1.59.1, and the cache-key fix is what makes
the correct chromium install.

* ci: run private-pr-validation on Node 24 for the OK format:check leg

The Monorepo Structural Validation job's `pnpm format:check` fan-out includes
format:check:open-knowledge, which runs OK's lint. The Open Knowledge workspace
now pins engines.node >= 24 with engine-strict (bun to pnpm migration), so on
the job's Node 22 OK's pnpm fails loud with "Expected version: >=24", which
trips concurrently --kill-others-on-fail and SIGTERMs every other format:check
leg. Bump the job to Node 24; the structural guards and actionlint are
Node-agnostic and run fine on 24.

* ci: run ok-marketing validation on Node 24 for the OK-core derive

ok-marketing's build/test hooks run vercel-derive-core-dist.sh, which invokes
pnpm inside the OK subtree (a pnpm workspace pinning engines.node >= 24 with
engine-strict, post bun->pnpm migration). The _subtree-validation reusable and
its composite install action both hardcoded Node 22, so the derive failed with
ERR_PNPM_UNSUPPORTED_ENGINE. Thread a node-version input (default 22) through the
reusable and the composite install action, and have the ok-marketing caller pass
24. Other subtree callers keep the Node 22 default unchanged.

* fix(open-knowledge): add setSystemTime to the bun:test shim

Several app suites (e.g. source-editor-navigation pending-intent expiry) import
`setSystemTime` from bun:test to time-travel the wall clock. The vitest shim did
not re-export it, so those tests failed under the migrated runner. Map bun's
clock-only mock to Vitest fake timers scoped to Date (a no-arg call restores the
real clock, matching bun's reset semantics).

* fix(open-knowledge): green the base test cell under Node 24 + pnpm

The `test` matrix cell surfaced four Node-vs-bun / pnpm-vs-bun gaps the
migration exposed. Each is a runtime/toolchain difference, not a code defect.

- Pin .node-version to 24.18.0. `actions/setup-node` resolved bare `24` to
  24.13.0, which has an fs.rmSync regression that follows the final symlink
  and throws ERR_FS_EISDIR on rmSync(symlink-to-dir, {force}). That broke the
  cli init.test cleanup and the server skill-projection/skill-reconcile
  symlink tests (dangling-link removal). 24.18.0 restores correct behavior;
  verified empirically on both versions.
- Add test.if / it.if to the bun:test shim, aliased to Vitest's runIf. The
  server markdownlint discovery/write suites use bun's test.if(cond) and were
  failing to collect with "test.if is not a function".
- crash-detection: assert no NET uncaughtException listener from
  createCrashDetection rather than an absolute count of zero. The Vitest
  worker installs its own handler, so the baseline is nonzero under the
  runner (it was zero under bun's).
- Add node-pty to pnpm onlyBuiltDependencies. node-pty has no Linux prebuild,
  so its install script must run to compile pty.node from source for the
  Node-runtime pty tests; pnpm gates build scripts by default where bun did
  not. No lockfile change (onlyBuiltDependencies is not lockfile-tracked).

* fix(open-knowledge): clear dirty-shutdown sentinel between QA-011 relaunches

desktop-smoke's QA-011/016 was the last red required cell. Its teardown
SIGKILLs app1 via closeAppBounded (it never sends app.quit()), so app1 never
reaches `will-quit` and markCleanQuit() never clears the boot dirty-shutdown
sentinel. app2 then relaunches on the same userData, detectBootCrash() sees the
stale sentinel, and opens a crash-invite dialog that stacks over the
create-project dialog and intercepts the click on create-advanced-trigger. The
click times out and fails all retries, which cascades to the "no .app bundle"
floor gate (build:mac:unsigned is skipped after the Playwright step fails).

The SIGKILL models a clean close, not a crash, so clear the sentinel between
the two launches. This is a pre-existing latent break from #2571; the migration
PR is simply the first CI run to actually exercise desktop-smoke's relaunch path
(it skips on main merges). Test-only, no product-code change.

* fix(open-knowledge): mirror .npmrc so the export hoists @napi-rs/keyring

desktop-smoke builds and packages the materialized public mirror export. The
migration added public/open-knowledge/.npmrc (public-hoist-pattern for
@napi-rs/keyring* + @parcel/watcher, plus D16 engine-strict) but did not add it
to the copybara manifest, so the export lacked it. Without the hoist, the
export's frozen install leaves @napi-rs/keyring-darwin-arm64 under .pnpm/ rather
than the top-level node_modules where prepare-universal.mjs and
electron-builder.yml's extraResources look. prepare-universal.mjs then aborted
with "@napi-rs/keyring-darwin-arm64 not present ... Run bun install first", so
build:mac:unsigned never ran and the "no .app bundle" floor gate failed.

Add .npmrc to the manifest include[] and regenerate the .bara.sky. The public
repo needs it too (its own DMG packaging hoists the same natives). Verified the
materialized export now contains .npmrc and a source install hoists keyring to
node_modules/@napi-rs/keyring-darwin-arm64.

* fixup! local-review: baseline (pre-review state)

* fix(open-knowledge): pin @codemirror to the bun baseline in pnpm overrides

The migration's editor-stack freeze pins TipTap/ProseMirror/React to their
bun-resolved versions in pnpm-workspace.yaml overrides, but @codemirror/state
(6.6.0) and @codemirror/view (6.43.3) lived only in a top-level `overrides`
block in package.json, which pnpm ignores (it reads pnpm.overrides or the
pnpm-workspace.yaml overrides key). So pnpm floated the source-mode editor core
to 6.7.1 / 6.43.6 within the app's carets, off the bun baseline the freeze is
meant to hold.

Move the two CodeMirror pins into pnpm-workspace.yaml alongside the rest of the
editor graph and regenerate the lockfile (now resolves 6.6.0 / 6.43.3), and
remove the now-inert top-level `overrides` block from package.json
(mdast-util-mdx-jsx there was already declared in pnpm-workspace.yaml). App
typecheck passes against the re-pinned versions.

* chore(open-knowledge): refresh THIRD_PARTY_NOTICES for the codemirror pin

Pinning @codemirror/state 6.7.1 to 6.6.0 and @codemirror/view 6.43.6 to
6.43.3 changed the captured license/homepage text, so the drift-checked
THIRD_PARTY_NOTICES.md (check-notices-clean.sh, run in the lint gate) went
stale. Regenerated via `pnpm run notices`.

* chore(open-knowledge): address local-review minors

Stale-bun sweep: PRECEDENTS.md #13(d)/#26/#47 bun-run invocations corrected to
pnpm with post-remove-bun corrigenda; api-extension.ts event-loop and dev-argv
JSDoc comments corrected to Node; check-monorepo-traps.mjs's stale "5+ bun run
references" justification corrected; comment-discipline-budgets.ts bun-run-check
comment corrected to pnpm.

Changeset: note the two shipped behavioral deltas (sha256 redaction tokens, and
ERR_FS_CP_* mapped to 409) instead of claiming zero runtime behavior change.

md-audit discovery.ts: the root scan-dir read now propagates instead of being
swallowed, so a wrong or inaccessible root can no longer make freshness gates
pass vacuously on an empty scan (subdirectory read failures still swallow).

Dedup the four byte-identical read-biome-config.test-helper.ts copies into
test-support/ and repoint the importers.

* test(open-knowledge): harden the bun:test shim (review Major 2 + Bun.Glob)

The bun:test/Bun.* compatibility shim underwrites the whole flipped ~1,650-test
suite, so add direct behavioral coverage and make its silent fallbacks loud.

- Behavioral tests: test.if (run/skip + falsy coercion), Bun.CryptoHasher
  (known-vector digest + update chaining), setSystemTime (Date frozen, real
  setTimeout intact, no-arg reset restores the wall clock), a cross-directory
  mock.module fixture that genuinely exercises callerFile()/absolutize(), and a
  vitest-version-pinned __vitest_mocker__.queueMock canary that trips if that
  internal moves.
- Bun.Glob facade: add the dot:false dotfile skip in scanSync (Minor 1),
  matching real Bun.Glob and md-audit discovery.ts.
- Observable fallbacks: console.warn on the absolutize()/mockBareSpecifier()
  degraded paths; narrow resolveAny's catch to the ESM-resolver-fallback code
  set, re-throwing genuinely-broken package configs instead of swallowing all.
- Header: reclassify the native-import codemod as tracked Future Work with a
  concrete trigger (before the next Vitest major).

* fix(open-knowledge): repoint the last read-biome-config helper importer

The helper dedup missed one importer whose specifier was
`./lint-plugins/read-biome-config.test-helper.ts` (a packages/server/src test
importing from its own lint-plugins subdir at depth 3, not the depth-4 shape the
other importers used), leaving a dangling import after the file moved to
test-support/. knip flagged it in the lint cell and the server test package
failed to load it in the test cell. Repoint it to
../../../test-support/read-biome-config.test-helper.

GitOrigin-RevId: 9f820ec4591684cbb58fa1bbe366237a56313b89
inkeep-oss-sync Bot pushed a commit that referenced this pull request Jul 17, 2026
* spec(open-knowledge): remove Bun, Node as sole execution engine

Full /spec workflow output: problem framing, 7-worker research wave
(toolchain, test-runner, TS-exec scan, public mirror, gate semantics,
CI timings, vitest spike on packages/core), decisions D1-D16, phased
rollout, 26-seam verification plan, and two cold-reader passes
(design review + claim audit) with corrections applied.

Headline decisions (agent-derived, pending ratification):
- pnpm 10.x replaces bun as installer/script-runner (D7)
- Vitest >=4.1 replaces bun test for all tiers via bun:test shim (D8);
  spike: core suite green at 0.64x bun wall-clock on Node 24
- tsx for scripts day one; native type-stripping ratchet as future work (D9)
- three-phase rollout: hygiene -> per-package test flips -> atomic
  toolchain cutover -> release-continuity proof (D15)

* [US-001] chore(open-knowledge): phase-0 import hygiene and suite re-census

Extensionless-import .ts hygiene across app perf fixtures/scenarios, the
load-bearing core/constants/upload.ts re-export, and the core list.test.ts
CJS-require-to-ESM codemod. Adds the phase-0 per-package suite census as
migration evidence for later parity checks. No runtime behavior change.

* [US-002] test(open-knowledge): land vitest infra (shim, facade, shared config) + dual-vocabulary meta-guard

Add vitest 4.1 plus the bun:test compatibility shim, the Bun.* facade, and a
shared vitest base config as test infrastructure (no package flipped yet), and
open the CI meta-guard's dual-vocabulary window so per-package flips become
mechanical script swaps.

- test-support/bun-test-shim.ts: the 14-symbol bun:test surface (hooks, expect,
  describe, test, it, spyOn, mock, jest, vi, Mock, setDefaultTimeout) with a
  vi.doMock-based mock.module facade
- test-support/bun-global-shim.ts: Bun.* facade (file, sleep, which, spawnSync
  via node:child_process, Glob, TOML, gc, resolveSync, CryptoHasher)
- test-support/vitest.base.ts: shared config pinning ssr.resolve.conditions
  ['development'], testTimeout 30000, and the import.meta.dir transform plugin
- vitest.config.ts and self-tests proving bun:test imports resolve, the mock
  facade mocks a dynamic import, and the Bun facade behaves on Node
- scripts/check-ok-ci-test-coverage.mjs: accept both bun --timeout and vitest
  config-based testTimeout; truth-table test extended in lock-step

Deps: vitest and smol-toml (backs the TOML facade). No package test script is
flipped; that is US-004 onward.

* [US-003] docs(open-knowledge): mock.module retro-patch taxonomy

* [US-004] test(open-knowledge): flip core test tiers to vitest + SB-1 resolved-path proof

* [US-005] test(open-knowledge): flip md-conformance test tier to vitest

* [US-006] test(open-knowledge): flip docs test tier to vitest

Flip docs test script (bun test src -> bunx vitest run src) and add
docs/vitest.config.ts spreading the shared base with the docs @->src alias.
15 files / 197 tests, exact parity, zero skipped/deleted. cli flip split to
a follow-up iteration (inventory in the remove-bun mock-taxonomy evidence).

* [SRC-COMPANION] docs(open-knowledge): production-src bun-ism sweep for companion PR

* [SRC-COMPANION] fix(open-knowledge): sha256 doc-name hashing in bundle-redact (Node-compat; was blake2b512+outputLength which throws on Node)

ok diagnose --redact crashed on the published Node CLI with ERR_OSSL_EVP_NOT_XOF_OR_INVALID_LENGTH because createHash('blake2b512', { outputLength: 32 }) rejects the outputLength option on Node's OpenSSL (bun returns a 32-byte digest). The doc-name token is ephemeral per bundle, so switching to sha256 truncated to 8 hex is safe. Rides a companion bug-fix PR, split from the test-runner migration.

* [US-006] test(open-knowledge): flip cli test tiers to vitest

Flip packages/cli off bun test: `test` -> `bun run build:schema && bunx vitest run`, `test:e2e:cli` -> `bunx vitest run --config vitest.e2e.config.ts` (the black-box spec omits the .test infix, so a bare positional collects nothing and a dedicated config pins include). Add vitest.config.ts (base spread) and vitest.e2e.config.ts. Exact census parity: 118 files / 1861 tests, zero skipped or deleted.

Six test-infra fix classes: spyOn on an ESM namespace -> mock.module + dynamic import (open-browser); a bun ?query dynamic-import cache-buster -> vi.resetModules (published-schema); dev-mode argv[1] stubs (pi-extension, project-integration-writers); a real fakeHome default + importActual factory (config/loader module-load crash); a Bun.Transpiler facade backed by node:module stripTypeScriptTypes (pi-extension); async Bun.spawn -> node:child_process spawn (project-anchor) and worker execPath -> explicit bun (keepalive-orphan-reaping emitted parent script).

The two suspected src-mechanism differences were both test-harness readiness races, not src behavior: pi-extension's rejects.toThrow was unawaited and lost to the finally shutdown; start.ts read degraded before awaiting booted.ready, so the async shadow-repo git init ran after PATH was restored. Both fixed by awaiting. No cli/src change (the bundle-redact Node-compat fix rode the separate SRC-COMPANION commit). Extends test-support/bun-global-shim.ts with Bun.Transpiler plus a self-test. cli test bodies are not copybara headDriftCheck anchors, so no manifest change.

* [SRC-COMPANION] fix(open-knowledge): duplicate-path returns 409 on Node when destination is occupied

Node's fs.cpSync throws ERR_FS_CP_DIR_TO_NON_DIR (or ERR_FS_CP_NON_DIR_TO_DIR)
when a directory copy collides with an existing file (or vice versa), where bun
throws EEXIST. isAlreadyExistsError only matched EEXIST / ERR_FS_CP_EEXIST, so on
the published Node server POST /api/duplicate-path returned 500 instead of 409
when the destination path was already taken by an incompatible type. Recognize
all four codes; behavior-neutral for same-type collisions.

* [US-007] test(open-knowledge): flip server test tier to vitest

Flip packages/server test from bun to vitest at exact census parity (316 files /
5098 tests). Add packages/server/vitest.config.ts. Rewrite the 21 Bun.serve test
servers to node:http via a shared fetch-test-server helper, and fix the Node-idiom
failures the flip surfaced: ESM-namespace spyOn on node builtins to mock.module +
dynamic import, require(biome.jsonc) to a JSONC-strip helper, Glob-from-bun to the
Bun.Glob facade, an SDK subpath specifier, import.meta.path and describe.if. Adjust
the collab close-code and handoff Host-header tests for Node runtime behavior. Add
Bun.file().stat() to the facade. build:skill-bundles stays on bun (US-014).

* [US-008] test(open-knowledge): flip app unit/conversion/fidelity tiers to vitest

* [US-009] test(open-knowledge): flip app integration tier (sharded) to vitest

* [US-010] test(open-knowledge): flip app DOM tier to a dedicated jsdom vitest project

* [US-011] test(open-knowledge): flip desktop test tier to vitest

* [US-012] test(open-knowledge): flip scripts and .github test tiers to a vitest .mjs project

* [US-013] chore(open-knowledge): knip vitest-aware, retire bun-test-blind allowlist rows

* [US-014] chore(open-knowledge): tsx script/shebang rewrites ahead of pnpm cutover (bun still installer)

* [US-014] feat(open-knowledge): cut toolchain over to pnpm 10 (workspace, lockfile, supply-chain posture; delete bun.lock/bunfig/.bun-version)

* [US-015] chore: retire bun from root OK integration (wrapper, guards, cross-harness) with D10 hybrid

* [US-016] ci(open-knowledge): swap validation + gate workflows to setup-node + pnpm (retry loop, shards, D12)

* [US-017] ci(open-knowledge): swap release spine to pnpm in-place (release.yml/desktop/promote/main-reset/compute-next-beta), preserve provenance

* [US-018] chore(open-knowledge): repoint public mirror to pnpm (manifest include/strip, overlay docs, regen)

* [US-019] docs(open-knowledge): rewrite governance docs + hooks to pnpm/vitest (CLAUDE/AGENTS/PRECEDENTS, root AGENTS/CI, .claude hooks)

* [US-020] chore(open-knowledge): final bun-artifact sweep, residual token cleanup, migration changeset

* [US-014] fix(open-knowledge): hoist electron-builder native deps for pnpm-isolated packaging (keyring, native-config)

* ci(open-knowledge): correct stale bun references in validation workflow comments

* fix(open-knowledge): resolve @types/mdast via createRequire in md-audit enumerator (pnpm-isolated layout)

The catalog enumerator hardcoded a flat node_modules/@types/mdast path that
pnpm's isolated node_modules does not produce; resolve it through Node module
resolution instead. Same pnpm-isolation class as the catalog-fresh check fix.
Surfaced by the uninterrupted full-gate QA run.

* fix(open-knowledge): resolve remove-bun review-gate findings

Phase-6 local-review fixes for the Bun->pnpm migration:

- CI: restore the dropped space in `pnpm exec turbo run ${{ matrix.task }}`
  (validation.yml) so the 6 gated matrix cells stop hard-failing on
  `turbo runtypecheck` / `runtest:dom` / etc.
- Scripts-vitest gate: exclude measure-scripts-contract + cluster-strict-gate
  tests (both spawn bun, ENOENT on the bun-less Node runner) and include the
  test-support Bun-compat shim self-tests that were orphaned from the gate.
- resolved-path-conditions: plant a toBeDefined so the referential-identity
  fence cannot pass vacuously if a symbol resolves to undefined.
- parcel-watcher asarUnpack guard: resolve deps via createRequire so the walk
  descends pnpm's isolated node_modules instead of guessing nested/hoisted
  paths (which pnpm does not populate). No packaging gap surfaced.
- Copybara: convert the 5 check:full:parallel strip anchors to pnpm form and
  add headDriftCheck so the mirrored script stops referencing privatized
  scripts and future drift fails loud; regenerate .bara.sky.
- Clean remaining stale bun references in migration-touched scripts/comments.

* docs(open-knowledge): fix stale bun run check comment in check-no-major-changeset.sh

No-skimp do-now: completes the operative-path bun-comment sweep for a
mirror-shipped guard script (metric-1 residual).

* revert(open-knowledge): keep check-subtree-cwd.sh hook off the migration diff

Per maintainer request: the Bun->pnpm string update in the shared-body
check-subtree-cwd.sh hook forced a change into the public/agents colocated
copy (check-claude-hook-sync requires body-identity across subtrees). Revert
all 3 copies to origin/main so public/agents is untouched; the OK UI-primitives
reminder keeps its origin/main wording.

* fix(open-knowledge): regenerate markdownlint rule-catalog after main merge

The merge brought in main's markdownlint content-rules feature; its committed
rule-catalog.generated.ts was stale against a fresh regeneration from the merged
schema. Regenerated via generate:lint-catalog (tsx) so core's drift-check passes.

* fix(open-knowledge): resolve post-merge CI gate failures

- Monorepo Structural (headDriftCheck 'anchors exactly once'): remove the
  headDriftCheck:true I added in Phase 6 to the 5 check:full:parallel md-audit/
  conformance strips. Their before-anchors appear in multiple scripts (check,
  check:full:parallel) by design, so they can't be headDriftCheck rules; the
  bun->pnpm anchor conversion stays. Regenerated .bara.sky.
- cap-verdict-staleness gate: the gate hard-failed on bun.lock present in only
  one ref (the migration deleted it). Recognize the Bun->pnpm transition
  (bun.lock gone in HEAD + pnpm-lock.yaml present) and skip the dep major-bump
  arm for it, while still fail-closing on a genuine accidental rm.

* fix(open-knowledge): import prosemirror Plugin/PluginKey via @tiptap/pm/state

frozen-table-headers.ts (from main #2548) imported raw 'prosemirror-state', a
phantom dependency the app never declares. It resolves locally via hoisting but
not in the strict pnpm mirror export, breaking the app build there. Align to the
OK convention (69 other files use @tiptap/pm/state, which re-exports these).

* fix(open-knowledge): dedup @types/node and fix node-pty install flag for pnpm CI

Pin @types/node to ^24.7.0 via a pnpm override so a frozen install resolves
node's ambient ChildProcess and EventEmitter types consistently. The workspace
declared a mix (cli/desktop ^24.7.0, core/md-conformance ^25.6.0); under pnpm's
strict tree a fresh frozen resolution split ChildProcess from its EventEmitter
base, so child.on(...) failed to typecheck in the server src the app checks via
the development condition. It was green under bun's flat dedup but red on CI
test:dom, test, typecheck, and the mirror export.

Also drop the unsupported --config.save=false flag from the node-pty rebuild
step in the validation workflow; pnpm add rejects it with
ERR_PNPM_OPTION_NOT_SUPPORTED.

* fix(open-knowledge): resolve pnpm-strict CI failures (phantom deps, tooling scope)

Five migration-completion fixes for the OK validation matrix, all failures
that bun's flat node_modules hid and pnpm's strict tree surfaces:

- typecheck: the node-pty re-materialize step used a bare `pnpm add` at the
  workspace root, which pnpm refuses (ERR_PNPM_ADDING_TO_ROOT). Add -w.
- lint: scripts/comment-identity.mjs and cluster-strict-gate.mjs import ts-morph
  from the OK root, but ts-morph was declared only in app/server/md-conformance.
  Declare it at the root.
- a11y and playwright: `pnpm exec playwright install` from the root cannot
  resolve the app-scoped @playwright/test bin. Scope the install to the app.
- test: the native-config load probe used a bare workspace specifier the root
  cannot resolve under pnpm strict. Use a relative require.
- desktop-smoke: the desktop electron-vite config transitively imports culori
  (app chrome-tokens plugin into core chrome-resolver); culori was only core's
  dep. Declare it in desktop so the externalized config .mjs resolves it.

The size cell (bundle drift) and preflight-macos are still being investigated.

* fix(open-knowledge): clear lint (knip), mirror-runnable, and structural gates

Three more pnpm-strict migration fixes:

- mirror-runnable: packages/core/src/y-prosemirror-patch.test.ts resolves
  @tiptap/y-tiptap via require.resolve to verify the destructive-delete patch,
  but core did not declare it (only app/server do). Declare it in core.
- lint (knip): declaring ts-morph at the root lets comment-identity.mjs resolve
  it, so ts-morph is now a used and declared dep and its knip ignore is removed.
  The two new resolution-only deps (@tiptap/y-tiptap in core via require.resolve,
  culori in desktop via the electron-vite build config) have no static specifier
  knip can trace, so they move to their workspaces' ignoreDependencies.
- Monorepo Structural Validation: the lockfile regen changed md-conformance's
  resolved @types/node (25.9.5 to 24.13.3, from the new @types/node override),
  so the copybara manifest literal strip anchor drifted. Re-sync the anchor and
  regenerate public-open-knowledge.bara.sky.

* fix(open-knowledge): clear comment-leak, size, and preflight-macos gates

- lint (comment-leak): the @types/node override comment in pnpm-workspace.yaml
  named the private, mirror-excluded md-conformance package, which the export
  leak check flags on the public mirror. Reword to not name any private package.
- size: a fresh pnpm resolution floats several bundled deps to newer patches
  within their existing caret ranges than the bun lockfile had pinned (dep
  declarations unchanged, no duplicate versions). Raise the combined-chunks
  budget 3.17 to 3.3 MB (measured 3.28) and main CSS 54 to 55 kB (measured 54.4);
  diffuse migration drift with no single reclaimable source.
- preflight (macos-latest): git-preflight-boot.test.ts seeds contentDir with a
  bare mkdtemp under /var (a macOS symlink to /private/var). When @parcel/watcher
  falls back to chokidar on CI, chokidar realpaths its root and feeds /private/var
  paths to the ignore filter, so relative(contentDir, ...) yields a ".." string
  the ignore lib rejects. realpath the temp dir, matching the precedent in
  file-watcher.test.ts and file-watcher-allfiles.test.ts.

* fix(open-knowledge): clear a11y/playwright, desktop-smoke, mirror-runnable, AC-1.8

Five more pnpm-migration fixes across the remaining OK validation cells:

- a11y and playwright: the per-worker dev-server fixtures spawned `bun run dev`,
  but the pnpm-migrated runners have no bun (spawn bun ENOENT), so the dev server
  never booted and every test timed out on "did not become ready". Spawn
  `pnpm run dev` instead (drops bun's --silent; NO_COLOR already quiets output).
- desktop-smoke: the runner image sets ELECTRON_SKIP_BINARY_DOWNLOAD, so
  electron's install.js exits 0 without fetching the runtime binary and
  _electron.launch fails; the packaging steps then skip and the floor gate reds
  "no .app bundle". Clear the skip, force the download, and assert path.txt.
- mirror-runnable (core-tests): core passes 2243 tests, but turbo colorizes the
  vitest summary it wraps, so the guard's /Tests N passed/ regex missed the
  ANSI-wrapped line and reported "tests did not actually run". Strip ANSI in the
  leg output before the shape assertions.
- mirror-runnable (core y-prosemirror): the R13 patch test import.meta.resolves
  y-prosemirror@1.3.7 (a patched dep) alongside @tiptap/y-tiptap; core declared
  neither. Declare y-prosemirror in core + knip ignore.
- lint (AC-1.8 perf gate): the comment-discipline perf gate ran under bun's test
  runner (~1.0-1.2s warm); Node plus vitest roughly doubles it (~2.3s on a loaded
  CI runner), exceeding the 2s budget. Raise warm 2->4s and cold 5->7s.

* fix(open-knowledge): pin Playwright + fix tsx PATH and remaining bun dev-spawns

- lint: check-schema-snapshot-clean.sh called a bare `tsx`, which is not on PATH
  in a plain CI shell under pnpm (bun provided a global shim). Use `pnpm exec tsx`.
- playwright (spawn bun ENOENT): two more per-spec dev-server boots in the
  init-load-byte-stable and single-file-ephemeral private e2e specs still spawned
  `bun run dev`; switch to `pnpm run dev` like the shared fixtures.
- playwright (QA-037 spoken-accelerator aria-label): a fresh pnpm resolution
  floated @playwright/test 1.59.1 to 1.61.1, whose newer Chromium no longer
  derives navigator.platform from a spoofed Mac userAgent. @tiptap/core's
  isMacOS() reads navigator.platform, so the toggle aria-labels flipped to the
  windows/linux accelerator form. Pin Playwright to the bun-resolved 1.59.1.

* fix(open-knowledge): harden electron binary install for desktop-smoke

The step-level empty-string ELECTRON_SKIP_BINARY_DOWNLOAD override did not take
effect on the mac runner (install.js still exited without fetching the binary
and no path.txt was written). Use `env -u` to remove the var entirely for the
node child, rm -rf dist so isInstalled() returns false and re-fetches, and add
set -x plus an echo of the var so a residual skip is diagnosable. desktop-smoke
is an advisory cell (not in the required gate needs).

* fix(open-knowledge): fix playwright browser-cache key + QA-037 platform spoof

The a11y and playwright cells derived their Playwright browser-cache key from
`grep -oE 'playwright@X.Y.Z' pnpm-lock.yaml | head -1`, which matched
@axe-core/playwright@4.12.1 before the real @playwright/test entry. The cache
was keyed on 4.12.1, so a stale entry hit exactly, the browser install was
skipped, and the tests could not find the chromium_headless_shell build for the
actual Playwright version. Anchor the grep on @playwright/test.

Also make QA-037 (spoken-accelerator aria-label) robust: @tiptap/core's
isMacOS() reads navigator.platform, which the describe-level Mac userAgent spoof
does not set, so the assertion depended on the bundled Chromium deriving
platform from the UA. Spoof navigator.platform=MacIntel in the test.

Drop the temporary @playwright/test override; the minimumReleaseAge cooldown
already holds it at the bun-resolved 1.59.1, and the cache-key fix is what makes
the correct chromium install.

* ci: run private-pr-validation on Node 24 for the OK format:check leg

The Monorepo Structural Validation job's `pnpm format:check` fan-out includes
format:check:open-knowledge, which runs OK's lint. The Open Knowledge workspace
now pins engines.node >= 24 with engine-strict (bun to pnpm migration), so on
the job's Node 22 OK's pnpm fails loud with "Expected version: >=24", which
trips concurrently --kill-others-on-fail and SIGTERMs every other format:check
leg. Bump the job to Node 24; the structural guards and actionlint are
Node-agnostic and run fine on 24.

* ci: run ok-marketing validation on Node 24 for the OK-core derive

ok-marketing's build/test hooks run vercel-derive-core-dist.sh, which invokes
pnpm inside the OK subtree (a pnpm workspace pinning engines.node >= 24 with
engine-strict, post bun->pnpm migration). The _subtree-validation reusable and
its composite install action both hardcoded Node 22, so the derive failed with
ERR_PNPM_UNSUPPORTED_ENGINE. Thread a node-version input (default 22) through the
reusable and the composite install action, and have the ok-marketing caller pass
24. Other subtree callers keep the Node 22 default unchanged.

* fix(open-knowledge): add setSystemTime to the bun:test shim

Several app suites (e.g. source-editor-navigation pending-intent expiry) import
`setSystemTime` from bun:test to time-travel the wall clock. The vitest shim did
not re-export it, so those tests failed under the migrated runner. Map bun's
clock-only mock to Vitest fake timers scoped to Date (a no-arg call restores the
real clock, matching bun's reset semantics).

* fix(open-knowledge): green the base test cell under Node 24 + pnpm

The `test` matrix cell surfaced four Node-vs-bun / pnpm-vs-bun gaps the
migration exposed. Each is a runtime/toolchain difference, not a code defect.

- Pin .node-version to 24.18.0. `actions/setup-node` resolved bare `24` to
  24.13.0, which has an fs.rmSync regression that follows the final symlink
  and throws ERR_FS_EISDIR on rmSync(symlink-to-dir, {force}). That broke the
  cli init.test cleanup and the server skill-projection/skill-reconcile
  symlink tests (dangling-link removal). 24.18.0 restores correct behavior;
  verified empirically on both versions.
- Add test.if / it.if to the bun:test shim, aliased to Vitest's runIf. The
  server markdownlint discovery/write suites use bun's test.if(cond) and were
  failing to collect with "test.if is not a function".
- crash-detection: assert no NET uncaughtException listener from
  createCrashDetection rather than an absolute count of zero. The Vitest
  worker installs its own handler, so the baseline is nonzero under the
  runner (it was zero under bun's).
- Add node-pty to pnpm onlyBuiltDependencies. node-pty has no Linux prebuild,
  so its install script must run to compile pty.node from source for the
  Node-runtime pty tests; pnpm gates build scripts by default where bun did
  not. No lockfile change (onlyBuiltDependencies is not lockfile-tracked).

* fix(open-knowledge): clear dirty-shutdown sentinel between QA-011 relaunches

desktop-smoke's QA-011/016 was the last red required cell. Its teardown
SIGKILLs app1 via closeAppBounded (it never sends app.quit()), so app1 never
reaches `will-quit` and markCleanQuit() never clears the boot dirty-shutdown
sentinel. app2 then relaunches on the same userData, detectBootCrash() sees the
stale sentinel, and opens a crash-invite dialog that stacks over the
create-project dialog and intercepts the click on create-advanced-trigger. The
click times out and fails all retries, which cascades to the "no .app bundle"
floor gate (build:mac:unsigned is skipped after the Playwright step fails).

The SIGKILL models a clean close, not a crash, so clear the sentinel between
the two launches. This is a pre-existing latent break from #2571; the migration
PR is simply the first CI run to actually exercise desktop-smoke's relaunch path
(it skips on main merges). Test-only, no product-code change.

* fix(open-knowledge): mirror .npmrc so the export hoists @napi-rs/keyring

desktop-smoke builds and packages the materialized public mirror export. The
migration added public/open-knowledge/.npmrc (public-hoist-pattern for
@napi-rs/keyring* + @parcel/watcher, plus D16 engine-strict) but did not add it
to the copybara manifest, so the export lacked it. Without the hoist, the
export's frozen install leaves @napi-rs/keyring-darwin-arm64 under .pnpm/ rather
than the top-level node_modules where prepare-universal.mjs and
electron-builder.yml's extraResources look. prepare-universal.mjs then aborted
with "@napi-rs/keyring-darwin-arm64 not present ... Run bun install first", so
build:mac:unsigned never ran and the "no .app bundle" floor gate failed.

Add .npmrc to the manifest include[] and regenerate the .bara.sky. The public
repo needs it too (its own DMG packaging hoists the same natives). Verified the
materialized export now contains .npmrc and a source install hoists keyring to
node_modules/@napi-rs/keyring-darwin-arm64.

* fixup! local-review: baseline (pre-review state)

* fix(open-knowledge): pin @codemirror to the bun baseline in pnpm overrides

The migration's editor-stack freeze pins TipTap/ProseMirror/React to their
bun-resolved versions in pnpm-workspace.yaml overrides, but @codemirror/state
(6.6.0) and @codemirror/view (6.43.3) lived only in a top-level `overrides`
block in package.json, which pnpm ignores (it reads pnpm.overrides or the
pnpm-workspace.yaml overrides key). So pnpm floated the source-mode editor core
to 6.7.1 / 6.43.6 within the app's carets, off the bun baseline the freeze is
meant to hold.

Move the two CodeMirror pins into pnpm-workspace.yaml alongside the rest of the
editor graph and regenerate the lockfile (now resolves 6.6.0 / 6.43.3), and
remove the now-inert top-level `overrides` block from package.json
(mdast-util-mdx-jsx there was already declared in pnpm-workspace.yaml). App
typecheck passes against the re-pinned versions.

* chore(open-knowledge): refresh THIRD_PARTY_NOTICES for the codemirror pin

Pinning @codemirror/state 6.7.1 to 6.6.0 and @codemirror/view 6.43.6 to
6.43.3 changed the captured license/homepage text, so the drift-checked
THIRD_PARTY_NOTICES.md (check-notices-clean.sh, run in the lint gate) went
stale. Regenerated via `pnpm run notices`.

* chore(open-knowledge): address local-review minors

Stale-bun sweep: PRECEDENTS.md #13(d)/#26/#47 bun-run invocations corrected to
pnpm with post-remove-bun corrigenda; api-extension.ts event-loop and dev-argv
JSDoc comments corrected to Node; check-monorepo-traps.mjs's stale "5+ bun run
references" justification corrected; comment-discipline-budgets.ts bun-run-check
comment corrected to pnpm.

Changeset: note the two shipped behavioral deltas (sha256 redaction tokens, and
ERR_FS_CP_* mapped to 409) instead of claiming zero runtime behavior change.

md-audit discovery.ts: the root scan-dir read now propagates instead of being
swallowed, so a wrong or inaccessible root can no longer make freshness gates
pass vacuously on an empty scan (subdirectory read failures still swallow).

Dedup the four byte-identical read-biome-config.test-helper.ts copies into
test-support/ and repoint the importers.

* test(open-knowledge): harden the bun:test shim (review Major 2 + Bun.Glob)

The bun:test/Bun.* compatibility shim underwrites the whole flipped ~1,650-test
suite, so add direct behavioral coverage and make its silent fallbacks loud.

- Behavioral tests: test.if (run/skip + falsy coercion), Bun.CryptoHasher
  (known-vector digest + update chaining), setSystemTime (Date frozen, real
  setTimeout intact, no-arg reset restores the wall clock), a cross-directory
  mock.module fixture that genuinely exercises callerFile()/absolutize(), and a
  vitest-version-pinned __vitest_mocker__.queueMock canary that trips if that
  internal moves.
- Bun.Glob facade: add the dot:false dotfile skip in scanSync (Minor 1),
  matching real Bun.Glob and md-audit discovery.ts.
- Observable fallbacks: console.warn on the absolutize()/mockBareSpecifier()
  degraded paths; narrow resolveAny's catch to the ESM-resolver-fallback code
  set, re-throwing genuinely-broken package configs instead of swallowing all.
- Header: reclassify the native-import codemod as tracked Future Work with a
  concrete trigger (before the next Vitest major).

* fix(open-knowledge): repoint the last read-biome-config helper importer

The helper dedup missed one importer whose specifier was
`./lint-plugins/read-biome-config.test-helper.ts` (a packages/server/src test
importing from its own lint-plugins subdir at depth 3, not the depth-4 shape the
other importers used), leaving a dangling import after the file moved to
test-support/. knip flagged it in the lint cell and the server test package
failed to load it in the test cell. Repoint it to
../../../test-support/read-biome-config.test-helper.

GitOrigin-RevId: 9f820ec4591684cbb58fa1bbe366237a56313b89
@robert-inkeep
robert-inkeep deleted the update-contributing branch July 22, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant