This repo was a copy of Ouroboric's umbrella. It becomes piabrota's company umbrella, ready for pirsig to manage it. - DOGMA.md holds ten invariants, and AGENTS.md follows it. - PIABROTA.md indexes the org's public repos, rendered by minijinja-cli from scripts/piabrota/repos.toml. - docs/DECISION_MODELS.md tracks decision models and where routines run. - brain/ and pirsig.toml seed PARA, smart notes and the decision track record. - The org, the bot name, the charter, the roadmap, the budget, the legal page and the palette change. - Ouroboric's logos go. Its licences stay, with credit in NOTICE.md. Dated records keep their text. - The Go component template becomes a Rust one, rendered by minijinja-cli. - Every plot is drawn with archify, pinned in forks/archify. Mermaid, D2 and diagrams.sh go. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The base CI
add-ci.sh writes the base CI into a repo: .pre-commit-config.yaml, .github/workflows/lint.yml (the
required check lint) and the files that some pieces ship. The config is built from pieces, one per tool.
Every hook revision is pinned to a release tag.
The pieces
| Piece | Hook | Ships |
|---|---|---|
gitleaks |
gitleaks, plus gitleaks-history (manual stage, the whole history) |
|
reuse |
reuse-tool, reuse lint |
|
markdown |
markdownlint-cli2. The config bends four rules: MD025 lets front matter title: and an H1 coexist; MD010 ignores code blocks, because gofmt requires tabs and the plan tools copy plan code verbatim; MD033 allows <picture>, <source> and <img>, because the docs standard prescribes <picture> for themed images and the org profile opens with <img>; MD041 is off, because journal entries, PR templates, Claude skill files and the org profile banner open without an H1 on purpose, and the docs standard's front matter rule already checks every other doc's title |
.markdownlint.yaml |
shell |
shellcheck-py. The config sets external-sources=true, because the hook passes no -x: without it, SC1091 fires on every sourced file that isn't among the hook's inputs, even with source-path directives. It makes shellcheck follow and check sourced files, which is stricter |
.shellcheckrc |
actions |
actionlint | |
yaml |
yamllint | .yamllint.yaml |
gitleaks is always first, even when nobody lists it.
Choosing pieces
The org config's ci list names the pieces. --ci <piece>,... replaces that list for one run. A repeated
name is kept once, in first-seen order. An empty item (reuse,,markdown) or an unknown piece exits 2 and
writes nothing.
The piece format
A piece is one file in pieces/. add-ci.sh enforces these rules and names the piece and the rule that fails:
- The file is
<name>.yaml, and the name matches^[a-z0-9-]+$. - It has exactly one line matching
^ - repo: https://. - It has exactly one line matching
^ rev:followed by one space, whose value matches^v?[0-9]+(\.[0-9]+)+([.-][0-9A-Za-z]+)*$(a release tag, never a branch). - It has a line matching
^ hooks:. - It has no tabs.
Each line is indented as it will be under repos:, because add-ci.sh appends the files in order.
A piece may have a <name>.files/ folder. Its files are copied into the repo at the same relative paths.
Two pieces must not ship the same path.
Realms will ship their own pieces in this format later (the repo-bootstrap proposal, future work item 3).
Existing repos
add-ci.sh --upgrade also works on a repo whose .pre-commit-config.yaml is the old gitleaks-only scaffold
(upgrade/gitleaks-only.yaml). It replaces that file, and adds lint.yml and the piece files. If the config
differs by even one byte, it exits 2 and writes nothing; every other target must still not exist.
The rollout to an existing repo, one base-ci branch per repo:
- An agent clones the repo fresh into
rollout/<repo>, beside themucheckout, and cutsbase-cifromorigin/main. It runsadd-ci.sh --upgrade, then fixes what the hooks flag, formatting only, until bothpre-commit run --all-filesandpre-commit run --hook-stage manual gitleaks-history --all-files(the whole history) pass.md-same-text.sh origin/main HEADproves each Markdown fix keeps the text, the code blocks and the links, and turns no rule off with amarkdownlint-disablecomment. - The founder runs
GH_TOKEN=... rollout-ci.sh --org <org> push <repo>.... It pushesbase-ci, because the pr-bot can't push.github/workflows/, and opens the PR as the pr-bot, or prints the one already open. It records each push in the clone'srefs/rollout-ci/base-ci. After a rebase, the same command updates the branch, with--force-with-leaseon that record. Ifbase-cimoved on GitHub since the last push (a person's commit, a review suggestion, "Update branch"), push refuses that repo and prints both SHAs. The agent folds the move into the local branch and acknowledges it withgit update-ref refs/rollout-ci/base-ci <GitHub's SHA>; then the founder runs push again. Oncemainholdslint.yml, push leaves the repo alone and says to run protect. - After the merge, the founder runs
rollout-ci.sh --org <org> protect <repo>.... It runssetup-repo.sh, solintbecomes required, only on a repo whosemainholdslint.yml, and names the others.
A repo that needs an exclusion keeps it in its own file, never in the shared copies, with a comment that gives the reason. Only files that aren't docs or config people read are excluded, such as test inputs read by code, or the pieces, which are fragments rather than YAML documents. Nothing is excluded from gitleaks or reuse.
- markdownlint:
.markdownlint-cli2.yamlholding onlyignores:. The rules still come from.markdownlint.yaml. - yamllint:
.yamllint, holdingextends: .yamllint.yamlandignore:. yamllint reads it before.yamllint.yaml.
scripts/tests/lint-config.test.sh proves that both keep the shared rules.
Bumping a pin
Change the rev in a reviewed pull request. The release must be at least two weeks old. For lint.yml, the
action SHAs and pre-commit==... follow the same rule. Run bash scripts/tests/add-ci.test.sh before the
pull request, because it runs every hook for real.
An org whose Actions policy allows only listed actions (piabrota's does) must list both of lint.yml's
actions, actions/checkout and actions/setup-python, at the exact SHAs it pins. Otherwise lint ends in
startup_failure and never runs. So a bump of either action SHA also updates the org's allowlist. A
user's repo holds its own allowlist: run setup-repo.sh on each of them after the bump.