mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-02 19:43:10 +03:00
chore(deps): bump golang from 1.26-bookworm to 1.27-bookworm in /Server in the docker-dependencies group across 1 directory (#1427)
* ci(deps): bump anthropics/claude-code-action (#1404) Bumps the actions-dependencies group with 1 update: [anthropics/claude-code-action](https://github.com/anthropics/claude-code-action). Updates `anthropics/claude-code-action` from 1.0.193 to 1.0.199 - [Release notes](https://github.com/anthropics/claude-code-action/releases) - [Commits](https://github.com/anthropics/claude-code-action/compare/9d7150bc8a3dae8149739a88019d192b579ad90c...dcb57747bfceeaa1fa72638cae52295d1d853d4a) --- updated-dependencies: - dependency-name: anthropics/claude-code-action dependency-version: 1.0.199 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): pin rfd to tauri-plugin-dialog's major to unblock the cargo group (#1406) The cargo-dependencies group PR (#1405) fails Rust Unit Tests on Linux: error: failed to run custom build command for `rfd v0.17.2` You need to choose at least one backend: `gtk3` or `xdg-portal` features for x86_64-linux rfd is not really ours. It arrives in the tree via tauri-plugin-dialog, which pins ^0.16; we declare it directly only for the fatal-startup message box in lib.rs, where the Tauri app never finished building and the plugin has no AppHandle to run a dialog through. Cargo unifies features only within a semver-compatible version group, so while both wanted ^0.16 there was a single rfd in the graph and the plugin's backend features covered our `default-features = false` declaration too. Bumping our direct dep to 0.17 forks rfd into two crates: the plugin keeps 0.16.0 with its features, ours resolves to 0.17.2 with none, and rfd 0.17 added a build.rs assertion that aborts the Linux build when no backend feature is set. Confirmed in the PR's lockfile, which carries both 0.16.0 and 0.17.2. Adding a Linux backend feature would be the wrong fix: it would paper over the fork and still build rfd twice on every platform for one error dialog. Our version has to track the plugin's instead, so ignore semver-minor rfd updates (0.16 -> 0.17 for a 0.x crate) until tauri-plugin-dialog moves. Patch updates inside 0.16.x still flow. The remaining five crates in the group are unaffected; `windows` in fact consolidates 3 versions down to 2. Cargo.toml is comment-only here - no dependency, feature, or lockfile change - so the build is untouched. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(deps): bump log (#1407) Bumps the cargo-dependencies group with 1 update in the /Client/tauri-client/src-tauri directory: [log](https://github.com/rust-lang/log). Updates `log` from 0.4.33 to 0.4.34 - [Release notes](https://github.com/rust-lang/log/releases) - [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/log/compare/0.4.33...0.4.34) --- updated-dependencies: - dependency-name: log dependency-version: 0.4.34 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * ci(deps): bump anthropics/claude-code-action (#1408) Bumps the actions-dependencies group with 1 update: [anthropics/claude-code-action](https://github.com/anthropics/claude-code-action). Updates `anthropics/claude-code-action` from 1.0.199 to 1.0.200 - [Release notes](https://github.com/anthropics/claude-code-action/releases) - [Commits](https://github.com/anthropics/claude-code-action/compare/dcb57747bfceeaa1fa72638cae52295d1d853d4a...24dcd50c0568f0fc9e9211213a4fd2d9eb15c4e0) --- updated-dependencies: - dependency-name: anthropics/claude-code-action dependency-version: 1.0.200 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * release: v1.2.0-alpha.4 — 62 fixes plus the B0/B1 repository foundation (#1426) * Fix 27 findings from 2026-08-21 bug hunt (#1400) * chore(findings): record 2026-08-21 bug hunt (38 findings) * fix(api): 1 defect(s) (OC-0240) * fix(client): 1 defect(s) (OC-0241) * fix(plugin): 2 defect(s) (OC-0243, OC-0265) * fix(client): 3 defect(s) (OC-0244, OC-0256, OC-0259) * fix(client): 1 defect(s) (OC-0247) * fix(client): 2 defect(s) (OC-0248, OC-0258) * fix(identity): 1 defect(s) (OC-0250) * fix(ws): 3 defect(s) (OC-0252, OC-0269, OC-0272) * fix(admin): 1 defect(s) (OC-0253) * fix(client): 1 defect(s) (OC-0254) * fix(voice): 1 defect(s) (OC-0255) * fix(ws): 1 defect(s) (OC-0260) * fix(client): 1 defect(s) (OC-0261) * fix(client): 1 defect(s) (OC-0262) * fix(client): 1 defect(s) (OC-0263) * fix(client): 1 defect(s) (OC-0264) * fix(client): 1 defect(s) (OC-0268) * fix(ws): 1 defect(s) (OC-0273) * fix(service): 1 defect(s) (OC-0275) * style: satisfy golangci-lint and prettier on 2026-08-21 fix commits - drop ineffectual backupDir reset before return (registry.go, OC-0265) - reflow long boolean expression (attachments.ts, OC-0241) * fix(client): 4 defect(s) (OC-0242, OC-0246, OC-0249, OC-0251) * fix(voice): 1 defect(s) (OC-0267) * fix(admin): 1 defect(s) (OC-0274) * fix(voice): 1 defect(s) (OC-0245) * fix(ws): 1 defect(s) (OC-0271) * fix(voice): 2 defect(s) (OC-0239, OC-0257) * fix(ws): 1 defect(s) (OC-0266) * fix(voice): 1 defect(s) (OC-0270) * style: clear golangci-lint modernize and prettier nits from 2026-08-21 fixes - range-over-int and slices.Contains modernizations in new Go test files - prettier reflow in dispatcher.ts * chore(findings): mark 2026-08-21 hunt findings fixed/declined 37 fixed across the fix waves, OC-0238 declined (LiveKit webhook TLS requires a product decision, not a mechanical patch). --------- Co-authored-by: Claude <noreply@anthropic.com> * fix: 35 findings from the 2026-08-22 bug hunt (#1402) * fix(voice): 1 defect(s) (OC-0277) * fix(voice): 1 defect(s) (OC-0278) * fix(client): 1 defect(s) (OC-0280) refreshDmSidebar() rebuilds the entire DM sidebar subtree on every dmStore.channels change - which includes presence flips and new messages, not just DM list changes. The "Find a conversation" filter text and input focus live only in that destroyed subtree, so they were silently wiped mid-typing. Capture and restore both across the destroy+recreate cycle. * fix(ws): 1 defect(s) (OC-0285) * fix(client): 1 defect(s) (OC-0286) * fix(client): 1 defect(s) (OC-0288) Consume the legacy unscoped mute key after migrating it onto the first host, so a brand-new host with no scoped key of its own no longer reads through to the same legacy list and inherits another server's mutes. * fix(voice): 1 defect(s) (OC-0290) * fix(db): 1 defect(s) (OC-0293) DecrementMentionCounts reversed mention_count bumps that were never applied: message_mentions stores every resolved mention id including the author's blockers, while applyMentionCounts excludes blockers before incrementing. Deleting a blocked author's message therefore wiped an unrelated, genuine mention badge on the same read_states row. Mirror the block exclusion in the decrement UPDATE. * fix(db): 1 defect(s) (OC-0294) DeleteAccount soft-deletes the departing user's messages but never reversed the read_states.mention_count bumps those messages made, leaving phantom mention badges. Reverse them inline in the existing transaction, mirroring DecrementMentionCounts' guards. * fix(client): 1 defect(s) (OC-0295) MemberList rebuilt every row on any non-presence-only membersStore change and on every roles_update, but registered each row's click/contextmenu listeners on the component-lifetime disposable.signal, which only aborts at destroy(). Discarded rows therefore stayed reachable (and their listeners live) for the component's whole lifetime. Route per-row listeners through a per-render AbortController that is aborted and replaced at the top of every render, and aborted again in destroy(). * fix(identity): 1 defect(s) (OC-0297) UpdateProfile's post-commit re-read of the user row could fail for reasons unrelated to context cancellation (SQLITE_BUSY, I/O error, pool exhaustion) and was reported as ErrInternal even though UpdateUserProfile had already committed. Callers that treat any UpdateProfile error as proof the write never landed — handleUploadAvatar deletes the file it just stored — would delete a file the committed avatar column now points at, permanently breaking the avatar with no user_update broadcast. Since UpdateUserProfile only writes username/avatar/display_name/about, merge those four onto the pre-write snapshot to reconstruct the committed row without needing the re-read to succeed, and log the read failure. * fix(ws): 2 defect(s) (OC-0298, OC-0299) - OC-0298: applyConnectStatus stamped c.user.Status even when the UpdateUserStatus write failed, so auth_ok and the presence broadcast claimed a status users.status disagreed with, and buildReady's ListMembers read never self-corrected for the session. - OC-0299: refreshUserSnapshot silently fell back to roleName "member" when the new role lookup failed, pinning the session to a fabricated role on the wire. It now fails closed like the sibling lookups in upgradeAndAuth and handleFreshConnect. * fix(client): 1 defect(s) (OC-0300) * fix(client): 1 defect(s) (OC-0301) * fix(ws): 1 defect(s) (OC-0302) * fix(api): 1 defect(s) (OC-0305) handleDiagnosticsConnectivity used clientIP(r), ignoring cfg.Server.TrustedProxies, so behind a configured trusted reverse proxy the endpoint reported the proxy hop instead of the real client address. Use clientIPWithProxies with the parsed trusted-proxy nets, matching RateLimitMiddleware on the same route. * fix(client): 2 defect(s) (OC-0306, OC-0308) * fix(client): 1 defect(s) (OC-0307) QuickSwitcher registered a per-row click listener against the overlay-lifetime AbortSignal, but renderResults() rebuilds every row on each keystroke, arrow key, and store refresh. Discarded rows kept their listeners alive until the overlay closed. Replaced with one delegated click listener on the stable results container, keyed off the data-channelid each row already carries. * fix(client): 1 defect(s) (OC-0310) * fix(server): 3 defect(s) (OC-0279, OC-0291, OC-0292) Reap a soft-deleted message's attachment files, count lapsed temporary bans as active users in the require_2fa enrollment gate, and only apply the 2FA-enrollment precondition when require_2fa itself is being enabled. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdkJRbjCtrG76jEnrhKbYo * test(api): sync apiTestSchema with the user_blocks migration DeleteAccount's mention-count reversal joins user_blocks; the api package's hand-rolled schema fixture predates migration 012. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdkJRbjCtrG76jEnrhKbYo * fix(client): 3 defect(s) (OC-0281, OC-0282, OC-0296) Decouple the E2EE identity-mismatch modal and right-click popovers from the sidebar's per-render abort signal, and let global drag listeners survive a mid-drag re-render. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdkJRbjCtrG76jEnrhKbYo * fix(voice): 2 defect(s) (OC-0283, OC-0287) Retire a departed peer's E2EE key unconditionally on leave, and surface a failed microphone unmute instead of reporting an unmuted state the room never saw. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdkJRbjCtrG76jEnrhKbYo * fix(client): 3 defect(s) (OC-0289, OC-0303, OC-0309) Guard the DM call button against redialing the channel already joined, resolve the incoming-call banner's caller through the nickname-aware display name, and keep the DM profile sidebar subscribed to live member/status updates. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdkJRbjCtrG76jEnrhKbYo * style(client): prettier-format the dm-store test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdkJRbjCtrG76jEnrhKbYo * fix(server): 1 defect(s) (OC-0284) Make message soft-delete a compare-and-set so a repeated chat_delete cannot reverse mention counts twice. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdkJRbjCtrG76jEnrhKbYo * fix(server): 2 defect(s) (OC-0276, OC-0304) Re-sync a resumed connection's voice E2EE peer keys in registerNow (announce frames are unsequenced and cannot be replayed), and apply the live-connection presence rule to every DM payload DMService builds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdkJRbjCtrG76jEnrhKbYo * chore(ledger): record the 2026-08-21 hunt findings as fixed Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdkJRbjCtrG76jEnrhKbYo * chore(ledger): independent revert-proof pass for OC-0276..OC-0310 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdkJRbjCtrG76jEnrhKbYo * refactor(service): extract DeleteMessage authorization into a helper Keeps DeleteMessage under the cyclop complexity ceiling after the OC-0284 guard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdkJRbjCtrG76jEnrhKbYo --------- Co-authored-by: Claude <noreply@anthropic.com> * chore(ledger): record the 38 open findings from the 2026-08-22 hunt (#1403) Claude-Session: https://claude.ai/code/session_01SdkJRbjCtrG76jEnrhKbYo Co-authored-by: Claude <noreply@anthropic.com> * chore(graphify): refresh knowledge graph * fix: close the three B0 P0 gates and record a measured baseline (#1409) * chore(security): stop tracking the private security-finding reports docs/security-findings/ holds detailed reports for defects that are not yet fixed. The directory was untracked but not ignored, so any 'git add .' would have published seven unfixed vulnerability traces to a public repository. Findings are coordinated through private GitHub Security Advisories (docs/security.md); only opaque identifiers and safe status belong in tracked plans. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(client): repair the two red P0 unit contracts (G-01, G-02) G-02: noise-suppression-restart stubbed MediaStream with vi.fn().mockImplementation(arrow), which is not constructible. Vitest 4 threw 'is not a constructor' at the new MediaStream([inputTrack]) call in noise-suppression.ts before reaching any assertion. Replaced with a real class; the OC-0277 assertions are unchanged. G-01: message-list's OC-0217 guard was inverted, not merely stale. It spied on AbortSignal.prototype.addEventListener and asserted zero abort registrations, but the leak it names registered row listeners via element.addEventListener(..., { signal }) — a path that never calls that prototype method. Measured: the leak produces 0 registrations (test passes), while the OC-0286 fix rotates a per-window AbortSignal.any and produces 5 across 5 distinct signals (test fails). The guard passed on the bug and failed on the fix. It now captures the signal each window's row listeners register against and asserts the invariant its name always claimed: one signal per rendered window, a fresh signal per jump, and every superseded window already aborted with exactly one live. Verified both directions — green on the fix, and 'expected 1 to be 5' with beginRowRender() reverted to rowSignal = ac.signal. Client suite: 5257 passed, 0 failed (was 5255 passed, 2 failed). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(client): make the Playwright suite terminate The runner finished every test and then never exited, printing no summary — so the failure read as 'tests never finish' when it was 'process never exits'. getActiveResourcesInfo() at hang time showed a live ProcessWrap plus several PipeWrap: the Vite dev server was still running. Playwright's webServer teardown does not kill it here. Measured, full suite each time: npm run dev hangs, tests pass node node_modules/vite/bin/vite.js hangs, tests pass reuseExistingServer: false hangs, tests pass gracefulShutdown SIGTERM/3s hangs, tests pass npx vite exits, 290 of 293 FAIL no webServer (pre-started) exits, 293 pass in 33s npx only appears to fix it: npx exits once Vite is up, Playwright reads that as the server dying and tears the group down mid-run, so later tests get ERR_CONNECTION_REFUSED. globalTeardown now kills the process listening on the dev port, releasing the runner's handle. The webServer command spawns Vite's entry point directly so the listening process is Playwright's own child — via 'npm run dev' the npm process would still hold the handle open. It also reaps servers orphaned by an interrupted run, which reuseExistingServer would otherwise silently adopt. An earlier revision used netstat, which is not on PATH in every shell here; the swallowed ENOENT made the fix look applied while the hang persisted. It now uses PowerShell on Windows and lsof elsewhere, and warns on failure rather than failing silently. npm run test:e2e: exit 0, 293 passed, 37s, reproducible, no orphan listener. playwright.config.prod.ts carried the same npm-wrapper shape. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(client): align .nvmrc with the Node version CI uses Three versions were in play, not two: .nvmrc said 20, CI pins 24, and the machine the audit was measured on runs 26. A baseline measured against .nvmrc is not the baseline CI produces, which defeats the point of B0. Scoped to .nvmrc only. The full single-source-of-truth work — package engines, contributor docs, release — stays in B1 (RL-17 / C-01). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(plans): add the beta audit set and the B0 baseline The 2026-08-23 audit set has been sitting untracked: repository-health and repository-layout audits, beta product requirements, requirement traceability, the issue register, and the B0-B10 roadmap. They are the plan of record for beta and belong in the repository. Adds b0-baseline-2026-08-25.md, which supersedes the roadmap's 'current evidence snapshot'. Every row is marked measured or carried, so nothing is inherited silently. It also records three audit claims that did not survive verification: - G-01 was an inverted guard, not a stale assertion — it passed on the bug and failed on the fix. - The Playwright hang matched none of the three hypotheses; the runner could not kill its own dev server. - The golangci-lint toolchain failure is refuted: 19 linters run, 0 issues, verified with -v to rule out the known zero-linters false-green. Adds b0-dev-branch-protection.sh, which records the applied dev branch protection and the reasoning behind each setting. Security detail stays private: the register carries only opaque SEC-* families and safe closure criteria, per the roadmap's public/private handling policy. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(graphify): refresh the knowledge graph Own commit, per CLAUDE.md — the graph payload does not belong in the diff of the changes that triggered it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(plans): add the active-plan index and fix a stale status header (G-04) Planning documents had no recorded state, so a reader could not tell current guidance from shipped history. docs/plans/README.md now indexes every plan as active, partially implemented, design-only, or shipped, and names the source of truth for each concern so a defect count is never read out of a plan. Status is recorded in the index rather than by moving or rewriting the historical plans, so links from audits and commit messages keep resolving. One real stale claim found and fixed: audit-2026-08-19-remediation.md still read 'in progress 2026-08-19' while its own phase table showed phases 1-6 done 2026-08-20 (merged03fcb7d5, PR #1396) with only phase 7 pending. The header had drifted because the table was updated in place and the header was not. No plan was found claiming '0 open findings'. Also records the Step 8 staleness pass in the B0 baseline: all 38 open OC records still resolve to a live file:line at this commit, so none is superseded by later work. Adjudicating them individually is bughunt-fix work, not B0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Update graph output files and manifest with new metadata - Updated graph.html and graph.json with new binary data. - Modified manifest.json to reflect changes in file modification times and AST hashes for several documents. - Added new entry for README.md in the manifest with its corresponding metadata. * docs(plans): close the Docker and coverage leftovers in the B0 baseline Docker smoke: measured and passing. Image builds at 50.1 MB and boots on :8443 with TLS; docker-smoke.sh exits 0. Server coverage: re-measured at 74.6% aggregate, confirming the figure carried from the audit rather than continuing to inherit it. Two findings from doing it: ENV-03 — docker-smoke.sh cannot be run from Git Bash on Windows. MSYS path conversion rewrites the container-internal /chatserver into 'C:/Program Files/Git/chatserver', so docker exec fails 127 and the script reports 'container never reported healthy within 30s' — indistinguishable from a real boot regression. MSYS_NO_PATHCONV=1 makes the same script pass. CI is Linux and unaffected, but Windows is an official contributor platform (RL-20). The CI Docker job is gated on main, so it is skipped for any PR targeting dev — a dev-targeted change cannot get Docker evidence from CI at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(graphify): refresh the knowledge graph Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(plans): B1 execution plan, and accept HP-0 (#1410) * docs(plans): add the B1 repository-foundation execution plan B1 is the isolated layout/contributor phase. This records the execution order, the proof for each step, and what is out of scope. Two findings worth surfacing before any B1 work starts: - HP-0 was never formally accepted. The roadmap's B1 entry gate requires it; no scorecard artifact exists, no commit or document records an acceptance, and the B0 baseline still lists "Step 10: HP-0 sign-off" under "Not yet done in B0". The plan lists the five gaps that closing it requires, including pinning required status checks on dev -- which are still unset, so a dev PR can currently merge red. - Several layout-audit claims do not survive verification against HEAD, matching the B0 pattern. RL-09's "no single command verifies both protocol consumers" is false (make protocol-verify does, and is enforced in CI, the pre-commit hook, and a contract test). RL-10's test-discovery side effect never fires (no _test.go in Server/scripts). RL-06's regeneration concern is refuted locally. RL-08 grows a toolchain constraint instead. RL-05, RL-07, RL-20 and RL-21 are each worse than written -- RL-20 includes a live bug where a missing `make` is reported as stale protocol constants. The riskiest item, RL-01 (flatten Client/tauri-client into Client), gets a full reference inventory and a mechanical proof for both commits: tree- object equality for the pure move, and scripted-substitution replay for the path rewrite. Release asset names and updater contracts are verified independent of the directory name, so the move cannot rename an artifact. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(plans): correct the B1 status-check pin list from a live dev PR The list was derived from ci.yml. Observing PR #1410's actual checks found three that exist in no workflow file -- Analyze (go), Analyze (javascript-typescript), Analyze (actions) -- because CodeQL runs from GitHub default setup, configured in repository settings. Reading .github/ alone misses them. Also confirms the two negative predictions against a real dev-targeted PR: Server Docker Build (verify) reports as "skipping", and Tauri Full Build never appears in the check list at all. Neither may be pinned. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(plans): accept HP-0 and pin the dev required status checks Closes B1's entry gate. All five B1-0 items are done. The scorecard is the artifact the hold point asks for: one place that answers its four questions, records what was accepted as a stated limitation rather than claimed green, and part-closes R-08. Required status checks are now pinned on dev -- ten of them. That was B0's one outstanding step. Two things came out of doing it: - The names cannot be inferred from ci.yml. Three of the ten (the Analyze jobs) exist in no workflow file, because CodeQL runs from GitHub default setup configured in repository settings. They were read off a live dev-targeted PR with `gh pr checks`. - Server Docker Build, Tauri Full Build and the CodeQL aggregate are deliberately excluded. The first two report "skipping" on a dev PR -- Tauri Full Build under its unexpanded matrix name, since the job is skipped before matrix expansion. Admin Panel E2E is excluded because continue-on-error makes it report success unconditionally. Two prior claims are corrected rather than left to propagate: - b0-dev-branch-protection.sh was written assuming repository-settings writes are blocked from the agent sandbox. They are not; the PUT succeeded. The script stays as the record of intent and the way to re-apply or undo. - An earlier revision of the B1 plan said Tauri Full Build does not appear in a dev PR's check list at all. It does, as skipping. Evidence closed out: - Rust is no longer a carried row. Re-measured: 115 passed, cargo clippy --all-targets -- -D warnings at exit 0, confirming the carried figure. - The 38 open ledger records are accepted as counted, non-stale and assigned: 11 medium / 27 low, zero high or critical, zero dead paths across all 348 re-verified at this commit, and none assigned to B1. - The private security review is reconciled: 7 findings, 7 of 7 mapped to existing public rows, 0 unmapped. Summary is content-free; the detail stays in the untracked private reports. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: flatten Client/tauri-client into Client (B1-1) (#1411) * refactor: move Client/tauri-client to Client (pure move, no content change) * refactor: re-point paths after the Client flatten (mechanical, no behaviour change) --------- Co-authored-by: Claude <noreply@anthropic.com> * B1-2: truth, entry points, and contributor path (#1412) * fix(hooks): guard on the command the hook actually runs pre-commit probed one binary and invoked another. The protocol block guarded on `command -v go` and then ran `make protocol-verify`; the sqlc block guarded on `command -v sqlc` and ran `make sqlc-verify`. `make` is not on PATH on a stock Windows box, so a contributor with Go installed but no make had their commit rejected with pre-commit: FAIL: protocol constants are stale — run 'make protocol-generate' in Server/ and stage the result when nothing had been generated and nothing compared. The real cause was `make: not found`, and the advice the message gives fails the same way. Rather than add a `command -v make` guard, inline what the two Makefile targets reduce to — `sqlc generate` / `go run ./scripts/genprotocol` followed by `git diff --exit-code`. Same semantics, one less prerequisite, and it doubles as the make-free equivalent B1-2 asks for. The Makefile targets stay for anyone who prefers them. Also: the protocol block was the only one with no `else`, so a contributor without Go got no check and no notice. It now warns like its two siblings. And gofmt is a separate binary from go, so it is probed separately. Verified both directions with the hook body replayed verbatim: - Go present, make absent -> passes, no staleness claimed. - schema edited without regenerating -> fails, as it must. Refs RL-20 / L-14. * docs: state one branch and PR model Active documents contradicted each other head-on. README.md and docs/contributing.md said branch from `dev` and target `dev`; CLAUDE.md said branch from `main`, PR to `main`. That is R-02, and the 2026-08-19 audit had already recorded it as D-06 without it being resolved. `dev` is the answer, and the repository already behaves that way: B0 made `dev` PR-only with ten required checks enforced on admins, and #1409, #1410 and #1411 all landed there. `main` carries releases. docs/contributing.md becomes the single source of truth. It now states the model, what protection is actually applied, and the two consequences a contributor meets on their first PR — that a self-mergeable PR still cannot merge red, and that Docker and Tauri Full Build report as skipped against `dev` rather than failing. Everywhere else summarises and links here. - CLAUDE.md: corrected, with a link rather than a second copy. - CONTRIBUTING.md: new. GitHub's contributing-guidelines affordance only resolves the root, .github/ or docs/ — `docs/contributing.md` is not a path it finds, so the link never appeared on issues or PRs. - PULL_REQUEST_TEMPLATE.md: names the base branch, which it did not. - bughunt-run skill: reviewed the branch against `origin/main`, which is the wrong base once every PR targets `dev`. README.md already said `dev` and is left as the short summary it should be. Dated audits and the historical remediation plan keep their `main`-era wording — they are records. Refs R-02. * fix(hooks): pick the pre-push base from the nearest integration branch pre-push decided which side's gates to run from `git diff --name-only origin/main...HEAD`. That was right when everything targeted `main`. Once `dev` became the integration branch it stopped being right: a branch cut from `dev` diffed against `main` counts everything on `dev` and not yet on `main` as "changed". Measured on this branch: the old base reported 609 changed files, the new one reports 6. So in practice the hook was running the full server build matrix and the client typecheck plus eslint on every push, whatever the change touched — the file-based narrowing it exists for never engaged. Now it picks whichever of origin/dev, origin/main is nearest, by commits between merge-base and HEAD, skipping a candidate that scores 0. Verified: a branch cut from dev picks origin/dev (2 ahead); dev itself scores 0 against dev and picks origin/main (8 ahead), which is what a dev -> main release PR wants. With no candidate resolvable it falls back to the existing `__all__`, so an unfetched or shallow clone still runs everything. Refs RL-20 / L-14, R-02. * chore(node): one Node source of truth `.nvmrc` and all ten `actions/setup-node` pins said 24; five active documents and the repo's only `engines` block still said 20. A contributor following the docs installed a version CI does not run. Node 24 wins — it is what CI already runs. Every manifest now declares `engines`, and `engine-strict=true` turns a wrong major into a failed install rather than an `EBADENGINE` warning nobody reads. `>=24` rather than `^24` so a Node 26 box keeps working; `Client/.nvmrc` stays the human-facing pin and the docs point at it instead of restating a number. The `.npmrc` is per package root, not one at the top. npm reads the project `.npmrc` from the package directory and does not walk parents — verified with a throwaway package requiring node >=99: with only a parent `.npmrc` npm warned and exited 0; with one in the package directory it failed `notsup`. A single root file would have left `Client/`, the package that matters most, on warnings. Five docs, not the four previously identified — `docs/mcp-introspect.md` also said 20. And `docs/contributing.md` claimed "`.nvmrc` + CI both say Node 20", which was wrong about both. Verified both directions in all three package roots: Node 22 fails `notsup`; Node 24 installs clean and `npm ci` passes in Client/. Refs RL-17 / C-01, ENV-01. * docs: add the documentation landing page `docs/` had 24 top-level files and no index. The root README carried a flat list of 22 links that had drifted: six documents were reachable from nowhere at all — including both 2026-08-23 audits and the test audit — and two entries were labelled "latest" while newer unlinked audits existed. docs/README.md is the index RL-12 asked for. It groups by what a document *is*, because that is what decides whether to trust it: guidance tells you how to do something, reference describes a contract the code implements, audits are dated snapshots nobody updates, plans record intent. Every tracked file under docs/ now appears exactly once, and the audit table says plainly that audit-2026-08-19.md still claims "0 open findings" when the ledger has 38. The root README keeps a short curated list and defers to the index, rather than maintaining a second copy that drifts again. Two fixes while there: `docs/plans/` was linked as a bare directory, unlike its two sibling directory entries, and was annotated "each carries a verified status header" — which docs/plans/README.md:7-9 explicitly contradicts, since a plan's header is exactly the thing that drifts and the index is the authority. Verified: 78 relative links across the new and edited files resolve, and no tracked docs/ file is unreachable from the index. Refs RL-12 / R-06. * feat(scripts): root command facade Entry points existed only inside Server/ (a Makefile) and Client/ (npm scripts). Nothing at the root told a new contributor where to start, and the root package.json had three scripts, none of which built or tested anything. `npm run check` from the root now runs what CI gates on, and check:server / check:client / check:rust run one stack. scripts/run.mjs is dependency-free Node — the shape render-ledger.mjs already uses — so `npm run check` works before `npm install` has. Cross-platform by construction: every step is spawned with an explicit cwd and no shell, so there is nothing to quote and no `cd &&` to behave differently on Windows. npm and npx get their .cmd suffix there. No step shells out to make. The facade orchestrates; it is not a new required path. Each step prints the command and the directory before running it, and those are exactly the commands documented per-stack — so a server contributor can read the output and type them instead, and still never needs Node. Tools CI installs but a contributor may not have (golangci-lint, which has no wrapper in this repo at all; sqlc, pinned by Server/sqlc.version) are skipped with a printed reason rather than failing. Three corrections to the ci-check skill while aligning it: - `make sqlc-verify protocol-verify` replaced by what those targets reduce to, so the documented path does not require make either. - `cargo test` -> `cargo test --lib`, which is what ci.yml actually runs. - "NODE_OPTIONS=--no-experimental-webstorage is mandatory on Node 22+" was false. tests/setup.ts installs the shim, CI runs Node 24 without the flag, and the suite was measured passing without it — 192 files / 5257 tests, identical to the flagged run. Also documents the third RL-20 problem, which needed no code: core.hooksPath is exclusive, so `npm run hooks:install` silently disables any .git/hooks/post-commit — including the one `graphify hook install` writes, which CLAUDE.md tells agents to install. Nothing warned about that. Verified: check:client 5257/192 green, check:rust 123 tests + clippy green, --list prints every command, and the optional-tool skip path reports rather than fails. Refs RL-04 / L-04, RL-20 / L-14. * feat(ci): fail on a document that contradicts the findings ledger G-04's remaining half. The ledger is the source of truth for defect counts, but nothing stopped a planning document from stating a different number and nothing noticed when one did. `render-ledger.mjs --check` cannot help: it validates the JSON schema and returns before rendering, so it never reads FINDINGS.md and cannot see drift at all — and no workflow ran it anyway. scripts/check-doc-counts.mjs counts ledger statuses and compares them to what an allow-list of active documents claims, failing with file, line, claimed value and actual. Wired into ci.yml as a job with no npm ci, since the script imports nothing outside node:, and into the facade as `npm run check:docs` — first in `check`, so a contradicted count does not wait behind ten minutes of -race. The patterns are narrow on purpose. A first attempt matched any "<number> <status>" and flagged nineteen things, all false: "the 45 open P1 rows" (issue-register rows, not ledger findings), "All 8 findings F1-F8" (a different register), "G-05 **refuted**" (an identifier), `">=20"` and `CGO_ENABLED=0` (not counts at all). A check that cries wolf gets ignored, which is the failure G-04 already describes. So a number is only read as a claim in three shapes that cannot mean anything else: an enumeration of two or more "<n> <status>" pairs, a status table row in a table that totals itself, and "<n> records/findings" where the ledger is named within three lines. Fifteen selftest assertions pin both directions, and the job runs them before it runs the check. It reads findings-ledger.json directly rather than importing render-ledger.mjs for `validate`/`render`: that module ends in a bare top-level `await main()` with no import.meta.main guard, so importing it rewrites FINDINGS.md as a side effect. Dated docs/audit-*.md are reported, never failed — they are snapshots nobody maintains. audit-2026-08-19.md does claim zero open findings against 38 open, so b0-baseline's "No plan was found claiming '0 open findings'" holds for docs/plans/ but not for docs/. Not included: a real FINDINGS.md render-drift check. That is RL-07 and belongs with the generated-artifact work, not here. Verified: 27 claims across 9 documents agree; corrupting one count in docs/plans/README.md fails the check naming that line, for both the status and the total. Refs G-04. --------- Co-authored-by: Claude <noreply@anthropic.com> * chore: remove graphify knowledge graph tooling (#1413) The committed knowledge graph and its PreToolUse hooks were steering every codebase question through `graphify query` before any other tool could run. Serena (gopls + rust-analyzer + tsserver over MCP) answers the same questions from real language servers rather than a generated snapshot that goes stale between rebuilds, so the graph no longer earns the ~20 MB it costs the tree. Removed: - `graphify-out/` untracked (7 files, ~20 MB) and now gitignored - both `graphify hook-guard` PreToolUse hooks from `.claude/settings.json` - the "Knowledge graph (graphify)" section of `CLAUDE.md` - the `graphify-out/**` block from `.gitattributes` and `.gitignore` - the graph-rebuild step from the `bughunt-run` skill, and the graph-edge guidance from the bughunt workflow prompt - the graphify-specific `core.hooksPath` example in `ci-check` and `docs/contributing.md`, keeping the underlying warning in generic form Also deletes the locally installed `post-commit` / `post-checkout` rebuild hooks (untracked, not part of this diff). This does not shrink clone size: the graph blobs stay in published history, which `docs/plans/b1-repository-foundation-2026-08-25.md` explicitly rules out rewriting. It does stop future refreshes from adding more. Dated audit and plan documents keep their graphify references as a historical record of the state they described. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> * B1-3: repository hygiene gates (RL-19 / L-13, S-05) (#1414) * chore(format): one Prettier config at the repository root Every formatting rule in this repository lived under Client/ and covered exactly two globs: Client/src/**/*.ts and Client/tests/**/*.ts. Root Markdown, all of docs/, every YAML and JSON, all CSS, the root scripts and tools/mcp-introspect were formatted by nothing. There was no .editorconfig. The obvious fix -- a second Prettier config at the root for "everything else" -- gives two configs and two ignore files that can silently disagree about the same file. So the root takes ownership instead: config, ignore file and gate move up, and Client/ folds in. Client's inline "prettier" block, its .prettierignore, its format/format:check scripts and its now-unused prettier devDependency are all deleted; knip would have failed client-check on that last one. The .prettierrc.json values are lifted byte-for-byte from Client/package.json, which is what keeps the reformat commit free of client TypeScript churn: 87 tracked files need reformatting and not one of them is under Client/src or Client/tests. .prettierignore carries only what .gitignore does not. Prettier 3 reads the root .gitignore by default, so node_modules/, dist/, coverage/, Client/src/generated/ and docs/security-findings/ need no entry. It does NOT read nested .gitignore files, which is why .remember/ is listed explicitly -- 38 untracked per-machine scratch files were otherwise able to turn a shared gate red. graphify-out/ is listed because its seven files are tracked and .graphify_labels.json is signed byte-for-byte by its .sig, so formatting it would silently invalidate the signature. check:hygiene is registered in scripts/run.mjs and folded into check and release:preflight. It deliberately contains no `gofmt -l` step: gofmt -l prints offenders and still exits 0, so it cannot fail a build. Go formatting is enforced separately. shellcheck and actionlint take their file lists from `git ls-files`, never a filesystem glob -- .claude/worktrees/ holds a gitignored pre-flatten copy of the tree with three .sh files a glob would happily lint. This commit leaves the tree non-conformant on purpose. The reformat is the next commit, so the 87-file diff is reviewable separately from the rule that caused it. Not included: editorconfig-checker. .editorconfig is the editor baseline the audit asked for; Prettier, gofmt and rustfmt already fail CI on the same indentation and newline rules, so a fourth tool checking them again is a gate with no failure mode of its own. Verified: `npx prettier --check .` names 87 tracked files and zero untracked ones; the same command listed 38 .remember/ scratch files before the ignore entry and none after. `node scripts/run.mjs --list` resolves check:hygiene to 8 shell targets and 4 workflow targets. Both package.json files parse. Refs RL-19 / L-13, S-05. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(format): reformat the tree to the repository Prettier rules Mechanical. This commit is `npx prettier --write .` and nothing else -- the rule that caused it landed in the previous commit so this diff can be reviewed as a transformation rather than as 84 files of hunks. 84 tracked files: 54 Markdown, 7 .mjs, 7 JSON, 6 YAML, 4 .js, 3 CSS, 2 TypeScript (the two Playwright configs at Client's root, which the old Client/src + Client/tests globs never covered). No file under Client/src or Client/tests moves, because .prettierrc.json carries Client's former inline values byte-for-byte. Prettier rewrote 87 files, not 84. The three in .github/ISSUE_TEMPLATE/ had CRLF on disk and differ only in line endings, which .gitattributes (`* text=auto eol=lf`) already normalises, so their committed blobs are unchanged. Worth knowing before someone reconciles the two numbers. The largest single diff is .superpowers/findings-ledger.json at 7976 lines rewritten. That is safe to format: nothing writes the ledger programmatically -- render-ledger.mjs reads it and writes only FINDINGS.md -- so no tool will fight Prettier over its style on the next hunt. FINDINGS.md itself is ignored as generated. Verified: `npx prettier --check .` reports "All matched files use Prettier code style", so the pass is both complete and idempotent. All 7 reformatted JSON files were parsed before and after and compared as values: semantically identical, zero content changes. `node .superpowers/render-ledger.mjs --check` still reports 348 valid findings and leaves FINDINGS.md untouched. `node scripts/check-doc-counts.mjs` still passes its selftest and still agrees on 27 claims across 9 watched documents -- table realignment did not break the patterns it matches on. `node scripts/run.mjs --list` still parses. Refs RL-19 / L-13, S-05. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(lint): enforce Go formatting in the Server linter S-05: repository-wide Go formatting was not a required gate. The only gofmt enforcement anywhere was .githooks/pre-commit, which is opt-in per clone (`npm run hooks:install`), only sees staged files, and warns-and-skips when gofmt is off PATH. The obvious fix -- a `gofmt -l` step in CI -- does not work: `gofmt -l` prints its offenders and still exits 0, so the step passes no matter what it finds. scripts/run.mjs has the same problem, which is why check:hygiene has no Go step either. So gofmt goes where it can actually fail something: Server/.golangci.yml. The file was already `version: "2"` but had no `formatters:` block at all, so the 19 enabled linters ran with zero formatters. In v2 gofmt/gofumpt/goimports moved out of `linters.enable` into their own section with its own exclusions. Adding it there means the gate reports through the Lint step of "Server Build & Test", which is already pinned as required on dev -- no new job and no new pin. Every tracked .go file is under Server/ (551 of them, one go.mod), so Server-scoped is repository-wide here. One file was genuinely misformatted: a one-space struct field alignment in Server/admin/handlers_users_broadcast_test.go, fixed in the same commit because a single line does not need its own reformat commit. Trap worth recording: `gofmt -l .` on a Windows working tree lists every file that has CRLF on disk, because gofmt normalises line endings. That reported 18 offenders here, 17 of them ghosts. The blobs are all LF -- .gitattributes forces `eol=lf` -- so CI never saw them, and the honest test is to run gofmt over `git show HEAD:<file>` rather than the working copy. Doing that across all 551 tracked Go files found exactly the one real offender above. Verified both directions with golangci-lint v2 locally: `golangci-lint run ./...` reports 0 issues on the formatted tree; appending a misformatted function to Server/auth/constants.go produces 2 gofmt findings; appending the same misformatted function to Server/db/dbgen/admin.sql.go produces 0, so the exclusion holds. Both files restored and verified clean afterwards. Refs RL-19 / L-13, S-05. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(scripts): escape the NUL separator instead of embedding one The `tracked()` helper added earlier in this branch splits `git ls-files -z` output on NUL. The separator was written as a literal NUL byte rather than the two-character JavaScript escape, so scripts/run.mjs became a binary file: `git diff` refused to show it, `grep` reported "Binary file matches" instead of the line, and `* text=auto` in .gitattributes stops normalising line endings for a blob it detects as binary. The code worked -- splitting on a raw NUL and splitting on "\0" are the same operation -- which is exactly why this is worth fixing before it is inherited. A source file that tooling classifies as binary is a file nobody can review. Verified: zero NUL bytes remain, `grep -n "split("` now prints line 50 instead of "Binary file matches", `node scripts/run.mjs --list` still resolves the same 8 shell and 4 workflow targets, and prettier still reports the file clean. * chore(lint): enforce Rust formatting Rust had no formatting gate of any kind: no rustfmt.toml, no `cargo fmt` anywhere in CI, in scripts/run.mjs, in the Makefile or in the git hooks. Clippy was the only Rust gate, and clippy does not check layout. `cargo fmt --all -- --check` now runs in the rust-tests job, ahead of clippy: a formatting failure is cheap to produce and cheap to fix, and there is no reason to spend a clippy pass to surface one. The stable toolchain in that job requested `components: clippy` only, so rustfmt is added there. Only that job. ci.yml has a second, byte-identical `Install Rust` block in tauri-build; it stays clippy-only, because a full desktop build is the wrong place to discover a misplaced brace. No rustfmt.toml. The default profile is the point of a baseline -- a config file here would be a second opinion about style with nothing to say. Client/src-tauri is a single `[package]`, not a workspace, so `--all` is a safeguard against a future member rather than a fan-out today. Verified: `node scripts/run.mjs --list` resolves check:rust to three steps with `cargo fmt --all -- --check` first, `npm run format` now also runs `cargo fmt --all`, and prettier reports ci.yml, run.mjs and the ci-check skill clean. `cargo fmt --all -- --check` currently fails on 13 files -- that is the reformat, and it is the next commit. Refs RL-19 / L-13. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(format): reformat the Rust crate to rustfmt defaults Mechanical. This commit is `cargo fmt --all` and nothing else; the gate that demands it landed in the previous commit so this diff is reviewable on its own. 13 of the 17 tracked .rs files, +343/-164. The crate had never been formatted, so the changes are the usual first-run set: aligned trailing comments collapsed to single spaces, single-element slice literals folded onto one line, long method chains broken across lines, closure bodies expanded into blocks. Verified: `cargo fmt --all -- --check` is clean, so the pass is complete and idempotent. `cargo clippy --all-targets -- -D warnings` finishes with no warnings, and `cargo test --lib` reports 115 passed / 0 failed -- identical to before the reformat, which is what "mechanical" has to mean for a commit that touches this much of the crate. Refs RL-19 / L-13. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(scripts): make the root facade actually run on Windows Adding the first gate that a contributor would run from the repository root exposed two bugs in the facade, both of which made it silently wrong on the platform this project is developed on. 1. Every npm and npx step failed. bin() appends `.cmd` on Windows, but Node refuses to spawn a .cmd or .bat with shell:false -- the CVE-2024-27980 mitigation -- and fails with EINVAL and a *null* exit status. run.mjs only special-cased ENOENT, so the result was `FAILED: npx prettier --check . exited null` with nothing to explain it. check:client has three npm steps and has never been able to run here. Fixed by spawning only the npm shims through a shell. They are concatenated into a single command string rather than passed as an args array, because shell:true plus a separate array is deprecated (DEP0190) and prints a warning on every invocation; no argument in this file contains a space. 2. Every optional() step was skipped, always. onPath() shelled out to `where` on Windows, but where.exe lives in C:\WINDOWS\System32, which a Git Bash PATH does not necessarily contain -- on this machine PATH carries System32\Wbem, System32\WindowsPowerShell\v1.0 and System32\OpenSSH but not System32 itself. The probe could not start, `probe.status === 0` was false, and golangci-lint and sqlc reported as "not installed" while installed. Fixed by resolving against PATH and PATHEXT directly. No subprocess, and no dependency on which directories happen to be on PATH. A spawn error other than ENOENT now reports its code instead of surfacing as a null exit status. Verified: before, `node scripts/run.mjs check:hygiene` died with "exited null" and both optional steps printed SKIP with the tools present on PATH. After, the same command runs prettier, shellcheck and actionlint and prints "check:hygiene: passed", with no deprecation warning. `golangci-lint` is detected by the new onPath where the old one missed it. Refs RL-20 / L-14. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(format): ignore build output that nested gitignores hide Prettier honours the root .gitignore and no other. Every build and scratch directory in this repository is ignored by a *nested* one -- Client/.gitignore, .serena/.gitignore, .superpowers/sdd/.gitignore -- so none of them were excluded from the new repository-wide gate. The effect is not subtle. Running `cargo test` once drops roughly 850 formattable files into Client/src-tauri/target/, and the hygiene gate goes from clean to "Code style issues found in 939 files". CI never sees it, because a fresh checkout has no build output; every contributor sees it on their second command. Mirrors the three nested files rather than inventing a list: dist, coverage, playwright-report, test-results, .vite, src-tauri/target and src-tauri/gen from Client/.gitignore, plus .serena/ and .superpowers/sdd/. node_modules needs no entry -- Prettier ignores it by default. Verified: `npx prettier --check .` reports "All matched files use Prettier code style" with a fully populated Client/src-tauri/target/ present on disk, and still names README.md when a misformatted table is appended to it. Refs RL-19 / L-13. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(ci): shellcheck, actionlint, and a repository hygiene job The last two gates RL-19 asks for. Neither existed: the shell scripts were never linted, the workflows were never syntax-checked, and .githooks/pre-commit carried hand-written `# shellcheck disable=` directives that nothing had ever read. New `hygiene` job, ubuntu-only and root-scoped, modelled on docs-consistency for the same reason: every gate in it is platform-independent text analysis, and .gitattributes pins eol=lf so a second OS would only re-prove line endings. It runs `npm run check:hygiene` -- the same entry point a contributor runs, not a parallel copy of the commands. shellcheck ships in the runner image. actionlint does not, so it is pinned by version and verified by sha256: an installer script piped from a branch would be the one unverified download in a workflow file that pins every action by commit SHA. Prettier's step moves here from client-check, where it no longer belongs. Both linters found real defects. shellcheck, 3 findings in 8 scripts. Two are SC1125 errors in .githooks/pre-commit: `# shellcheck disable=SC2086 - repo paths contain no spaces` is not a valid directive. Trailing prose makes shellcheck discard the rest of the line, so neither suppression was ever in effect -- and one of the two was written earlier in this same branch, which is a fair demonstration of why the gate is worth having. The prose moves to its own line above. The third is SC2015 in start-server.sh, rewritten as an explicit if. actionlint, 5 findings, all inside `run:` blocks it shellchecks once shellcheck is on PATH. Three SC2015 in load-baseline.yml, rewritten as explicit ifs. Two SC2035 in release.yml, where `sha256sum *` should not become `sha256sum ./*`: the comment four lines above records that ParseChecksumFile exact-matches the last field, so a "./" prefix would strand every deployed server exactly as a "windows/" prefix would. `sha256sum -- *` satisfies the linter and leaves the output bytes identical. Verified all three gates in both directions with shellcheck 0.10.0 and actionlint 1.7.7 on PATH. Passing: `node scripts/run.mjs check:hygiene` prints "check:hygiene: passed" with all three steps run, not skipped. Failing: appending `bait_fn() { cat $1; }` to Server/scripts/voice-test.sh fails on SC2086; changing a runs-on to `ubunt-latest` fails on runner-label; appending a misformatted table to README.md fails prettier. All three files restored and confirmed clean afterwards. actionlint validates the new job in ci.yml itself. Refs RL-19 / L-13, S-05. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(plans): record B1 progress through B1-3 The header still read "B1-0 is complete; B1-1 is the next step" three merged phases later. A plan that misstates where it is costs a reader the same confusion whether it is stale by one phase or three. B1-0 (#1410), B1-1 (#1411), B1-2 (#1412) and B1-3 (this branch) are done; B1-4, dependency automation, is next. Verified: `node scripts/check-doc-counts.mjs` still agrees on 27 claims across 9 watched documents -- this file is one of them -- and prettier reports it clean. * chore(ci): pin Repository Hygiene as a required check on dev The second half of S-05. Its acceptance criterion is "tree is formatted AND a fast required gate fails future drift" -- a check that runs but is not pinned lets a formatting regression merge, so the gate is not a gate until this lands. The name was read off PR #1414 with `gh pr checks` after the job reported `pass` in 26s, not copied out of ci.yml. That order matters: the B0 script records that three pinned names exist in no workflow file at all, and that a required check which never reports blocks every PR forever. Extends the existing script rather than adding a second one, per the B1 plan. Also records, in the "deliberately NOT pinned" list, that Docs & Ledger Consistency reports and passes on a dev PR yet is unpinned. That reads as an oversight from the 2026-08-25 pass rather than a decision, but it belongs to G-04, so it is documented here and not changed. NOT APPLIED YET. Running this script now would pin a check that PR #1413 cannot report -- its branch predates the hygiene job, so the job does not exist in its workflow file and the check would never arrive. Run it after #1414 merges; #1413 needs a rebase onto dev regardless. Verified: shellcheck clean, the embedded JSON parses, and `check:hygiene` passes with prettier, shellcheck and actionlint all running. Refs S-05, RL-14 / G-03. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> * B1-4: dependency automation (RL-05 / L-05, RL-18) (#1415) * chore(deps): cover the root and mcp-introspect npm roots The repository has three npm package roots — `/` (changelogen, prettier), `/Client`, and `/tools/mcp-introspect` (@modelcontextprotocol/sdk, zod) — each with its own package-lock.json, and `npm run bootstrap` runs `npm ci` in all three. Dependabot watched exactly one of them. The root's prettier is what the Repository Hygiene gate runs, so the formatting gate's own toolchain was drifting unwatched. The obvious fix is to collapse the three roots into an npm workspace and watch one lockfile. Measured on npm 11.17 / Node 26 rather than assumed, that trade is bad, and it is bad for different reasons than expected. Workspaces do not break the things you would predict: `npm ci` inside `Client/` still exits 0, `npm run <script>` still resolves the hoisted binaries because npm prepends every ancestor `node_modules/.bin` to PATH, and `engine-strict` still fails the install on a wrong Node major. What they cost is ten CI steps keyed on `cache-dependency-path: Client/package-lock.json` (six in ci.yml, four in the tag-only, CI-ungated release.yml) pointing at a file that stops existing; the Repository Hygiene job's deliberate root-only install growing 970 ms to 6172 ms and 39 to 318 packages unless every call site remembers `--workspaces=false`; and one shared lockfile putting all three npm Dependabot groups back into the same file, which is precisely the rebase storm the grouping comment at the top of dependabot.yml exists to prevent. The measured benefit is one 298 KB lockfile instead of three (17 KB / 253 KB / 42 KB) and 614 resolved packages deduped to 582 — 32 packages, 5.2% — with client install time unchanged at 5642 ms against 5667 ms. So the roots stay separate and each gets its own block, matching the four that already exist: grouped to one PR, majors ignored, weekly on Monday. A single block with `directories:` was rejected for the same reason as workspaces — grouping only works while a group rewrites exactly one lockfile. The decision and its numbers are recorded in docs/contributing.md under Dependency Policy, so the next person to propose workspaces reads the measurement instead of repeating it. Verified: a coverage checker cross-references every `package-ecosystem` / `directory` pair in dependabot.yml against every manifest in `git ls-files`, in both directions. Against dev at2a37f386it reports `UNWATCHED npm package.json` and `UNWATCHED npm tools/mcp-introspect/package.json` (plus Server/Dockerfile, which the next commit covers). Against this commit both npm rows read `ok`, and the forward direction confirms each newly declared directory really holds a package.json. `npx prettier --check` reports both edited files unchanged, so the B1-3 formatting gate stays green. Not included: the docker ecosystem (next commit, RL-18); immutable image digests for release/runtime containers, which is R-04's remaining half and belongs to B6; and turning the coverage checker into a permanent `check:hygiene` gate — a new manifest root can still drift unwatched, which is how this gap arose, but that is new gate machinery rather than the coverage this item asks for. Refs RL-05 / L-05 * chore(deps): watch the server container base images Server/Dockerfile pulls `golang:1.26-bookworm` to build and `gcr.io/distroless/static-debian12` to run, and nothing watched either. Every other dependency root in the repository is on a weekly Dependabot schedule, so the one artefact that ships to users as a whole filesystem was the only one whose upstream moved silently — including its CA certificates, which the Dockerfile comment specifically calls out as the reason distroless was chosen over scratch. The obvious fix is to pin both images by digest and be done. That is the wrong move here for two reasons. A digest pin with no automation behind it is worse than a tag: it freezes the base image at whatever was current the day someone typed it, and a frozen distroless base is a frozen CA bundle. And digest refresh for release and runtime images is R-04's other half, scoped to B6 alongside the smoke tests that have to gate it — landing half of it here would leave the digests pinned and the refresh unowned. So this adds the `docker` ecosystem for /Server on the same terms as the five blocks around it: grouped to one PR, majors ignored, weekly on Monday. Be precise about what that actually buys, because it is less than the block implies. Of the two images only `golang:1.26-bookworm` carries a comparable version, so it is the only one Dependabot can act on today; `gcr.io/distroless/static-debian12` has no version tag, and an untagged image is not something a version update can move — it needs the digest pinning that B6 owns. The comment above the block records that the Go builder tag tracks Server/go.mod and every `actions/setup-go` in CI, so a `1.27-bookworm` PR is a prompt to move all three together rather than a standalone merge. Verified: the coverage checker cross-references every `package-ecosystem` / `directory` pair against every manifest in `git ls-files`, in both directions. Against dev at2a37f386the reverse direction reports `UNWATCHED docker Server/Dockerfile`; against this commit every row reads `ok` and it exits 0 — `ALL ROOTS WATCHED`, six blocks covering six manifest roots, with the forward direction confirming /Server really holds a Dockerfile. `npx prettier --check` passes on the edited file. Not included: Server/docker-compose.yml and Server/docker-compose.otel.yml. Their four images are `ghcr.io/j3vb/owncord-server:latest` (this repository's own published image), `livekit/livekit-server:v1` (a floating major tag, and majors are ignored everywhere), `jaegertracing/all-in-one:latest` and `prom/prometheus:latest` — none of which a version update can move, so a compose block would be configuration that provably produces nothing. Also not included: immutable digests plus digest-refresh PRs with smoke tests for the release and runtime images, which is the remainder of R-04 and belongs to B6. Refs RL-18 * docs: apply skill-review findings to ci-check and the project skills (#1416) The observation log had accumulated 46 open entries against a last review of 2026-08-14. Seven of them target skills tracked in this repository and were verified still-unapplied against the current files. `ci-check` gains four things it was missing. It never mentioned `cargo audit`, which CI runs pinned at 0.22.1 in `tauri-build` — the one gate that turns red with zero local changes, because an upstream advisory breaks a branch that was clean yesterday, and the one a hand-written mirror silently drops because no edit provokes it. It never mentioned that `release.yml` is tag-triggered and PR-ungated, so a smoke/sign/strip step added only there first executes on the release; #1376 shipped a smoke harness whose own bug then blocked a release, and #1378 fixed it structurally by extracting `Server/scripts/docker-smoke.sh` for both workflows. And it had no guidance for reading a red check at all: a new section adds causality-before-forensics triage (diff the changed-file set against the failing job's input surface before opening a log — a workflow-only diff cannot cause a Go goroutine leak), the lockfile-fork diagnosis for dependency bumps (a 1 → 2 entry-count transition means the update forked the dependency and revoked the features it was borrowing, so aligning versions is the fix, not setting the feature the new copy demands), and the known-flake table promoted to a signature-to-recovery index, now including the apt-mirror hang that cancels `tauri-build` by timeout. The baseline rule that came with the triage section needed adjusting rather than transcribing. Its source observation recorded `golangci-lint`'s known-red complexity baseline as 23 cyclop / 6 dupl / 21 funlen / 12 nestif; #1389 cleared that to zero, so quoting those numbers would have taught the reader to excuse a failure that is now genuinely theirs. The rule is recorded without them, stating that the repo currently carries no known-red gate and what to do if one is ever reintroduced. `protocol-change` claimed the schema is the source of truth without saying what it covers. It holds message-type names only, so a payload-field change touches the Go command/message files, the client types and `docs/protocol.md` and never the schema — routing one through the regenerate cycle is wasted work. A table splits the three cases, with the relay-handler caveat: a server that re-serialises drops unknown fields, so a forwarded field is not backward compatible with older servers. `task-observer`'s numbering discipline treated collisions as a parallel-human accident. They are structural in fan-out workflows, because a dispatched subagent has the skill active in its own context and writes to the same log. `bughunt-run` covered findings blocked by a circuit breaker but not findings that went stale: a later hunt routinely fixes a blocked finding as a side effect of an overlapping sibling, and a saved debris patch stops applying once a refactor rewrites its files. Of 6 findings blocked on 2026-08-14, 2 were already fixed 5 days later. `docs/contributing.md` gains the commit-body convention that was being followed without being written down anywhere — reasoning over diff-restatement, a `Verified:` paragraph proving both directions, and an explicit `Not included:` line. That last one is what keeps adjacent scope from becoming either silent drift or an unnecessary blocking question. Verified: each edit was checked against the live file before applying, which changed two outcomes. Observation 50 (make the hunt's stop rule measure coverage, not just quietness) is already implemented — `bughunt-run` documents `coverage + dry is the real stop`, `stalledCoverage` and `coverage.uncoveredAtStop`, landed by #1399 — so it is marked actioned rather than re-applied. Observation 42 looked covered by the same grep and was not: the existing text handles breaker-blocked findings, a different case from a finding a sibling fix already closed. Confirmed absent before editing: `cargo audit` and `release.yml` in ci-check, `payload` in protocol-change, `subagent` in task-observer. `npm run check:hygiene` passes (prettier clean on all five files); `npm run check:docs` passes. Not included: the 21 open observations targeting `superpowers:*` plugin skills, which live in a versioned plugin cache and… * chore(deps): bump the npm-dependencies group across 1 directory with 3 updates (#1428) Bumps the npm-dependencies group with 3 updates in the /Client directory: [eslint](https://github.com/eslint/eslint), [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint). Updates `eslint` from 10.9.0 to 10.9.1 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.9.0...v10.9.1) Updates `oxlint` from 1.79.0 to 1.80.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.80.0/npm/oxlint) Updates `typescript-eslint` from 8.67.0 to 8.68.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.68.0/packages/typescript-eslint) --- updated-dependencies: - dependency-name: eslint dependency-version: 10.9.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-dependencies - dependency-name: oxlint dependency-version: 1.80.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm-dependencies - dependency-name: typescript-eslint dependency-version: 8.68.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * ci(deps): bump anthropics/claude-code-action (#1429) Bumps the actions-dependencies group with 1 update in the / directory: [anthropics/claude-code-action](https://github.com/anthropics/claude-code-action). Updates `anthropics/claude-code-action` from 1.0.200 to 1.0.206 - [Release notes](https://github.com/anthropics/claude-code-action/releases) - [Commits](https://github.com/anthropics/claude-code-action/compare/24dcd50c0568f0fc9e9211213a4fd2d9eb15c4e0...1f291e1cfe0f5fc21db2aef19af844591600ade7) --- updated-dependencies: - dependency-name: anthropics/claude-code-action dependency-version: 1.0.206 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump golang Bumps the docker-dependencies group with 1 update in the /Server directory: golang. Updates `golang` from 1.26-bookworm to 1.27-bookworm --- updated-dependencies: - dependency-name: golang dependency-version: 1.27-bookworm dependency-type: direct:production dependency-group: docker-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: J3vb <192430104+J3vb@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
J3vb
Claude Opus 5
parent
f47ac1b39a
commit
874fc969a9
+1
-1
@@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# ─── Build stage ────────────────────────────────────────────────────────────
|
||||
FROM golang:1.26-bookworm AS builder
|
||||
FROM golang:1.27-bookworm AS builder
|
||||
|
||||
ARG VERSION=dev
|
||||
|
||||
|
||||
Reference in New Issue
Block a user