mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
B1-6: generated artifacts (RL-06 / L-06, RL-07 / L-07, RL-08 / L-08) (#1418)
* ci: verify FINDINGS.md against the ledger it renders from (RL-07) `.superpowers/FINDINGS.md` is generated from `findings-ledger.json`, and `CLAUDE.md` forbids hand-editing it — but nothing checked. The one automated consumer, `render-ledger.mjs --check`, validates the ledger's JSON schema and `return`s at line 116, *before* the only `render()` call at line 118, and never opens `FINDINGS.md` at all. A stale 1.09 MB rendering passed it cleanly. The audit says "no workflow runs it". That was true when it was written and is not now: B1-2 (#1412) wired `--check` into the `Docs & Ledger Consistency` job. So the gate exists, reports, and is blind to the thing its name suggests it watches — which is worse than absent, because it reads as covered. The obvious fix — render to a temp file and diff, as the B1 plan suggests — is not what this repository does. It has three implementations of one idea (`Server/Makefile` sqlc-verify and protocol-verify, `.githooks/pre-commit`, `scripts/run.mjs`), and all three regenerate **in place** and let `git diff --exit-code` be the differ. That needs no temp path, no cleanup, and inherits `.gitattributes`' line-ending normalisation for free. A fourth shape would cost a reader something for nothing. Done: - The gate, in all three places the existing two gates live: the `docs-consistency` CI job, `scripts/run.mjs`'s `CHECK_DOCS`, and a new `.githooks/pre-commit` block gated on the ledger, the rendering, or the renderer being staged. `npm run check` never ran the renderer at all before this, which contradicted `run.mjs`'s own stated purpose. - `validate()` now requires `severity`. This is not a nice-to-have riding along: `render()` sorts the open section by `SEV_RANK`, and an unranked severity makes the comparator return `NaN`, which leaves the sort order implementation-defined. A gate whose expected output is implementation-defined can go red across a Node upgrade for a reason that is not drift. The validation is what makes the gate's premise — that the rendering is a pure function of the ledger — true rather than merely true today. - `--stat` on the diff. Deliberate deviation from the three precedents: a fully drifted rendering is a ~40,000-line CI log, and the exit code is what gates. Eight files, 119 insertions, 24 deletions. The gate is one render (67-170 ms) plus one `git diff`. Rendering subsumes `--check`, because `main()` validates and exits 1 before it writes — so the CI job keeps both steps only so the checks UI names which fix is needed. Verified: both directions, and the naive test would have lied. Appending to `FINDINGS.md` proves nothing — the renderer overwrites it, so the perturbation vanishes and the diff comes back clean. `git diff <path>` compares the worktree against the **index**, so the drift has to live in the index. Changing one finding's title in the ledger and staging it *without* re-rendering — exactly the mistake the gate exists to catch — makes `git diff --exit-code --stat` exit 1 with a one-line stat, and `.githooks/pre-commit` fail with `FINDINGS.md is stale`. Restoring the ledger and re-rendering returns both to exit 0, and `git status --porcelain` is clean afterwards. Severity validation both ways: setting one finding to `moderate` makes `--check` print `INVALID OC-0001: bad severity moderate` and exit 1; `git checkout` of the ledger makes it valid again. The hook's grep pattern was exercised against five paths — the three `.superpowers/` targets match, `.superpowers/sdd/notes.md` and `scripts/check-doc-counts.mjs` do not. `node scripts/run.mjs --list` resolves `check:docs` to three steps rather than one; `npm run check:docs` and `npm run check:hygiene` pass, the latter with prettier, shellcheck (the new hook block) and actionlint (the new CI step) all running for real. Not included: untracking `FINDINGS.md` — that is the next commit, and the order matters. L-07 requires the drift check to exist *before* the removal, because the check is what proves the tracked copy was current at the moment it was deleted. No `import.meta.main` guard on the renderer: no caller imports it, and `import.meta.main` landed in Node 24.2 against an `engines` floor of `>=24`, so it would silently no-op on 24.0/24.1 — `scripts/check-doc-counts.mjs` documents the workaround and stays accurate. No `existsSync` guard for a missing ledger: the unhandled rejection already exits non-zero, so CI already rejects it and only the message is ugly, which is not drift. The `docs-consistency` job is not converted to `npm run check:docs`; it is deliberately `npm ci`-free with direct `node` calls in every step, and half-converting it would be worse than being internally consistent. No `Server/Makefile` target — the ledger is root-scoped, and `make` is not on PATH on a stock Windows box (RL-20). Refs RL-07, L-07 * chore: stop tracking the rendered FINDINGS.md (RL-07) The previous commit built the drift check RL-07 asked for. This is the second half: with the check in place proving the committed rendering was current, the rendering itself comes out of the index. Untracking is strictly stronger than checking. A drift check watches for a rendering that has fallen behind its source; not tracking it removes the possibility. `findings-ledger.json` stays the only tracked copy and remains canonical — `CLAUDE.md` tells contributors to open a PR against it — and the 1.09 MB view of it is regenerated in 67-170 ms by a command that was already documented. Why the drift check still had to land first, in its own commit: it is what proved the tracked copy was current at the moment it was deleted. Deleting a generated file you have never verified against its source is how you discover, later, that the source was wrong. L-07 sequences it the same way — "remove the tracked duplicate human rendering *after* deterministic on-demand/CI rendering and a drift check exist" — and this commit is the "after". The gate transforms rather than disappears. `git diff --exit-code` cannot watch an untracked file, so what remains of L-07's "CI rejects generation failure or drift" is the generation half, plus its separate "a downloadable rendering is reproducible" clause. CI now renders **twice and compares** — which tests both: the render must succeed (it validates and exits 1 before writing) and it must be a pure function of the ledger. The severity rule added in the previous commit is what makes that second property true rather than merely true today. The rendering is then uploaded as the `findings-ledger-rendering` artifact with `if: always()`, so a reviewer reads it without a Node run — and can read it precisely when the job failed. Six coordinated edits, and the fourth is not optional: - `.gitignore` — drop the `!` negation; the `.superpowers/*` blanket takes over. - `.gitattributes` — drop `linguist-generated=true`, now dead. - `.prettierignore` — drop the entry; Prettier 3 reads the root `.gitignore`. - `scripts/check-doc-counts.mjs` — drop it from `WATCHED`. A missing watched file is pushed to `failures` and exits 1 by design, with a message telling you to fix the list. Forgetting this line reds `Docs & Ledger Consistency` and `npm run check` on every subsequent run. - `CLAUDE.md` — the command stays, the "tracked artifact" framing goes. - `.claude/skills/bughunt-run/SKILL.md` — the human gate between hunt and fix reads this file, so it now says to generate it first. That reader is already at a terminal that ran the renderer seconds earlier. 13 files, 103 insertions, 9,278 deletions. The check-doc-counts gate goes from 27 claims across 9 documents to 21 across 8; the six it loses were rendered *from* the ledger they were checked against, so they were self-consistent by construction and could only ever have failed on a stale rendering — which is the thing that can no longer exist. Verified: both directions. `git ls-files .superpowers/` returns exactly two files; `git check-ignore -v .superpowers/FINDINGS.md` names `.gitignore:87` while the ledger itself is not ignored (exit 1), so the blanket rule did not overreach. Deleting the rendering outright and running `node scripts/check-doc-counts.mjs` prints `21 claim(s) across 8 watched document(s)` and exits **0** — the proof that the `WATCHED` line was dropped, because leaving it would have failed here. `npm run check:docs` then regenerates the file (1,087,051 bytes) and passes. Rendering twice and `cmp`-ing the results reports byte-identical output. The pre-commit hook was exercised both ways with the ledger staged: a severity of `moderate` fails with `findings-ledger.json is invalid`, and a valid tree passes with exit 0. `npm run check:hygiene` passes with prettier, shellcheck and actionlint all running for real. Not included: `findings-ledger.json` is untouched by this commit — it is the canonical copy and it stays tracked, at 1,205,085 bytes, which is *larger* than the rendering just removed. Anyone reaching for the size argument should know that untracking the rendering removes 47% of the pair and leaves the bigger, less readable half; the reason to do it is that the rendering is 100% derived and would otherwise write a fresh ~1.06 MB blob into permanent history on every hunt, not that it is the heavy one. No history rewrite — the blobs already committed stay where they are, per the B1 non-goal. `Server/Makefile` gains no ledger target: root-scoped, and `make` is not on PATH on a stock Windows box. Refs RL-07, L-07 * chore: stop tracking the prebuilt hello.wasm plugin example (RL-08) `Server/plugin/examples/hello/hello.wasm` was 946,410 bytes of committed build output — 84% of that directory — for a plugin subsystem that is disabled twice over: it compiles only under `-tags wazero`, and `plugins.enabled` defaults to `false`. Nothing verified it matched the `main.go` beside it. The remedy the audit names is a compile-and-compare gate. It cannot be built, and not for cost reasons: TinyGo embeds absolute host paths from the building machine's Go SDK and module cache into its output and offers no `-trimpath` equivalent, so two machines compiling identical source produce different bytes. A byte-identity gate cannot pass in principle. What is left is a compile-only check, and that needs three pinned downloads — TinyGo, a *second* Go SDK at 1.25.x because TinyGo 0.40.1 rejects the Go 1.26 this module pins, and Binaryen 129 — on every PR, to prove something weaker than advertised about a subsystem that ships in zero release artifacts. So the artifact goes and its provenance is written down instead. BPR-080 asks that the example WASM be "reproducible **or** provenance-verified" — disjunctive — and the second branch is the one that is actually reachable here. The repository had already made this call for itself. `sandbox_wazero_test.go` uses a 41-byte inline WASM literal, with the comment "Using a literal here avoids dragging a binary asset into the repo." This extends that from the tests to the example. Done: - `git rm --cached` the artifact; a narrow `.gitignore` entry naming the exact path. Deliberately **not** a blanket `*.wasm`: `Client/public/rnnoise.wasm` is a vendored npm artifact this repository does not build and the client fetches at runtime, so ignoring it would break noise suppression. The rule that separates them — untrack build output whose source we own and whose absence breaks nothing; keep vendored third-party artifacts required at runtime — is written into the ignore comment. - `Server/.dockerignore` gains `plugin/examples/`. `Dockerfile` does `COPY . .` and the file already excluded `scripts/` and `cmd/` but not this, so a developer who still has the untracked artifact on disk was shipping it into the build context. Same omission B1-5 fixed for `cmd/`. - The README carried two false statements, both now removed: it claimed the plugin is "used by `Server/plugin/plugin_test.go`" and that that test "exercises the manifest parser and the loader against this directory". Neither is true — `plugin_test.go` builds every fixture in `t.TempDir()`. - A Provenance section: TinyGo 0.40.1 + Go 1.25.3 + Binaryen 129, why the output is not byte-reproducible, and why the compile gate is deferred rather than merely absent. - The ABI-stability sentence L-08 requires, which existed nowhere in the repository: the ABI is experimental with no compatibility promise, and both halves of "disabled" are named with the files that prove them. Verbatim identical in the example README and `docs/contributing.md`. - The TinyGo/Go/Binaryen table existed in two hand-maintained copies that had already drifted in wording. It now lives in the example README only; `docs/contributing.md` links to it, which is the pattern that page already used two lines above for the ABI itself. Five files, 87 insertions, 20 deletions, plus the 946,410-byte deletion. Verified: both directions. The inertness proof is the load-bearing one, and it is the inverse of B1-5's remove-and-watch-it-fail, because here passing is the point: with `hello.wasm` moved out of the tree entirely, `go build ./...`, `go build -tags wazero ./...`, `go vet ./...`, `go test ./plugin/...`, `go test -tags wazero -count=1 ./plugin/...` and `go test ./api/...` all pass. `go list ./plugin/...` returns a single package with and without the tag, so `//go:build tinygo` keeps the example out of the module's build graph. The narrowness proof is one pair: `git check-ignore -v` matches `Server/plugin/examples/hello/hello.wasm` at `.gitignore:59` and exits 0, and exits 1 on `Client/public/rnnoise.wasm`, which `git ls-files` confirms is still tracked. `git ls-files Server/plugin/examples/` now returns exactly the three source files. `npm run check:hygiene` and `npm run check:docs` pass. Not included: no CI compile-and-compare job, per the reasoning above — deferred to B2, which the issue register already names as L-08's second phase. **L-08 is not claimed closed**: its closure evidence reads "Deterministic source build passes", and that is precisely what TinyGo cannot deliver here; the register's B1/B2 span is what makes deferring it in-scope rather than a slip. No `tinygo.version` pin file — `Server/sqlc.version` earns its existence through four mechanical consumers, and nothing would read this one; the gap in `docs/contributing.md`'s toolchain-pinning policy is closed by recording TinyGo and Binaryen as a documented exception instead. `main.go`, `plugin.json` and the README stay tracked — L-08 says keep the source, and this commit keeps all of it. `.gitattributes` keeps `*.wasm binary`, which still covers the client's vendored module. No history rewrite: the artifact's existing blobs stay where they are, per the B1 non-goal. Refs RL-08, L-08 * docs(plans): retire the removed graphify tooling from the B1 plan (RL-06) RL-06 asked for a 20.41 MB tracked `graphify-out/` payload to stop being tracked, after a portable regeneration command and a CI artifact existed. None of that happened. Instead `a5f7d95` (#1413) deleted the tool outright, taking all 7 tracked files with it — 20,408,656 bytes, `graph.json` at 19,463,420 — before B1-6 opened. `git ls-files` matches nothing graphify-related today. So the outcome RL-06 wanted holds (no large tracked payload, history intact) and the method it prescribed was bypassed. There is nothing left to do in the repository. What was left is a documentation problem, and a live one: this plan is an active document, and it still told a reader to run a tool that does not exist. The obvious response — delete every graphify mention — is wrong twice over. The `.gitignore` rule has to stay: the local directory reached ~208 MB with cache and dated snapshots on the machine that ran the tool, and dropping the rule would flood that contributor's `git status` with untracked noise. And the "do not rewrite history to shrink graphify-out" non-goal has to stay too: the files are gone from the tree but four `graph.json` revisions remain in the pack (~71 MiB logical, ~3.2 MiB packed of 13.28 MiB), so the line is still operative. It is what keeps "closed" honest rather than overclaiming. Done — nine edits, each a dead instruction rather than a stale mention: - **B1-2 Step 7, the worst of them.** It told a human to `unset GRAPHIFY_SKIP_HOOK`, run `graphify update .`, and `git commit -am` a refresh. The tool is gone, and `git commit -am` with nothing to commit exits non-zero while reading like a no-op success. Replaced with a retirement note; Step 7 is the last step, so nothing renumbers. - **The "Traps carried forward" entry.** A live instruction, in a list of traps, aimed at exactly the multi-commit sequence this phase is. Deleted. - B1-1 Step 1's `export GRAPHIFY_SKIP_HOOK=1` and its four-line hook rationale, collapsed to one sentence of history. The "close any editor, cargo, vite" paragraph beside it is still true and stays. - The RL-06 verdict row, the B1-6 bullet, the flatten's "leave alone" list, the `post-commit` parenthetical, B1-3's exclusion list, and the non-goal line. - `.gitignore`'s stale "delete the dir when convenient" TODO becomes a recorded decision citing the commit that caused it. Verified: `git grep -i graphify` outside the dated audit and the issue register returns exactly five hits, and every one is intended — the `.gitignore` rule and four plan lines that are explicitly retirement or history notes ("once began", "Retired", "closed by deletion", and the non-goal). `git grep GRAPHIFY_SKIP_HOOK` returns one hit, the sentence recording that it used to be required. `node scripts/check-doc-counts.mjs` still passes — this file is one of the documents it watches — and `npx prettier --check` is clean after the verdict-row rewrite reflowed the table. Not included: `docs/audit-2026-08-23-repository-layout.md` keeps its RL-06 row — dated point-in-time snapshot, and `check-doc-counts.mjs` already classifies `docs/audit-*` as report-only. `docs/plans/repo-health-issue-register-2026-08-23.md` keeps L-06 and the R-03 row that routes to it, and the reason is *not* that it is dated: it is in the watched set, i.e. this repository treats it as active. It is that no B1 phase has updated its closure column, so L-01, L-04, L-05 and L-09 through L-13 are all closed in fact and open on paper. Changing that convention in the phase with the least to say about it would leave the register half-updated, which is worse than uniformly stale. That sweep belongs to `R-06`, or to one pass at B1's end. No history rewrite, per the non-goal this commit deliberately keeps. Refs RL-06, L-06 * docs(plans): record B1 progress through B1-6 The header still read "B1-3 are complete; B1-4 is the next step" three merged phases later — B1-3 (#1414), B1-4 (#1415) and B1-5 (#1417) have all landed, and B1-6 is this branch. B1-3 set this convention with its own `docs(plans): record B1 progress through B1-3` commit, and then B1-4 and B1-5 both skipped it. A plan that misstates where it is costs a reader the same confusion whether it is one phase stale or three; three is just harder to notice, because the header looks deliberate. Verified: `node scripts/check-doc-counts.mjs` still agrees on 21 claims across 8 watched documents — this file is one of them — and prettier reports it clean. Refs RL-06 (the phase this records), R-08 * chore(ci): pin Docs & Ledger Consistency as a required check on dev The previous commits gave `Docs & Ledger Consistency` a gate that can actually fail: it now rejects a ledger that will not render, on top of the schema check it already ran. But the job is not among `dev`'s required contexts, so it reports and cannot block. L-07's closure evidence reads "CI **rejects** generation failure or drift" — reporting is not rejecting, and the item is not closed until this lands. The script's own header already diagnosed the omission: it listed `Docs & Ledger Consistency` under "deliberately NOT pinned" with the note that it "looks like an oversight from the 2026-08-25 pass rather than a decision". That entry is now wrong in the other direction, so it moves out of the not-pinned list and into a dated note beside `Repository Hygiene`'s. The name was read off **PR #1418's live check runs** after the job reported `success` — not copied out of `ci.yml`. That order is B1-3's rule and it is not pedantry: the B0 script records that three of the pinned names exist in no workflow file at all, because CodeQL runs from GitHub default setup. Two count claims move with it. `docs/contributing.md` said "All ten required checks" and the HP-0 scorecard's table said **10**, both stale since B1-3 added `Repository Hygiene` and now doubly so. B1-5 spotted the first and deferred it to "the branch-protection item's to fix"; this is that item, and it is also the commit that changes the number, so leaving them stale here would make this commit the proximate cause of a documented inconsistency. The scorecard is in `check-doc-counts.mjs`'s watched set — the repository classifies it as active, not as a frozen snapshot — so the don't-edit-dated-docs rule does not shield it. Its pinned block gains both names and a line recording when each was added. NOT APPLIED YET. Running this script is `gh api -X PUT repos/J3vb/OwnCord/branches/dev/protection`, a repository-settings write this session cannot perform. Run `bash docs/plans/b0-dev-branch-protection.sh` after this PR merges. The pre-flight is clear, stated positively rather than assumed: a required check that never reports blocks every PR forever, which is the hazard B1-3's own NOT-APPLIED note was about. It does not apply here. `Docs & Ledger Consistency` has existed in `dev`'s `ci.yml` since #1412, so no in-flight branch predates the job, and it reported `success` on this PR in 11 seconds. Verified: `bash -n` and `shellcheck` are clean. Extracting the heredoc and parsing it with `node` reports **12** contexts including `Docs & Ledger Consistency`, spelled exactly as the live check reports it — the JSON is machine-checked rather than eyeballed, because a typo here is a branch that cannot merge. `node scripts/check-doc-counts.mjs` still agrees on 21 claims across 8 watched documents, the scorecard among them, and `npm run check:hygiene` passes with prettier, shellcheck and actionlint all running. Not included: the script is not run — that is the owner's step, above. No other context is added or removed; the four remaining "deliberately NOT pinned" entries keep their recorded reasons, including `Admin Panel E2E`, whose `continue-on-error: true` still makes requiring it theatre until `R-01` graduates it. Refs RL-07, L-07, RL-14, G-03 --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -121,9 +121,16 @@ candidate counts make an anomalously empty lens visible after the fact.
|
|||||||
|
|
||||||
## 2. Gate (human)
|
## 2. Gate (human)
|
||||||
|
|
||||||
Read `.superpowers/FINDINGS.md`. Mark anything you do not want fixed as
|
Generate the readable rendering, then read it — it is gitignored, so a fresh
|
||||||
`declined` with a rationale — declined findings are fed back into the next hunt's
|
clone has no copy until you make one:
|
||||||
prompts and never re-reported.
|
|
||||||
|
```bash
|
||||||
|
node .superpowers/render-ledger.mjs # writes .superpowers/FINDINGS.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Mark anything you do not want fixed as `declined` with a rationale — declined
|
||||||
|
findings are fed back into the next hunt's prompts and never re-reported. Edit
|
||||||
|
`findings-ledger.json` to do that, not the rendering.
|
||||||
|
|
||||||
## 3. Fix
|
## 3. Fix
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,30 @@ without it — 192 files / 5257 tests, identical to the flagged run.
|
|||||||
|
|
||||||
`npm audit --audit-level=high` and `knip` also run in CI but are advisory.
|
`npm audit --audit-level=high` and `knip` also run in CI but are advisory.
|
||||||
|
|
||||||
|
## Docs and ledger (from the repository root)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run check:docs
|
||||||
|
```
|
||||||
|
|
||||||
|
Which is `scripts/check-doc-counts.mjs` plus, since B1-6, an actual render of
|
||||||
|
the findings ledger:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node .superpowers/render-ledger.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
`.superpowers/FINDINGS.md` is **not tracked** — it is generated on demand and
|
||||||
|
gitignored, so there is no committed rendering to go stale. The gate is that
|
||||||
|
generation succeeds. Rendering subsumes `--check`: the renderer validates and
|
||||||
|
exits 1 before it writes, so a schema break (including an unranked `severity`)
|
||||||
|
fails here.
|
||||||
|
|
||||||
|
CI does one thing more, in `Docs & Ledger Consistency` — it renders **twice**
|
||||||
|
and compares, proving the output is a pure function of the ledger, then uploads
|
||||||
|
the rendering as the `findings-ledger-rendering` artifact so a reviewer can read
|
||||||
|
it without running Node.
|
||||||
|
|
||||||
## Hygiene (from the repository root)
|
## Hygiene (from the repository root)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -8,5 +8,3 @@
|
|||||||
*.wasm binary
|
*.wasm binary
|
||||||
*.exe binary
|
*.exe binary
|
||||||
|
|
||||||
# Rendered from findings-ledger.json by render-ledger.mjs — never hand-edit.
|
|
||||||
.superpowers/FINDINGS.md linguist-generated=true
|
|
||||||
|
|||||||
@@ -60,6 +60,20 @@ if printf '%s\n' "$staged" | grep -qE '^(protocol/schema\.json|Server/cmd/genpro
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Findings ledger changed -> it must still be valid. Unlike the two blocks
|
||||||
|
# above there is nothing to diff: FINDINGS.md is not tracked (RL-07), so a
|
||||||
|
# stale rendering cannot be committed. --check is the whole gate here, and it
|
||||||
|
# writes nothing. render-ledger.mjs is Node-stdlib-only, so `node` alone is the
|
||||||
|
# probe — no node_modules guard, unlike the prettier block below.
|
||||||
|
if printf '%s\n' "$staged" | grep -qE '^\.superpowers/(findings-ledger\.json|render-ledger\.mjs)$'; then
|
||||||
|
if command -v node >/dev/null 2>&1; then
|
||||||
|
node .superpowers/render-ledger.mjs --check \
|
||||||
|
|| fail "findings-ledger.json is invalid — see the INVALID lines above"
|
||||||
|
else
|
||||||
|
printf 'pre-commit: WARNING: node not installed; skipping the ledger check. CI will run it.\n' >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# ---------- Formatting (repository-wide) ----------
|
# ---------- Formatting (repository-wide) ----------
|
||||||
# Prettier is configured once at the repository root (.prettierrc.json) and
|
# Prettier is configured once at the repository root (.prettierrc.json) and
|
||||||
# covers every material tracked source, not just client TypeScript.
|
# covers every material tracked source, not just client TypeScript.
|
||||||
|
|||||||
@@ -193,6 +193,35 @@ jobs:
|
|||||||
- name: Ledger schema is valid
|
- name: Ledger schema is valid
|
||||||
run: node .superpowers/render-ledger.mjs --check
|
run: node .superpowers/render-ledger.mjs --check
|
||||||
|
|
||||||
|
# RL-07. FINDINGS.md is not tracked, so it cannot drift -- but L-07 also
|
||||||
|
# asks that the rendering be reproducible and that CI reject a generation
|
||||||
|
# failure. Rendering twice and comparing tests both: the render must
|
||||||
|
# succeed (it validates and exits 1 before writing), and it must be a pure
|
||||||
|
# function of the ledger. The severity rule in validate() is what makes
|
||||||
|
# the second half true -- an unranked severity would make render()'s sort
|
||||||
|
# implementation-defined.
|
||||||
|
- name: FINDINGS.md renders, and renders identically twice
|
||||||
|
run: |
|
||||||
|
node .superpowers/render-ledger.mjs
|
||||||
|
cp .superpowers/FINDINGS.md "$RUNNER_TEMP/FINDINGS.first.md"
|
||||||
|
node .superpowers/render-ledger.mjs
|
||||||
|
cmp "$RUNNER_TEMP/FINDINGS.first.md" .superpowers/FINDINGS.md || {
|
||||||
|
echo "ERROR: rendering the ledger twice produced different output."
|
||||||
|
echo "render() must be a pure function of findings-ledger.json."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# The rendering is the human-readable view and is deliberately untracked,
|
||||||
|
# so this artifact is how a reviewer reads it without a Node run.
|
||||||
|
# if: always() -- you want it downloadable precisely when the job failed.
|
||||||
|
- name: Upload the rendered findings ledger
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
|
with:
|
||||||
|
name: findings-ledger-rendering
|
||||||
|
path: .superpowers/FINDINGS.md
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
# Repository-wide formatting, script lint and workflow lint (RL-19 / L-13, S-05).
|
# Repository-wide formatting, script lint and workflow lint (RL-19 / L-13, S-05).
|
||||||
#
|
#
|
||||||
# Root-scoped and ubuntu-only for the same reason as docs-consistency above:
|
# Root-scoped and ubuntu-only for the same reason as docs-consistency above:
|
||||||
|
|||||||
+23
-5
@@ -47,6 +47,17 @@ Server/server.exe
|
|||||||
Server/config.yaml
|
Server/config.yaml
|
||||||
Server/data/
|
Server/data/
|
||||||
|
|
||||||
|
# Prebuilt plugin example (RL-08). Built from the main.go beside it with the
|
||||||
|
# TinyGo toolchain that directory's README pins. Read by nothing in the build
|
||||||
|
# or test graph, and not byte-reproducible on another machine: TinyGo embeds
|
||||||
|
# absolute host paths from the building machine's Go SDK and module cache, and
|
||||||
|
# has no -trimpath equivalent.
|
||||||
|
#
|
||||||
|
# Deliberately NOT a blanket *.wasm rule. Client/public/rnnoise.wasm is a
|
||||||
|
# vendored npm artifact this repository does not build and the client fetches
|
||||||
|
# at runtime; ignoring it would break voice noise suppression.
|
||||||
|
Server/plugin/examples/hello/hello.wasm
|
||||||
|
|
||||||
# Test coverage artifacts
|
# Test coverage artifacts
|
||||||
*.out
|
*.out
|
||||||
Server/cov.out
|
Server/cov.out
|
||||||
@@ -76,12 +87,16 @@ node_modules/
|
|||||||
.claude-flow/
|
.claude-flow/
|
||||||
.rust-review-results/
|
.rust-review-results/
|
||||||
|
|
||||||
# Bug-hunt ledger: shared so contributors can add findings. Only the ledger,
|
# Bug-hunt ledger: shared so contributors can add findings. Only the ledger and
|
||||||
# its render and its validator are tracked; hunt transcripts, .bak snapshots
|
# its renderer are tracked; hunt transcripts, .bak snapshots and debris patches
|
||||||
# and debris patches are per-session scratch and stay local.
|
# are per-session scratch and stay local.
|
||||||
|
#
|
||||||
|
# FINDINGS.md is deliberately NOT tracked (RL-07): it is 100% derived from
|
||||||
|
# findings-ledger.json, and every hunt would otherwise write a fresh ~1.06 MB
|
||||||
|
# blob into permanent history for a file a reader can regenerate in under a
|
||||||
|
# second with `node .superpowers/render-ledger.mjs`.
|
||||||
.superpowers/*
|
.superpowers/*
|
||||||
!.superpowers/findings-ledger.json
|
!.superpowers/findings-ledger.json
|
||||||
!.superpowers/FINDINGS.md
|
|
||||||
!.superpowers/render-ledger.mjs
|
!.superpowers/render-ledger.mjs
|
||||||
|
|
||||||
.claude/worktrees/
|
.claude/worktrees/
|
||||||
@@ -114,5 +129,8 @@ Client/.env
|
|||||||
# local server run logs
|
# local server run logs
|
||||||
server.log
|
server.log
|
||||||
|
|
||||||
# Knowledge graph output (tool removed 2026-08-26; delete the dir when convenient).
|
# Knowledge-graph output. The tool and its 20.41 MB tracked payload were removed
|
||||||
|
# in a5f7d95 (#1413, RL-06). The rule stays so a machine that still has the local
|
||||||
|
# directory — it reached ~208 MB with cache and dated snapshots — does not see it
|
||||||
|
# as untracked noise.
|
||||||
graphify-out/
|
graphify-out/
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
Server/db/dbgen/
|
Server/db/dbgen/
|
||||||
Client/src/lib/protocolTypes.ts
|
Client/src/lib/protocolTypes.ts
|
||||||
|
|
||||||
# Rendered from findings-ledger.json by render-ledger.mjs.
|
|
||||||
.superpowers/FINDINGS.md
|
|
||||||
|
|
||||||
# Dated point-in-time snapshots. scripts/check-doc-counts.mjs already treats
|
# Dated point-in-time snapshots. scripts/check-doc-counts.mjs already treats
|
||||||
# these as deliberately unmaintained and out of scope to edit; reformatting
|
# these as deliberately unmaintained and out of scope to edit; reformatting
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,13 @@ import assert from "node:assert/strict";
|
|||||||
|
|
||||||
const VALID_STATUS = ["open", "fixed", "declined", "refuted", "duplicate", "blocked"];
|
const VALID_STATUS = ["open", "fixed", "declined", "refuted", "duplicate", "blocked"];
|
||||||
|
|
||||||
|
// Must stay in lockstep with SEV_RANK below. render() sorts the open section by
|
||||||
|
// SEV_RANK, and an unranked severity makes the comparator return NaN — which
|
||||||
|
// leaves the sort order implementation-defined, so the rendering would stop
|
||||||
|
// being a pure function of the ledger. The drift gate compares the rendering
|
||||||
|
// against the ledger, so its whole premise rests on this being enforced.
|
||||||
|
const VALID_SEVERITY = ["critical", "high", "medium", "low"];
|
||||||
|
|
||||||
export function validate(ledger) {
|
export function validate(ledger) {
|
||||||
const problems = [];
|
const problems = [];
|
||||||
const ids = new Set();
|
const ids = new Set();
|
||||||
@@ -14,6 +21,7 @@ export function validate(ledger) {
|
|||||||
ids.add(r.id);
|
ids.add(r.id);
|
||||||
if (!/^OC-\d{4}$/.test(r.id)) problems.push(`${r.id}: malformed id`);
|
if (!/^OC-\d{4}$/.test(r.id)) problems.push(`${r.id}: malformed id`);
|
||||||
if (!VALID_STATUS.includes(r.status)) problems.push(`${r.id}: bad status ${r.status}`);
|
if (!VALID_STATUS.includes(r.status)) problems.push(`${r.id}: bad status ${r.status}`);
|
||||||
|
if (!VALID_SEVERITY.includes(r.severity)) problems.push(`${r.id}: bad severity ${r.severity}`);
|
||||||
if (r.status === "fixed" && (!r.fix || !r.fix.commit))
|
if (r.status === "fixed" && (!r.fix || !r.fix.commit))
|
||||||
problems.push(`${r.id}: fixed without a commit`);
|
problems.push(`${r.id}: fixed without a commit`);
|
||||||
if (r.status === "declined" && !r.rationale)
|
if (r.status === "declined" && !r.rationale)
|
||||||
@@ -25,23 +33,44 @@ export function validate(ledger) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function selftest() {
|
function selftest() {
|
||||||
|
// Every fixture carries a severity: validate() now requires one, so omitting
|
||||||
|
// it would make each case report two problems and assert against the wrong one.
|
||||||
assert.deepEqual(validate({ findings: [] }), []);
|
assert.deepEqual(validate({ findings: [] }), []);
|
||||||
assert.deepEqual(validate({ findings: [{ id: "OC-0001", status: "fixed", fix: null }] }), [
|
assert.deepEqual(
|
||||||
"OC-0001: fixed without a commit",
|
validate({ findings: [{ id: "OC-0001", severity: "low", status: "fixed", fix: null }] }),
|
||||||
|
["OC-0001: fixed without a commit"],
|
||||||
|
);
|
||||||
|
assert.deepEqual(validate({ findings: [{ id: "bad", severity: "low", status: "open" }] }), [
|
||||||
|
"bad: malformed id",
|
||||||
]);
|
]);
|
||||||
assert.deepEqual(validate({ findings: [{ id: "bad", status: "open" }] }), ["bad: malformed id"]);
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
validate({
|
validate({
|
||||||
findings: [
|
findings: [
|
||||||
{ id: "OC-0001", status: "open" },
|
{ id: "OC-0001", severity: "low", status: "open" },
|
||||||
{ id: "OC-0001", status: "open" },
|
{ id: "OC-0001", severity: "low", status: "open" },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
["duplicate id OC-0001"],
|
["duplicate id OC-0001"],
|
||||||
);
|
);
|
||||||
assert.deepEqual(validate({ findings: [{ id: "OC-0002", status: "declined" }] }), [
|
assert.deepEqual(
|
||||||
"OC-0002: declined without a rationale",
|
validate({ findings: [{ id: "OC-0002", severity: "low", status: "declined" }] }),
|
||||||
|
["OC-0002: declined without a rationale"],
|
||||||
|
);
|
||||||
|
|
||||||
|
// An unranked severity is what makes render()'s sort implementation-defined.
|
||||||
|
assert.deepEqual(
|
||||||
|
validate({ findings: [{ id: "OC-0003", severity: "moderate", status: "open" }] }),
|
||||||
|
["OC-0003: bad severity moderate"],
|
||||||
|
);
|
||||||
|
assert.deepEqual(validate({ findings: [{ id: "OC-0004", status: "open" }] }), [
|
||||||
|
"OC-0004: bad severity undefined",
|
||||||
]);
|
]);
|
||||||
|
for (const sev of VALID_SEVERITY) {
|
||||||
|
assert.deepEqual(
|
||||||
|
validate({ findings: [{ id: "OC-0005", severity: sev, status: "open" }] }),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
}
|
||||||
console.log("selftest: all assertions pass");
|
console.log("selftest: all assertions pass");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,17 +19,21 @@ CI fails on drift, and the next generator run silently discards your edit.
|
|||||||
|
|
||||||
## Bug-hunt ledger
|
## Bug-hunt ledger
|
||||||
|
|
||||||
`.superpowers/findings-ledger.json` is the shared ledger of hunt findings —
|
`.superpowers/findings-ledger.json` is the shared ledger of hunt findings and
|
||||||
open a PR against it to add one. `FINDINGS.md` is rendered from it:
|
the only tracked copy — open a PR against it to add one. The readable
|
||||||
|
`FINDINGS.md` is **not tracked**: generate it whenever you want to read one
|
||||||
|
(gitignored, under a second, and CI uploads it as a build artifact):
|
||||||
|
|
||||||
```
|
```
|
||||||
node .superpowers/render-ledger.mjs # rewrite FINDINGS.md
|
node .superpowers/render-ledger.mjs # write a local FINDINGS.md
|
||||||
node .superpowers/render-ledger.mjs --check # validate the ledger only
|
node .superpowers/render-ledger.mjs --check # validate the ledger only
|
||||||
```
|
```
|
||||||
|
|
||||||
Statuses: `open`, `fixed`, `declined`, `refuted`, `duplicate`, `blocked`.
|
Statuses: `open`, `fixed`, `declined`, `refuted`, `duplicate`, `blocked`;
|
||||||
Never edit `FINDINGS.md` by hand — edit the ledger and re-render. Everything
|
`severity` must be `critical`, `high`, `medium` or `low`. Edit the ledger, never
|
||||||
else under `.superpowers/` is per-session scratch and stays local.
|
the rendering — a hand-edited `FINDINGS.md` is overwritten by the next render
|
||||||
|
and committed by nothing. Everything else under `.superpowers/` is per-session
|
||||||
|
scratch and stays local.
|
||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ config.yaml
|
|||||||
scripts/
|
scripts/
|
||||||
cmd/
|
cmd/
|
||||||
|
|
||||||
|
# Plugin examples: source for a disabled subsystem, plus a prebuilt .wasm a
|
||||||
|
# developer may still have on disk untracked. Nothing in the image reads either.
|
||||||
|
plugin/examples/
|
||||||
|
|
||||||
# Git metadata
|
# Git metadata
|
||||||
.git
|
.git
|
||||||
.gitignore
|
.gitignore
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
# hello plugin
|
# hello plugin
|
||||||
|
|
||||||
Phase C Step 9 — proof-of-life plugin used by `Server/plugin/plugin_test.go`.
|
Phase C Step 9 — a worked reference implementation of the plugin ABI, and the
|
||||||
|
manifest the loader parses. Source only: the `.wasm` is not checked in.
|
||||||
|
|
||||||
|
> **The plugin ABI is experimental and carries no compatibility promise.** The
|
||||||
|
> subsystem is disabled twice over — it compiles only under `-tags wazero`
|
||||||
|
> (`Server/plugin/sandbox_default.go`), and `plugins.enabled` defaults to
|
||||||
|
> `false` (`Server/config/config.go`) — and the five exported functions may
|
||||||
|
> change or be removed in any release without a deprecation period.
|
||||||
|
|
||||||
## Manifest
|
## Manifest
|
||||||
|
|
||||||
@@ -17,15 +24,24 @@ the WASM but not the manifest simply never binds.
|
|||||||
|
|
||||||
`main.go` in this directory implements the full plugin ABI
|
`main.go` in this directory implements the full plugin ABI
|
||||||
(`allocate`, `deallocate`, `list_commands`, `command_dispatch`, `on_event`).
|
(`allocate`, `deallocate`, `list_commands`, `command_dispatch`, `on_event`).
|
||||||
The pre-built `hello.wasm` (925 KiB) is checked in, but you can rebuild it:
|
Build it before pointing a plugin directory at this example — `hello.wasm` is
|
||||||
|
gitignored and produced locally, not distributed.
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
| Tool | Version | Notes |
|
This table is the single source of truth for the plugin toolchain;
|
||||||
| -------- | ------------ | ---------------------------------------- |
|
`docs/contributing.md` links here rather than repeating it.
|
||||||
| TinyGo | 0.40.1 | Supports Go 1.19–1.25 only |
|
|
||||||
| Go | 1.25.x | TinyGo 0.40.1 rejects Go 1.26+ |
|
| Tool | Version | Notes |
|
||||||
| wasm-opt | Binaryen 129 | Required by TinyGo for the `wasi` target |
|
| -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| TinyGo | 0.40.1 | Supports Go 1.19–1.25 only |
|
||||||
|
| Go | 1.25.x | TinyGo 0.40.1 rejects Go 1.26+. Install alongside the system Go: `go install golang.org/dl/go1.25.3@latest && go1.25.3 download` |
|
||||||
|
| wasm-opt | Binaryen 129 | Required by TinyGo for the `wasi` target; download from Binaryen GitHub releases |
|
||||||
|
|
||||||
|
Note the Go row: this repository's own module is pinned to Go 1.26
|
||||||
|
(`Server/go.mod`), so building a plugin needs a _second_, older Go SDK
|
||||||
|
side-installed. That conflict is why there is no CI job for this — see
|
||||||
|
Provenance.
|
||||||
|
|
||||||
On Windows, extract TinyGo to e.g. `D:\Local-Lab\Coding\Software\tinygo` and
|
On Windows, extract TinyGo to e.g. `D:\Local-Lab\Coding\Software\tinygo` and
|
||||||
add `<tinygo>\bin` plus `<binaryen>\bin` to `PATH`. Then point TinyGo at the
|
add `<tinygo>\bin` plus `<binaryen>\bin` to `PATH`. Then point TinyGo at the
|
||||||
@@ -43,8 +59,35 @@ $env:PATH = "$env:GOROOT\bin;$env:PATH"
|
|||||||
tinygo build -o hello.wasm -target wasi ./main.go
|
tinygo build -o hello.wasm -target wasi ./main.go
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Any WASM toolchain (Rust/`wasm32-wasi`, AssemblyScript, …) that exports the five
|
||||||
|
ABI functions is equally valid. TinyGo is just the toolchain this example uses.
|
||||||
|
|
||||||
|
## Provenance
|
||||||
|
|
||||||
|
The `.wasm` that used to be committed here was produced by **TinyGo 0.40.1 with
|
||||||
|
Go 1.25.3 and Binaryen 129**, on Windows. It was removed in B1-6 (RL-08).
|
||||||
|
|
||||||
|
It is **not byte-reproducible**, and that is why no CI job compiles and compares
|
||||||
|
it: TinyGo embeds absolute host paths from the building machine's Go SDK and
|
||||||
|
module cache into its output, and offers no `-trimpath` equivalent. Two
|
||||||
|
machines building the same source produce different bytes, so a byte-identity
|
||||||
|
gate cannot pass in principle — not merely "is unbuilt". BPR-080 asks that the
|
||||||
|
example WASM be _reproducible or provenance-verified_; this section is the
|
||||||
|
second branch.
|
||||||
|
|
||||||
|
A compile-only drift check would still be possible, but it needs three pinned
|
||||||
|
downloads (TinyGo, a second Go SDK, Binaryen) on every PR for a subsystem that
|
||||||
|
is disabled at compile time _and_ at runtime. That is deferred to B2, which the
|
||||||
|
issue register already names as L-08's second phase.
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
`Server/plugin/plugin_test.go` exercises the manifest parser and the loader
|
Nothing in the Go build or test graph reads this directory. `main.go` carries
|
||||||
against this directory. It does not require the .wasm to be present —
|
`//go:build tinygo`, so `go list ./plugin/...` returns one package with and
|
||||||
manifest-only validation is the default-build coverage path.
|
without `-tags wazero`, and `examples/hello` is not in it.
|
||||||
|
|
||||||
|
The plugin tests build their own fixtures instead: `plugin_test.go` writes
|
||||||
|
manifests and stub bytes into `t.TempDir()`, and `sandbox_wazero_test.go` uses a
|
||||||
|
41-byte inline WASM literal — with the comment _"Using a literal here avoids
|
||||||
|
dragging a binary asset into the repo."_ That call was already made for the test
|
||||||
|
suite; RL-08 extends it to the example.
|
||||||
|
|||||||
Binary file not shown.
+32
-23
@@ -26,18 +26,18 @@ From the repository root. These orchestrate the per-stack commands below; they
|
|||||||
are a convenience, not a replacement. Nothing here needs `make`, and everything
|
are a convenience, not a replacement. Nothing here needs `make`, and everything
|
||||||
works the same on Windows, macOS and Linux.
|
works the same on Windows, macOS and Linux.
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
| ----------------------------- | ------------------------------------------------------------------------------- |
|
| ----------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||||
| `npm run bootstrap` | `npm ci` in all three package roots |
|
| `npm run bootstrap` | `npm ci` in all three package roots |
|
||||||
| `npm run check` | Everything CI gates on: server, client, Rust |
|
| `npm run check` | Everything CI gates on: server, client, Rust |
|
||||||
| `npm run check:server` | Server only — build variants, vet, race, deadlock, lint, generated-output drift |
|
| `npm run check:server` | Server only — build variants, vet, race, deadlock, lint, generated-output drift |
|
||||||
| `npm run check:client` | Client only — typecheck, lint, format, unit + integration tests |
|
| `npm run check:client` | Client only — typecheck, lint, format, unit + integration tests |
|
||||||
| `npm run check:rust` | Tauri backend — `cargo test --lib` and clippy |
|
| `npm run check:rust` | Tauri backend — `cargo test --lib` and clippy |
|
||||||
| `npm run check:docs` | Fail if a watched document states a finding count the ledger contradicts |
|
| `npm run check:docs` | Fail if a watched document contradicts the ledger's finding counts, or the ledger fails to render |
|
||||||
| `npm run format` | Prettier over the client, `gofmt -w` over the server |
|
| `npm run format` | Prettier over the client, `gofmt -w` over the server |
|
||||||
| `npm run generate` | Regenerate protocol constants and the sqlc query layer |
|
| `npm run generate` | Regenerate protocol constants and the sqlc query layer |
|
||||||
| `npm run release:preflight` | `check` plus a client production build |
|
| `npm run release:preflight` | `check` plus a client production build |
|
||||||
| `node scripts/run.mjs --list` | Print the exact command every task runs, and where |
|
| `node scripts/run.mjs --list` | Print the exact command every task runs, and where |
|
||||||
|
|
||||||
Tools CI installs but you may not have — `golangci-lint`, `sqlc` — are skipped
|
Tools CI installs but you may not have — `golangci-lint`, `sqlc` — are skipped
|
||||||
with a printed reason rather than failing the run.
|
with a printed reason rather than failing the run.
|
||||||
@@ -144,18 +144,23 @@ you. Put it under `.githooks/` instead (untracked, so it stays yours), or skip
|
|||||||
## Plugin Development
|
## Plugin Development
|
||||||
|
|
||||||
Plugins are WASM modules loaded at runtime when the server is built with `-tags wazero`.
|
Plugins are WASM modules loaded at runtime when the server is built with `-tags wazero`.
|
||||||
See `Server/plugin/examples/hello/README.md` for the full plugin ABI and build instructions.
|
|
||||||
|
|
||||||
**Toolchain requirements for building `.wasm` plugins with TinyGo:**
|
> **The plugin ABI is experimental and carries no compatibility promise.** The
|
||||||
|
> subsystem is disabled twice over — it compiles only under `-tags wazero`
|
||||||
|
> (`Server/plugin/sandbox_default.go`), and `plugins.enabled` defaults to
|
||||||
|
> `false` (`Server/config/config.go`) — and the five exported functions may
|
||||||
|
> change or be removed in any release without a deprecation period.
|
||||||
|
|
||||||
| Tool | Version | Notes |
|
See [`Server/plugin/examples/hello/README.md`](../Server/plugin/examples/hello/README.md)
|
||||||
| -------- | ------------ | --------------------------------------------------------------------------------------------------- |
|
for the ABI, the build command, and the pinned TinyGo/Go/Binaryen versions. That
|
||||||
| TinyGo | 0.40.1 | Supports Go 1.19–1.25 only |
|
file is the single source of truth for the plugin toolchain — this page used to
|
||||||
| Go SDK | 1.25.x | Install alongside the system Go via `go install golang.org/dl/go1.25.3@latest && go1.25.3 download` |
|
carry a second copy of the version table, and the two had already drifted apart
|
||||||
| wasm-opt | Binaryen 129 | Required by TinyGo for the `wasi` target; download from Binaryen GitHub releases |
|
in wording.
|
||||||
|
|
||||||
Any WASM toolchain (Rust/`wasm32-wasi`, AssemblyScript, etc.) that exports the five ABI
|
The example's `.wasm` is not checked in: TinyGo embeds absolute host paths from
|
||||||
functions is equally valid — TinyGo is just the example toolchain used by `examples/hello/`.
|
the building machine and offers no `-trimpath`, so its output is not
|
||||||
|
byte-reproducible and no CI job can verify it. Build it locally from the source
|
||||||
|
beside it.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -220,7 +225,7 @@ and target.
|
|||||||
|
|
||||||
1. Branch from `dev`
|
1. Branch from `dev`
|
||||||
2. Open the PR against `dev`
|
2. Open the PR against `dev`
|
||||||
3. All ten required checks must pass -- `dev` is protected, so a red PR cannot
|
3. All twelve required checks must pass -- `dev` is protected, so a red PR cannot
|
||||||
merge
|
merge
|
||||||
4. Request code review
|
4. Request code review
|
||||||
5. Squash merge, conventional commit subject
|
5. Squash merge, conventional commit subject
|
||||||
@@ -314,7 +319,11 @@ closing audit findings 2026-04-07 #8 / DC-11):
|
|||||||
`package.json` files say Node 24 — with `engine-strict=true` in each
|
`package.json` files say Node 24 — with `engine-strict=true` in each
|
||||||
package's `.npmrc`, so a wrong major fails the install instead of warning.
|
package's `.npmrc`, so a wrong major fails the install instead of warning.
|
||||||
`Server/sqlc.version` pins sqlc, Go pins via `go.mod` (`GOTOOLCHAIN=auto`),
|
`Server/sqlc.version` pins sqlc, Go pins via `go.mod` (`GOTOOLCHAIN=auto`),
|
||||||
and GitHub Actions are SHA-pinned with Dependabot bumping the pins.
|
and GitHub Actions are SHA-pinned with Dependabot bumping the pins. The one
|
||||||
|
deliberate exception is the plugin toolchain: TinyGo and Binaryen are
|
||||||
|
_documented_ rather than file-pinned, because no gate installs them and
|
||||||
|
nothing would read the pin — the example plugin's README is their single
|
||||||
|
source of truth.
|
||||||
- **Three package roots, not an npm workspace** — measured 2026-08-26 (npm
|
- **Three package roots, not an npm workspace** — measured 2026-08-26 (npm
|
||||||
11.17, Node 26), not decided on principle. Making `/`, `/Client` and
|
11.17, Node 26), not decided on principle. Making `/`, `/Client` and
|
||||||
`/tools/mcp-introspect` npm workspaces buys one 298 KB lockfile instead of
|
`/tools/mcp-introspect` npm workspaces buys one 298 KB lockfile instead of
|
||||||
|
|||||||
@@ -62,11 +62,14 @@ Planning documents are not trackers. Do not read a defect count out of one.
|
|||||||
| Current measured baseline | [b0-baseline-2026-08-25](b0-baseline-2026-08-25.md) |
|
| Current measured baseline | [b0-baseline-2026-08-25](b0-baseline-2026-08-25.md) |
|
||||||
|
|
||||||
Ledger at 2026-08-25: **306 fixed / 38 open / 3 declined / 1 duplicate = 348**.
|
Ledger at 2026-08-25: **306 fixed / 38 open / 3 declined / 1 duplicate = 348**.
|
||||||
All 38 open records still resolve to a live `file:line` at
|
All 38 open records still resolved to a live `file:line` at
|
||||||
`5cc0888964e26276d1aca145e83270a2c1b9febd` — none is stale. Verify with:
|
`5cc0888964e26276d1aca145e83270a2c1b9febd` when that sweep was run — it was a
|
||||||
|
manual pass, not something a command reproduces. What the tooling does check:
|
||||||
|
|
||||||
```
|
```
|
||||||
node .superpowers/render-ledger.mjs --check
|
node .superpowers/render-ledger.mjs --check # the ledger's schema is valid
|
||||||
|
node scripts/check-doc-counts.mjs # documents agree with it, and
|
||||||
|
# FINDINGS.md is not stale
|
||||||
```
|
```
|
||||||
|
|
||||||
## Adding a plan
|
## Adding a plan
|
||||||
|
|||||||
@@ -29,6 +29,12 @@
|
|||||||
# This is the half of S-05 that makes the gate a gate -- a check that is
|
# This is the half of S-05 that makes the gate a gate -- a check that is
|
||||||
# present but unpinned lets a formatting regression merge.
|
# present but unpinned lets a formatting regression merge.
|
||||||
#
|
#
|
||||||
|
# Docs & Ledger Consistency added 2026-08-27 (B1-6). Name read off PR #1418
|
||||||
|
# after the job reported `success`. Unpinned it was harmless while the job
|
||||||
|
# only validated the ledger's JSON schema. B1-6 made it also reject a
|
||||||
|
# ledger that fails to render, and L-07's closure evidence is "CI rejects
|
||||||
|
# generation failure or drift" -- rejecting requires pinning.
|
||||||
|
#
|
||||||
# Deliberately NOT pinned, and why:
|
# Deliberately NOT pinned, and why:
|
||||||
# Server Docker Build (verify) reports "skipping" on a dev PR
|
# Server Docker Build (verify) reports "skipping" on a dev PR
|
||||||
# (if: ref_name=='main' || base_ref=='main')
|
# (if: ref_name=='main' || base_ref=='main')
|
||||||
@@ -40,10 +46,6 @@
|
|||||||
# theatre (that is R-01, B10 work)
|
# theatre (that is R-01, B10 work)
|
||||||
# CodeQL default-setup aggregate over the three
|
# CodeQL default-setup aggregate over the three
|
||||||
# Analyze jobs; pinning those is enough
|
# Analyze jobs; pinning those is enough
|
||||||
# Docs & Ledger Consistency reports and passes on a dev PR, and is NOT
|
|
||||||
# pinned. That looks like an oversight from
|
|
||||||
# the 2026-08-25 pass rather than a decision;
|
|
||||||
# it belongs to G-04, not to B1-3.
|
|
||||||
#
|
#
|
||||||
# A required check that never reports blocks every PR forever. Re-read the
|
# A required check that never reports blocks every PR forever. Re-read the
|
||||||
# list before changing it:
|
# list before changing it:
|
||||||
@@ -66,6 +68,7 @@ gh api -X PUT "repos/${REPO}/branches/dev/protection" --input - <<'JSON'
|
|||||||
"Client Unit Tests",
|
"Client Unit Tests",
|
||||||
"Rust Unit Tests",
|
"Rust Unit Tests",
|
||||||
"Repository Hygiene",
|
"Repository Hygiene",
|
||||||
|
"Docs & Ledger Consistency",
|
||||||
"Client E2E (Playwright)",
|
"Client E2E (Playwright)",
|
||||||
"Client E2E (parity subset, blocking)",
|
"Client E2E (parity subset, blocking)",
|
||||||
"Analyze (go)",
|
"Analyze (go)",
|
||||||
|
|||||||
@@ -3,8 +3,9 @@
|
|||||||
**Drafted:** 2026-08-25
|
**Drafted:** 2026-08-25
|
||||||
**Base commit:** `6a1561fa` (`dev`, post-PR #1409)
|
**Base commit:** `6a1561fa` (`dev`, post-PR #1409)
|
||||||
**Status:** in progress; **entry gate met — HP-0 accepted 2026-08-25**. B1-0
|
**Status:** in progress; **entry gate met — HP-0 accepted 2026-08-25**. B1-0
|
||||||
(#1410), B1-1 (#1411), B1-2 (#1412) and B1-3 are complete; B1-4 is the next
|
(#1410), B1-1 (#1411), B1-2 (#1412), B1-3 (#1414), B1-4 (#1415), B1-5 (#1417)
|
||||||
step.
|
and B1-6 (this branch) are complete; B1-7, community intake and automation
|
||||||
|
authorization, is the next step.
|
||||||
|
|
||||||
Primary inputs:
|
Primary inputs:
|
||||||
|
|
||||||
@@ -136,9 +137,9 @@ that changes the work.
|
|||||||
| **RL-01** release names | **Safe to move** | `productName: "OwnCord"`, `identifier: "com.owncord.client"`, crate `owncord-client`, lib `owncord_client_lib` — none derived from the directory. `updater.endpoints` is `[]` (server-mediated via `Server/api/client_update.go`). Every release staging step globs by **filename suffix**, matched server-side by `Server/updater/assets.go`. **The move cannot rename a release asset.** |
|
| **RL-01** release names | **Safe to move** | `productName: "OwnCord"`, `identifier: "com.owncord.client"`, crate `owncord-client`, lib `owncord_client_lib` — none derived from the directory. `updater.endpoints` is `[]` (server-mediated via `Server/api/client_update.go`). Every release staging step globs by **filename suffix**, matched server-side by `Server/updater/assets.go`. **The move cannot rename a release asset.** |
|
||||||
| **RL-09** "no one command verifies both consumers" | **Sub-claim refuted** | `make protocol-verify` already regenerates _and_ diffs both outputs, and it is enforced three times over: `ci.yml`, `.githooks/pre-commit`, and `Server/ws/protocol_contract_test.go`. Only the schema's _location_ is a real finding. Scope shrinks to a relocation. |
|
| **RL-09** "no one command verifies both consumers" | **Sub-claim refuted** | `make protocol-verify` already regenerates _and_ diffs both outputs, and it is enforced three times over: `ci.yml`, `.githooks/pre-commit`, and `Server/ws/protocol_contract_test.go`. Only the schema's _location_ is a real finding. Scope shrinks to a relocation. |
|
||||||
| **RL-10** "`init()` creates a data dir during test discovery" | **Alarming half refuted** | `Server/scripts/` contains zero `_test.go` files, so Go never builds a test binary there and `init()` never fires under `go test ./...`. `seed.go` does `os.MkdirAll("data", 0o750)`, but only when the binary is run — and `.gitignore` already ignores `Server/data/`. Residual finding is narrow: an untagged `package main` in the main module's build graph. |
|
| **RL-10** "`init()` creates a data dir during test discovery" | **Alarming half refuted** | `Server/scripts/` contains zero `_test.go` files, so Go never builds a test binary there and `init()` never fires under `go test ./...`. `seed.go` does `os.MkdirAll("data", 0o750)`, but only when the binary is run — and `.gitignore` already ignores `Server/data/`. Residual finding is narrow: an untagged `package main` in the main module's build graph. |
|
||||||
| **RL-06** "regeneration not demonstrated" | **Refuted locally** | `graphify` 0.9.41 is installed and on PATH. Tracked payload is **20.41 MB**, `graph.json` **19.46 MB**. Note `du -sh graphify-out/` reports 208 MB — that is gitignored dated snapshots plus `cache/`, not repo weight. Linux/CI portability remains unproven. |
|
| **RL-06** "regeneration not demonstrated" | **Closed by deletion** | Superseded before B1-6 opened: `a5f7d95` (#1413) removed the tool and all 7 tracked files — **20,408,656 bytes**, `graph.json` **19,463,420**. `git ls-files` now matches nothing. The outcome RL-06 wanted (no large tracked payload, history intact) holds; the method it prescribed (regenerate-then-untrack) was bypassed. Portability is moot — there is nothing to regenerate. |
|
||||||
| **RL-08** "committed without its source" | **Half refuted** | The source _is_ committed (`Server/plugin/examples/hello/main.go`, `//go:build tinygo`). Only the gate is missing. **New constraint:** pinned TinyGo 0.40.1 rejects Go 1.26, so a compile-and-compare CI job needs a second Go SDK. L-08 is harder than the audit implies. |
|
| **RL-08** "committed without its source" | **Half refuted** | The source _is_ committed (`Server/plugin/examples/hello/main.go`, `//go:build tinygo`). Only the gate is missing. **New constraint:** pinned TinyGo 0.40.1 rejects Go 1.26, so a compile-and-compare CI job needs a second Go SDK. L-08 is harder than the audit implies. |
|
||||||
| **RL-07** FINDINGS.md duplication | **Confirmed, sharper** | `render-ledger.mjs --check` validates the JSON schema and returns **before** rendering, so it cannot detect drift at all; a stale 1.09 MB `FINDINGS.md` passes cleanly. And no workflow runs it. |
|
| **RL-07** FINDINGS.md duplication | **Confirmed, sharper** | `render-ledger.mjs --check` validates the JSON schema and returns **before** rendering, so it cannot detect drift at all; a stale 1.09 MB `FINDINGS.md` passes cleanly. B1-2 (#1412) wired that schema-only check into the `Docs & Ledger Consistency` job, which is why the original "no workflow runs it" no longer holds — the job runs, it just cannot see drift. B1-6 adds the check that can. |
|
||||||
| **RL-20** hooks | **Confirmed, plus an unreported bug** | `make` is not on PATH on a normal Windows contributor box, yet the `ci-check` skill lists `make sqlc-verify protocol-verify` as required. Worse: `.githooks/pre-commit`'s protocol branch guards on `command -v go`, not `make` — so Go-without-`make` yields a false **"protocol constants are stale"** hard failure. Separately, `core.hooksPath` may be unset (so `.githooks/` never runs) while a local `post-commit` does; `npm run hooks:install` redirects `core.hooksPath` and silently disables any `.git/hooks/post-commit`. |
|
| **RL-20** hooks | **Confirmed, plus an unreported bug** | `make` is not on PATH on a normal Windows contributor box, yet the `ci-check` skill lists `make sqlc-verify protocol-verify` as required. Worse: `.githooks/pre-commit`'s protocol branch guards on `command -v go`, not `make` — so Go-without-`make` yields a false **"protocol constants are stale"** hard failure. Separately, `core.hooksPath` may be unset (so `.githooks/` never runs) while a local `post-commit` does; `npm run hooks:install` redirects `core.hooksPath` and silently disables any `.git/hooks/post-commit`. |
|
||||||
| **RL-05** package roots | **Confirmed, wider** | Three JS roots, no workspaces. `dependabot.yml` covers npm for one of them — root and `tools/mcp-introspect` are uncovered — and omits the **`docker` ecosystem entirely** despite three Docker files. |
|
| **RL-05** package roots | **Confirmed, wider** | Three JS roots, no workspaces. `dependabot.yml` covers npm for one of them — root and `tools/mcp-introspect` are uncovered — and omits the **`docker` ecosystem entirely** despite three Docker files. |
|
||||||
| **RL-11** cross-stack test | **Confirmed; both directions exist** | Client→Server: `tests/unit/admin-static-channel-perms.test.ts` plus three e2e siblings (`playwright.config.admin.ts`, `tests/e2e/admin/admin-panel.spec.ts`, `tests/e2e/admin/start-server.sh`). Server→Client: `Server/updater/updater_test.go` does `os.ReadFile` on the client's `tauri.conf.json`. Also `Server/ws/protocol_contract_test.go` reads `docs/protocol-schema.json`. A sweep reporting "no server→client reads" is wrong. |
|
| **RL-11** cross-stack test | **Confirmed; both directions exist** | Client→Server: `tests/unit/admin-static-channel-perms.test.ts` plus three e2e siblings (`playwright.config.admin.ts`, `tests/e2e/admin/admin-panel.spec.ts`, `tests/e2e/admin/start-server.sh`). Server→Client: `Server/updater/updater_test.go` does `os.ReadFile` on the client's `tauri.conf.json`. Also `Server/ws/protocol_contract_test.go` reads `docs/protocol-schema.json`. A sweep reporting "no server→client reads" is wrong. |
|
||||||
@@ -172,15 +173,9 @@ else in the diff.
|
|||||||
|
|
||||||
### Step 1 — freeze the environment
|
### Step 1 — freeze the environment
|
||||||
|
|
||||||
```bash
|
A detached `post-commit` graph rebuild used to dirty the tree between commits
|
||||||
export GRAPHIFY_SKIP_HOOK=1 # keep set for the whole sequence
|
here, which is why this step once began by exporting `GRAPHIFY_SKIP_HOOK=1`.
|
||||||
```
|
That tool and its hook were removed in `a5f7d95` (#1413); nothing to disable.
|
||||||
|
|
||||||
The graphify `post-commit` hook launches a detached graph rebuild after any
|
|
||||||
commit touching a non-`graphify-out/` path. Left on, it rewrites `graphify-out/`
|
|
||||||
between commit 1 and commit 2 — the two commits stop being adjacent, and the
|
|
||||||
background write can race staging. `GRAPHIFY_SKIP_HOOK=1` is the documented off
|
|
||||||
switch; no uninstall needed.
|
|
||||||
|
|
||||||
Close any editor, `cargo`, `vite`, or file watcher holding
|
Close any editor, `cargo`, `vite`, or file watcher holding
|
||||||
`Client/tauri-client/src-tauri/target/`: a Windows directory rename fails while a
|
`Client/tauri-client/src-tauri/target/`: a Windows directory rename fails while a
|
||||||
@@ -286,9 +281,7 @@ the client's own `.gitignore`. `Server/Makefile` and `genprotocol` use
|
|||||||
`../Client/…` from `Server/`, so their depth is unchanged — only the path
|
`../Client/…` from `Server/`, so their depth is unchanged — only the path
|
||||||
element drops.
|
element drops.
|
||||||
|
|
||||||
**Leave alone:** every dated `docs/audit-*.md`, `graphify-out/**` (regenerated —
|
**Leave alone:** every dated `docs/audit-*.md`, and `CHANGELOG.md`.
|
||||||
and `.graphify_labels.json` is covered byte-for-byte by its `.sig`, so
|
|
||||||
hand-editing invalidates it), and `CHANGELOG.md`.
|
|
||||||
|
|
||||||
### The ledger is the one real judgement call
|
### The ledger is the one real judgement call
|
||||||
|
|
||||||
@@ -363,15 +356,10 @@ Targeted proofs the generic suite will not give you:
|
|||||||
|
|
||||||
### Step 7 — refresh the graph, separately
|
### Step 7 — refresh the graph, separately
|
||||||
|
|
||||||
```bash
|
**Retired.** This step ran `graphify update .` to regenerate the 13,233 stale
|
||||||
unset GRAPHIFY_SKIP_HOOK
|
path strings in `graph.json` after the flatten. The tool was removed wholesale
|
||||||
graphify update .
|
in `a5f7d95` (#1413), so the command no longer exists and `git commit -am` here
|
||||||
git commit -am "chore(graphify): refresh knowledge graph after the Client flatten"
|
would commit nothing while appearing to succeed.
|
||||||
```
|
|
||||||
|
|
||||||
Its own commit, per `CLAUDE.md`. The 13,233 stale path strings in `graph.json`
|
|
||||||
regenerate; never hand-edit them, and never fold an 18 MB blob into the
|
|
||||||
structural diff.
|
|
||||||
|
|
||||||
### PR shape
|
### PR shape
|
||||||
|
|
||||||
@@ -411,7 +399,7 @@ Covers `RL-17/C-01`, `RL-12/R-06`, the `G-04` remnant, `RL-04/L-04`,
|
|||||||
facade — `go run ./scripts/genprotocol` plus `git diff --exit-code` already
|
facade — `go run ./scripts/genprotocol` plus `git diff --exit-code` already
|
||||||
works — and update the `ci-check` skill to match.
|
works — and update the `ci-check` skill to match.
|
||||||
- `npm run hooks:install` repoints `core.hooksPath` at `.githooks/`, which has
|
- `npm run hooks:install` repoints `core.hooksPath` at `.githooks/`, which has
|
||||||
no `post-commit`, silently disabling any locally installed one (graphify).
|
no `post-commit`, silently disabling any locally installed one.
|
||||||
Either ship a chaining `.githooks/post-commit` or document the exclusivity.
|
Either ship a chaining `.githooks/post-commit` or document the exclusivity.
|
||||||
6. **Branch policy (`R-02`).** One statement of the branch/PR model. Active
|
6. **Branch policy (`R-02`).** One statement of the branch/PR model. Active
|
||||||
documents currently disagree.
|
documents currently disagree.
|
||||||
@@ -420,7 +408,7 @@ Covers `RL-17/C-01`, `RL-12/R-06`, the `G-04` remnant, `RL-04/L-04`,
|
|||||||
|
|
||||||
`RL-19/L-13` and `S-05`. Add a root `.editorconfig`, widen Prettier beyond the
|
`RL-19/L-13` and `S-05`. Add a root `.editorconfig`, widen Prettier beyond the
|
||||||
client's TypeScript, add `cargo fmt --check`, repository-wide `gofmt`,
|
client's TypeScript, add `cargo fmt --check`, repository-wide `gofmt`,
|
||||||
`shellcheck`, and `actionlint`. Exclude `graphify-out/`, `Server/db/dbgen/`, the
|
`shellcheck`, and `actionlint`. Exclude `Server/db/dbgen/`, the
|
||||||
generated client directory, and the untracked `docs/security-findings/`. **Land
|
generated client directory, and the untracked `docs/security-findings/`. **Land
|
||||||
the gate and the reformat as two commits** so the world-reformat diff is
|
the gate and the reformat as two commits** so the world-reformat diff is
|
||||||
reviewable separately from the rule that caused it.
|
reviewable separately from the rule that caused it.
|
||||||
@@ -455,12 +443,19 @@ behaviour rather than adopting workspaces on principle.
|
|||||||
|
|
||||||
## B1-6 — Generated artifacts
|
## B1-6 — Generated artifacts
|
||||||
|
|
||||||
- **`RL-06/L-06`** — 20.41 MB tracked, `graph.json` 19.46 MB. Local regeneration
|
- **`RL-06/L-06`** — **closed by deletion, before this phase opened.** `a5f7d95`
|
||||||
is demonstrated; Linux/CI portability is the remaining unknown. Do not untrack
|
(#1413) removed the tool and all 7 tracked files (20,408,656 bytes;
|
||||||
before a CI artifact exists. Never rewrite published history.
|
`graph.json` 19,463,420). Nothing graphify-related is tracked, so the
|
||||||
- **`RL-07/L-07`** — the sharpest of the three. `--check` cannot detect drift and
|
"prove portable regeneration, then untrack" sequence has no subject: there is
|
||||||
no workflow runs it. Add a real drift check (render to a temp file and diff),
|
nothing left to regenerate and no committed report to drift-check. History was
|
||||||
wire it into CI, _then_ consider untracking the rendering.
|
not rewritten and is not going to be. B1-6 only retires the dead operational
|
||||||
|
steps this plan still carried.
|
||||||
|
- **`RL-07/L-07`** — **done.** `--check` returned before `render()` and never
|
||||||
|
opened `FINDINGS.md`, so a stale 1.09 MB rendering passed the
|
||||||
|
`Docs & Ledger Consistency` job cleanly. B1-6 landed the drift check first,
|
||||||
|
then untracked the rendering — which removes the drift class entirely rather
|
||||||
|
than watching it. `findings-ledger.json` stays the only tracked copy; CI
|
||||||
|
renders twice, compares, and uploads the result as an artifact.
|
||||||
- **`RL-08/L-08`** — source is committed; only the gate is missing, and it is
|
- **`RL-08/L-08`** — source is committed; only the gate is missing, and it is
|
||||||
**blocked by a toolchain conflict** (pinned TinyGo rejects Go 1.26, so a
|
**blocked by a toolchain conflict** (pinned TinyGo rejects Go 1.26, so a
|
||||||
compile-and-compare job needs a second Go SDK). The cheaper honest option may
|
compile-and-compare job needs a second Go SDK). The cheaper honest option may
|
||||||
@@ -495,15 +490,15 @@ behaviour moves in B1.** Adapter extraction is B7 and must not be smuggled in.
|
|||||||
- Server architecture, database seams, hub lifecycle (B3).
|
- Server architecture, database seams, hub lifecycle (B3).
|
||||||
- Renaming `Server/`, lowercasing `Client`/`Server`, or monorepo consolidation —
|
- Renaming `Server/`, lowercasing `Client`/`Server`, or monorepo consolidation —
|
||||||
the layout audit explicitly rejects all three.
|
the layout audit explicitly rejects all three.
|
||||||
- Rewriting Git history to shrink `graphify-out/`.
|
- Rewriting Git history to shrink the removed `graphify-out/` blobs. The files
|
||||||
|
are gone from the tree (#1413), but four `graph.json` revisions remain in the
|
||||||
|
pack — ~71 MiB logical, ~3.2 MiB packed of 13.28 MiB. They stay.
|
||||||
- Editing dated audit files to match new paths.
|
- Editing dated audit files to match new paths.
|
||||||
|
|
||||||
## Traps carried forward from B0
|
## Traps carried forward from B0
|
||||||
|
|
||||||
- `dev` is PR-only, 0 approvals, enforced on admins. **Required checks are not
|
- `dev` is PR-only, 0 approvals, enforced on admins. **Required checks are not
|
||||||
pinned**, so a PR can still merge red until B1-0 lands.
|
pinned**, so a PR can still merge red until B1-0 lands.
|
||||||
- Set `GRAPHIFY_SKIP_HOOK=1` for any multi-commit sequence, or the detached
|
|
||||||
post-commit rebuild dirties the tree between commits.
|
|
||||||
- The CI Docker job is `main`-gated and therefore **skipped on dev PRs**. Produce
|
- The CI Docker job is `main`-gated and therefore **skipped on dev PRs**. Produce
|
||||||
Docker evidence locally; on Git Bash for Windows, `MSYS_NO_PATHCONV=1` is
|
Docker evidence locally; on Git Bash for Windows, `MSYS_NO_PATHCONV=1` is
|
||||||
required or MSYS path conversion reports a false boot failure.
|
required or MSYS path conversion reports a false boot failure.
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ the live API.
|
|||||||
| Approvals required | 0 (solo maintainer) |
|
| Approvals required | 0 (solo maintainer) |
|
||||||
| Applies to admins | yes |
|
| Applies to admins | yes |
|
||||||
| Force pushes / deletions | disabled |
|
| Force pushes / deletions | disabled |
|
||||||
| Required status checks | **10** |
|
| Required status checks | **12** |
|
||||||
|
|
||||||
Pinned:
|
Pinned:
|
||||||
|
|
||||||
@@ -116,8 +116,13 @@ Server Build & Test (windows-latest) Client E2E (parity subset, blocking)
|
|||||||
Client Static Checks Analyze (go)
|
Client Static Checks Analyze (go)
|
||||||
Client Unit Tests Analyze (javascript-typescript)
|
Client Unit Tests Analyze (javascript-typescript)
|
||||||
Rust Unit Tests Analyze (actions)
|
Rust Unit Tests Analyze (actions)
|
||||||
|
Repository Hygiene Docs & Ledger Consistency
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`Repository Hygiene` was added 2026-08-26 (B1-3, S-05) and
|
||||||
|
`Docs & Ledger Consistency` 2026-08-27 (B1-6, L-07); both names were read off a
|
||||||
|
live PR after the job reported, per the rule below.
|
||||||
|
|
||||||
Deliberately **not** pinned, with the observed reason:
|
Deliberately **not** pinned, with the observed reason:
|
||||||
|
|
||||||
| Check | Why not |
|
| Check | Why not |
|
||||||
@@ -129,7 +134,7 @@ Deliberately **not** pinned, with the observed reason:
|
|||||||
|
|
||||||
A required check that never reports blocks every PR forever, so the list was
|
A required check that never reports blocks every PR forever, so the list was
|
||||||
read off a live dev-targeted PR with `gh pr checks`, not inferred from
|
read off a live dev-targeted PR with `gh pr checks`, not inferred from
|
||||||
`ci.yml`. That mattered: **three of the ten exist in no workflow file**, because
|
`ci.yml`. That mattered: **three of the twelve exist in no workflow file**, because
|
||||||
CodeQL runs from GitHub default setup configured in repository settings.
|
CodeQL runs from GitHub default setup configured in repository settings.
|
||||||
|
|
||||||
Two consequences to carry into B1:
|
Two consequences to carry into B1:
|
||||||
|
|||||||
@@ -7,8 +7,11 @@
|
|||||||
//
|
//
|
||||||
// Scope, deliberately small: this counts ledger statuses and compares them to
|
// Scope, deliberately small: this counts ledger statuses and compares them to
|
||||||
// the numbers active documents assert. It is not a document-status framework,
|
// the numbers active documents assert. It is not a document-status framework,
|
||||||
// and it does not check that FINDINGS.md is in sync with the ledger — that is a
|
// and it does not check that FINDINGS.md is in sync with the ledger — that was
|
||||||
// different check (RL-07) with a different owner.
|
// RL-07, and B1-6 answered it by not tracking FINDINGS.md at all, so there is
|
||||||
|
// no committed rendering left to drift. `npm run check:docs` runs this script
|
||||||
|
// and then regenerates the rendering, which is where a generation failure
|
||||||
|
// surfaces.
|
||||||
//
|
//
|
||||||
// It reads findings-ledger.json directly and does NOT import render-ledger.mjs.
|
// It reads findings-ledger.json directly and does NOT import render-ledger.mjs.
|
||||||
// That module has no `import.meta.main` guard, so importing it to reuse
|
// That module has no `import.meta.main` guard, so importing it to reuse
|
||||||
@@ -51,7 +54,6 @@ const WATCHED = [
|
|||||||
"docs/plans/repo-health-issue-register-2026-08-23.md",
|
"docs/plans/repo-health-issue-register-2026-08-23.md",
|
||||||
"docs/plans/b0-baseline-2026-08-25.md",
|
"docs/plans/b0-baseline-2026-08-25.md",
|
||||||
"docs/plans/b1-repository-foundation-2026-08-25.md",
|
"docs/plans/b1-repository-foundation-2026-08-25.md",
|
||||||
".superpowers/FINDINGS.md",
|
|
||||||
"CLAUDE.md",
|
"CLAUDE.md",
|
||||||
"README.md",
|
"README.md",
|
||||||
];
|
];
|
||||||
|
|||||||
+11
-1
@@ -110,9 +110,19 @@ const CHECK_RUST = [
|
|||||||
step("cargo", ["clippy", "--all-targets", "--", "-D", "warnings"], "Client/src-tauri"),
|
step("cargo", ["clippy", "--all-targets", "--", "-D", "warnings"], "Client/src-tauri"),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// RL-07. FINDINGS.md is not tracked, so there is no committed rendering to
|
||||||
|
// drift — the gate is that generation must succeed. Rendering subsumes
|
||||||
|
// `--check`: main() validates and exits 1 on a schema problem before it writes.
|
||||||
|
// It also leaves the contributor a readable copy, which is the point of running
|
||||||
|
// it locally. CI additionally renders twice and compares, to prove the output
|
||||||
|
// is a pure function of the ledger; that needs a temp path, so it lives in
|
||||||
|
// ci.yml rather than here.
|
||||||
|
// `step`, not `optional`: this file is itself Node, so probing for it is theatre.
|
||||||
|
const LEDGER_VERIFY = [step("node", [".superpowers/render-ledger.mjs"], ".")];
|
||||||
|
|
||||||
// Fast and dependency-free, so it goes first: a contradicted count should not
|
// Fast and dependency-free, so it goes first: a contradicted count should not
|
||||||
// wait behind ten minutes of -race.
|
// wait behind ten minutes of -race.
|
||||||
const CHECK_DOCS = [step("node", ["scripts/check-doc-counts.mjs"], ".")];
|
const CHECK_DOCS = [step("node", ["scripts/check-doc-counts.mjs"], "."), ...LEDGER_VERIFY];
|
||||||
|
|
||||||
// Repository-wide formatting and script/workflow lint (RL-19 / L-13, S-05).
|
// Repository-wide formatting and script/workflow lint (RL-19 / L-13, S-05).
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user