25 Commits

Author SHA1 Message Date
Alex Dunmow
8418d2535b allowlist CMS registry browse/detail fetch() to orchestrator endpoints
The CMS registry browse tab and new Phase-5 detail view call the
orchestrator's public Connect endpoints (PluginRegistryService /
PluginReviewService) on a different origin. The CMS transport and generated
hooks only cover the CMS's own proto surface, so these are plain JSON POSTs
to the registry URL — same rationale routes/admin/plugins.tsx carried before
the BrowseRegistryTab extraction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 10:42:08 +08:00
Alex Dunmow
3476316e9c fix(frontend): match calcomblock REST allowlist by location-independent suffix
calcomblock was extracted from cms/backend/internal/plugins/calcomblock into a
standalone repo at plugins/calcomblock. The allowedPluginRESTFiles entries were
pinned to the old bundled path, so the standalone plugin's web/{settings,editor}.tsx
(which legitimately call the plugin's own HTTPHandler REST routes — no ConnectRPC
surface exists) tripped no-fetch-in-plugin. Switch to the suffix
plugins/calcomblock/web/... which matches both the standalone and legacy bundled
locations (pluginRESTFileAllowed uses HasSuffix).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 03:06:20 +08:00
Alex Dunmow
2c7fe9aa62 fix: model orchestrator + cli as first-class scan targets (WO-WZ-023)
- resolveScanRoots recognizes the orchestrator layout (backend/go.mod +
  frontend/src) so backendDir resolves to backend/ — fixes the go.mod check,
  classifies the frontend as a standalone app (not a plugin), and routes tsc
  through the plain typecheck. Clears three orchestrator false-positives.
- check 2 (RBAC): only require MethodRoles for proto services a target actually
  serves (mounted connect handler). The orchestrator carries but does not serve
  the blockninja.v1 surface (714 false 'missing' -> 0). Exclude cli
  (definitions-only, vendors a registry client) from check 2.
- Skip buf-generated gen/ output in the placeholder check (real proto enum
  COMING_SOON values are not TODO debt).
- Normalize the color allowlist path so theme-card.tsx swatch previews match
  regardless of scan root.
- Whitelist the gitignored, tool-regenerated styles/package-lock.json.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 22:39:47 +08:00
Alex Dunmow
44d60b5f7b feat(frontend): allowlist /.well-known/skills/ fetches
The CMS skill-install card now reads the instance-derived skill name from
the public /.well-known/skills/index.json discovery index — a well-known
endpoint with no ConnectRPC surface, so it warns instead of failing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 15:21:21 +08:00
Alex Dunmow
542324fe5c feat(colors): exempt templates/email/*.ninjatpl from the hardcoded-color check
Mail clients don't support CSS custom properties — themed email wrappers
(codeless WO-WZ-021) inline resolved {{ colors.* }} hex with hex
fallbacks by necessity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 14:42:41 +08:00
Alex Dunmow
e03144e12d feat: codeless plugin repos (plugin.mod, no Go) exempt from go.mod checks (WO-WZ-020)
A codeless .bnp repo has no go.mod by design — mirror the ninja CLI
classifier (core bnp.IsCodelessRepo) and skip both the standalone-plugin
go.mod checks and the backend replace-directive parse for such roots.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 12:43:54 +08:00
Alex Dunmow
482e4f9d43 fix(check2): skip RBAC check for the definitions-only core SDK
Check 2 (RPC methods registered in RBAC interceptor) fired FAIL on core,
flagging all 926 shared procedures as "NOT in MethodRoles". Core is the
shared Go SDK: it carries the canonical proto (blockninja.v1, orchestrator.v1,
helpdesk.v1) but serves nothing and has no interceptor, so there is no
MethodRoles map to match against and every procedure looked missing.

RBAC is genuinely enforced where these procedures are actually served, and
check 2 already validates it there: cms covers blockninja.v1 (green) and
orchestrator covers orchestrator.v1 (0 missing). The abi.v1 protos declare no
services, and helpdesk.v1 is unmounted definitions. So scanning the SDK in
isolation is a not-applicable result, not a finding.

Detect the core SDK target (no serving package prefix, no interceptor.go,
module path == core) and SKIP check 2 for it instead of folding its shared
procedures into the missing-set. Serving backends (cms/orchestrator) and
plugins are untouched — the guard short-circuits the moment a target has a
package prefix or an interceptor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 09:28:19 +08:00
Alex Dunmow
05a0354c85 style: normalize any check finding lines to canonical file:line format
Drop the stray em-dash (envreads) and arrow (reinvented) separators so all
findings use the two canonical shapes: "file:line snippet" and
"file:line [rule] detail".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 01:30:52 +08:00
Alex Dunmow
c4d01de82c feat: concise output + diff-scoped any check
Reporter is now a collector: checks declare a verdict (OK/Skip/Warn/Fail/
Fatal) plus findings, and a single central render() prints output. Default
output is silent on pass/skip — only FAIL/WARN/ERR checks print, followed by
one tally line, so a clean run is two lines. --verbose restores full per-check
output. All ~30 checks were converted to this API; orphaned guidance/label
helpers (printPerTargetOKLines, per-check *Help blocks, colors_format.go) were
removed.

The any-usage check (2e) now defaults to only the unstaged working-tree diff
(changed lines), via a new per-repo git-diff index in changedlines.go; --all-any
restores the full scan. Not-a-git-repo / no-diff warns on nothing.

Golden fixtures regenerated; integration tests updated to the new format; added
unit tests for the diff index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 01:16:56 +08:00
Alex Dunmow
3b5a6d26d6 docs: design for concise output + diff-scoped any check
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 00:45:26 +08:00
Alex Dunmow
b423f90e29 feat: no -buildmode=plugin check (disabled until WO-WZ-017)
Adds check 29: ported plugin repos must not carry a `-buildmode=plugin` build
target (the legacy .so compile the wasm migration retires). Lands DISABLED
behind a per-check const toggle (enableBuildmodePluginCheck=false) so it emits
no output and never fails while the fleet is still porting; WO-WZ-017 flips it
on. Scans plugin Makefiles/*.sh only. Unit-tested; golden snapshots unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 18:34:28 +08:00
Alex Dunmow
40d33d0587 rawsql: allow FROM pg_roles admin catalog lookup
The wasm per-plugin role provisioner (cms role_provisioner.go, WO-WZ-007)
checks role existence with a parameterized `SELECT ... FROM pg_roles`.
That is a Postgres administrative catalog lookup (DCL provisioning, not
sqlc-able), the same category as the already-allowed `FROM pg_database`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 16:36:58 +08:00
Alex Dunmow
bba5946bb1 allow site-agent streaming hook createClient (WO-SA-006)
SiteAgentService.SendMessage is a Connect server-streaming RPC consumed
with for-await; generated Connect Query hooks are unary-only (same
justification as use-restart-operation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 03:05:25 +08:00
Alex Dunmow
3c88b19ff6 docs: make README all-inclusive (full 31-check table, all CLI flags)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:12:19 +08:00
Alex Dunmow
936a7e7ef9 feat: enforce proto generation freshness 2026-06-20 12:40:06 +08:00
Alex Dunmow
f968cb31d9 fix: skip gitignored .worktrees/ dirs in repo scans
Directory walkers pruned .git/vendor/node_modules but not .worktrees/, so a repo scan descended into nested git worktrees (e.g. an orchestrator worktree under cms/.worktrees/). Their Go/TS files surfaced as false positives in the standalone-plugin import check and noise in the any-usage warnings.

Add ".worktrees" to the skip set across the implicated and common walkers: proto RBAC proto-scan, standalone-plugin imports, frontend extras, go-lint, sqlc-uuid, presets.json, and plugin segmentation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 16:53:18 +08:00
Alex Dunmow
7b96ceac4d Link CLAUDE.md to AGENTS.md 2026-06-18 16:46:53 +08:00
Alex Dunmow
73259c6b30 fix(check-safety): recognize methodRolesSeed RBAC table; resolve backend scan-root
extractRBACMethodRoles matched only a map literal named MethodRoles. The CMS now
keeps its RBAC table behind an atomic-pointer copy-on-write live table, with the
static literal renamed to methodRolesSeed -- so the validator read an empty table
and reported 641 phantom "missing RBAC entry" methods, blinding the gate to any
genuine unregistered RPC. Match methodRolesSeed as well as MethodRoles, and add a
regression test (TestExtractRBACMethodRolesParsesSeedTable).

resolveScanRoots: also resolve a `backend` directory containing go.mod whose
parent holds buf.yaml or proto/, so the checker targets that layout correctly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 11:04:40 +08:00
Alex Dunmow
c048766075 feat(checks): add check 28 — public page handlers inject admin toolbar
New AST check scans handler render functions that build site settings
(getSiteSettings + doc["site_settings"]) and fails if they don't also set
siteSettings["toolbar"], so admin-toolbar injection can't silently regress
when a new public page type is added.

- toolbar.go: AST scanner; exempts renderListingPage (auto SEO pages) and
  renderVersionPreview (carries its own preview banner).
- check_toolbar.go: registration at Seq 280 (ID "28").
- registry_test.go: +1 expected check; golden fixtures regenerated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 22:37:59 +08:00
Alex Dunmow
7599fff971 feat: sanction /api/helpdesk/ as a known non-proto fetch prefix (WO-028)
Helpdesk attachment upload/download is multipart HTTP by design — ConnectRPC
has no multipart support — so the frontend fetch gets the same WARN-not-FAIL
treatment as /api/plugins/ and the other sanctioned REST endpoints.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 21:40:25 +08:00
Alex Dunmow
c3bb958f23 feat(frontend-check): flag raw <button> in JSX — use the shared <Button> component
New rules no-raw-button / no-raw-button-in-plugin ride inside Check 5's
walkers, same as the browser-confirm rules. Raw buttons bypass the
action=/entity= automation attributes (docs/BUTTON_AUTOMATION.md), so
MCP/Puppeteer can't target them.

Scope: .tsx/.jsx only (plain .ts builds DOM strings for non-React
surfaces), components/ui/ exempt (defines the primitives — same
carve-out as the button-automation check).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 15:15:46 +08:00
Alex Dunmow
fbe14f6c57 chore(frontend-check): document use-entity-search exception; allow helpdesk multipart fetches
use-entity-search reason expanded per 2026-06-10 work order Task 11
(Gate C: allowlist with documented reason). /api/support/ (CMS widget)
and /api/helpdesk/upload (orchestrator) are multipart FormData uploads
connect-query cannot express — WARN, not FAIL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 14:52:35 +08:00
Alex Dunmow
0e8d0e6d1c fix(scan-roots): detect consolidated CMS layout (backend/go.mod + web/src)
The old detection keyed on backend/cmd/check-safety, which left the CMS
tree when check-safety was hoisted standalone. Since then the CMS
resolved as its own backend: web/ was classified as a plugin frontend
(strict rules, allowedFrontendFiles bypassed — audit F2's check-5 noise),
check 2 demanded RBAC entries for client-only orchestrator protos
(audit F3), check 2c flagged a bogus missing go.mod, and checks 3/3b
silently SKIPPED. Recognize backend/go.mod + web/src as the CMS shape
so backendDir resolves to backend/ and web/src scans under core rules.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 14:52:35 +08:00
Alex Dunmow
57bca429cc chore: follow CMS module rename in rule strings + test fixtures
CMS Go module renamed from git.dev.alexdunmow.com/block/ninja to
git.dev.alexdunmow.com/block/cms (along with the git remote rename
on gitea). All import paths, string-literal rules, test fixtures,
and doc references updated; (historical 'ninja-orchestrator' refs
preserved). go mod tidy regenerated checksums.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 13:55:47 +08:00
Alex Dunmow
cd88c808b0 initial: standalone check-safety module hoisted from CMS
Static safety/lint runner for the BlockNinja codebase. ~25 invariant
checks across Go and frontend sources. Was at git.dev.alexdunmow.com:block/ninja
in backend/cmd/check-safety/ until the 2026-06-06 consolidation moved
the BlockNinja repos under a shared ~/src/blockninja/ parent.

This repo is the standalone extraction:
- Own go.mod (git.dev.alexdunmow.com/block/check-safety, go 1.26.4)
- Vendored internal/{helpers,theme} from CMS (Go's internal/ rule
  blocks cross-module imports; vendoring is the workaround)
- CLI contract unchanged: `check-safety <target-dir> [--flags]`
- CMS Makefile shells into ../check-safety for safety-check /
  install-safety-checker targets

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 13:04:02 +08:00