Nself CI Adoption - nself-org/plugins GitHub Wiki
Status: wired into plugins (this repo) 2026-09-11 โ the first of 13 nself-org repos to adopt it. Zero repos had it wired before this, including plugins itself, which owns the source at free/ci.
This guide is the copy-paste pattern for the other 12 repos. It also records, honestly, what the gate does and does not check today โ read "Known gaps" before requiring it as a sole merge gate.
- It owns the
nself-cisource (free/ci/) and the wrapper (scripts/nself-ci.sh) โ self-hosting the gate here is the purest dogfood: a regression in the gate breaks its own repo's checks first. - It is public, so the workflow runs on free GitHub-hosted Actions โ no runner-fleet dependency to prove the pattern.
- Its own
.claude/docs/CI-LOCAL.md(project-level) already documents the branch-protection recipe; this guide is the repo-level companion showing the wiring actually in place.
-
Workflow โ
.github/workflows/nself-ci.yml: checkout โsetup-go(builds the binary) โsetup-node/pnpm(for the Node gate path) โ installgitleaksโ runscripts/nself-ci.sh .withpermissions: statuses: writeandGH_TOKEN: ${{ github.token }}sogh apican post the status without a PAT. -
Wrapper โ
scripts/nself-ci.shbuildsfree/ci/nself-ciif missing/stale, thenexecs it. Same script works locally and in CI. -
Status โ the binary posts a
nself-cicommit status viagh api repos/{owner}/{repo}/statuses/{sha}(OAuth, no token in a URL). -
Branch protection (NOT applied by this PR โ owner's call) โ add
nself-citorequired_status_checks.contextsper the exactgh apicommand in~/Sites/nself/.claude/docs/CI-LOCAL.mdยง "Requiring nself-ci in branch protection". Do this as an additional required check alongside the repo's existing checks, not a replacement โ see gaps below.
Copy .github/workflows/nself-ci.yml from this repo into each of the other 12 (cli, admin, web, nchat, nclaw, nsentry, ntask, nfamily, clawde, homebrew-nself, packages, plugins-pro), adjusting the toolchain setup steps to match that repo's stack (e.g. Flutter repos need subosito/flutter-action instead of setup-node).
$ scripts/nself-ci.sh --check -v .
[nself-ci] building gate binary...
[nself-ci] built .../free/ci/nself-ci
[nself-ci] running: gitleaks detect --source . --exit-code 1 --config .github/gitleaks.toml
nself-ci gate results โ .
Stacks: node
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
secrets:gitleaks PASS (3.617s)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Overall: PASSED (4s)
That is the real, complete output โ one gate ran (secrets:gitleaks), it passed, in under 4 seconds. Read that literally: no lint, typecheck, test, or build gate ran, for the reason below.
1. This repo's Node gate is a no-op. nself-ci detects Node stacks by checking the root package.json for lint / typecheck / test / build scripts (or workspace members' scripts, if pnpm-workspace.yaml exists). This repo's root package.json has neither: it has one custom script, ci:local (a stub โ tsc --noEmit --allowJs || echo skip; echo passed), and no pnpm-workspace.yaml, even though shared/ has its own package.json. Result: the workspace-recursion path in free/ci/internal/legacy/gate_runners.go (runNodeGates) never triggers, the non-workspace path finds no matching scripts, and the gate silently runs nothing but the secret scan โ while still printing Overall: PASSED. This is exactly the shape of failure flagged in .claude/memory/lesson_hollow_ci_gates.md: a gate that passes while checking nothing.
-
Fix options (not applied by this PR): (a) add real
lint/typecheck/test/buildscripts to rootpackage.json(or addpnpm-workspace.yamlso member scripts are picked up), or (b) extendgate_runners.goto also recognize this repo'sci:localconvention. Until one of these lands,nself-cion this repo is a secrets-only gate, not a lint/test/build gate.
-
No per-plugin checks. The real quality bar for this repo is per-plugin (schema validation, duplicate-port checks, registry consistency โ see
validate.yml,registry-check.yml,hygiene.yml,license-gate.yml).nself-cionly inspects the repo root; it does not walkfree/*/plugin.jsonor run plugin-level TypeScript builds. It does not replace any of those workflows. -
No lint/typecheck/test/build for Go-only subpackages either.
free/ciitself (and other Go-based plugins) has ago.mod, but stack detection only looks at the repo root, which has no rootgo.mod. A regression insidefree/ci's own Go code would not be caught bynself-cirunning against repo root โ only by its owngo test ./...if invoked directly in that subdirectory, or bybuild-test.yml. -
Per the plugin's own README ("What it explicitly does NOT do"):
nself ciis not a GitHub Actions emulator. E2E, Lighthouse, axe-a11y, CodeQL, Trivy, SBOM, license audit, coverage ratchet, bundle-size, commitlint, i18n-check, and deploy jobs all still need to run somewhere else โ free-runner Actions for public repos (this repo), or a dedicated additional gate for private ones. -
Fork PRs:
GH_TOKEN: ${{ github.token }}on a fork PR is read-only for status writes; the binary treats a failed status post as a warning (non-fatal), so the job's pass/fail still reflects the real gate result, but thenself-cicheck may not appear on fork PRs.
Bottom line: this PR adds nself-ci as evidence the wiring works end-to-end (workflow โ build โ gates โ status), not as proof the gate is a complete substitute for this repo's existing checks. Recommend requiring it in branch protection in addition to, not instead of, validate.yml / build-test.yml / gitleaks.yml / registry-check.yml until gap 1 is closed.
- Copy
.github/workflows/nself-ci.yml, adjust toolchain setup for the repo's stack. - Run
scripts/nself-ci.sh --check -v .(orplugins/free/ci/nself-cidirectly) locally first โ confirm which stacks it detects and which gates actually execute. Do not assume; read the printed gate table. - If the repo's real lint/typecheck/test/build live under a non-standard script name (as
ci:localdoes here) or in workspace members withoutpnpm-workspace.yaml, fix that before treating a greennself-cias meaningful. - Open a PR, let Actions run it once, confirm the
nself-cicommit status appears on the PR. - Propose (do not silently apply) the branch-protection addition per
CI-LOCAL.mdโ owner approves it.