BYOR Quickstart — Govern Your Own Repository
Bring Your Own Rules. This guide takes a repo through every step needed to place it under constitutional enforcement and verify that the audit catches a real violation.
Two different tools are involved, and they have different infrastructure needs:
core project onboard/promote/scout(Phases A/B below) go throughcore-cli, a pure-HTTP client (ADR-146 D2) — they talk to a running CORE API instance over HTTP. There is currently no offline/zero-infra mode for these; the API itself needs Postgres + Qdrant behind it (see getting-started.md).core-admin code audit --offline(Steps 6-8 below) needs no running services at all — it reads.intent/and your source tree directly.
If you already have a CORE instance running somewhere (your own dev install,
or a shared team instance), skip straight to Step 3 and point CORE_API_URL
at it. If not, Step 2 below is the fastest path to a local one.
What you need
| Requirement | Notes |
|---|---|
| Python ≥ 3.12 | Check: python3 --version |
| pip | Usually ships with Python; check: pip --version |
| git | The project you want to govern must be a git repository |
A project with .py files |
Anything works — a library, a service, even a toy repo |
| A reachable CORE API | Either one you already run, or the local one you stand up in Step 2 |
Co-location note: onboard/promote write .intent/ on the API
host's filesystem, not the machine running core-cli — per ADR-054 D3,
core-api is loopback-bound and unauthenticated (single-operator, Phase 1),
so it can't safely accept writes to an arbitrary path from an untrusted
remote client. Run core-cli on the same host as the CORE API (or the CORE
API on the same host as your target repo). scout's ratified rule files, by
contrast, are written locally by core-cli itself — only the rule
induction (reading your code, proposing candidates) goes over the API.
Details: F-1, .specs/planning/archive/CORE-CLI-2.9.0-Followups.md.
Always pass an absolute path to onboard/promote/scout — never ..
core-cli sends path as a plain string over HTTP; a relative path is
resolved by whichever process receives it (the CORE API for onboard/
promote), not by your own shell. . would silently target the API
server's own working directory instead of your project. Use $(pwd) or a
full path instead.
Step 1 — Install the CLI
pip install core-cli
core-cli depends on core-runtime, so this pulls in both entry points:
core --help # consumer CLI — onboard, scout, audit-via-API, etc.
core-admin --help # operator CLI — offline audit, daemon control, etc.
If the shell cannot find either command, your Python scripts directory is not
on PATH; add it (e.g. ~/.local/bin on Linux, the Scripts folder on
Windows) and reload your shell.
Step 2 — Make sure a CORE API is reachable
Skip this if you already have a CORE instance running and reachable.
git clone https://github.com/DariuszNewecki/CORE.git
cd CORE
poetry install
cp .env.example .env
docker compose up -d # Postgres + Qdrant
docker compose exec -T postgres psql -U postgres -d core < schema.sql
Start the API server. In a fresh clone with no systemd user units installed yet, run this in its own terminal (or background it):
poetry run uvicorn src.api.main:create_app --factory --host 127.0.0.1 --port 8000 --env-file .env
(core-admin daemon up is the operator shortcut for this once you've
installed CORE's systemd user units — see getting-started.md — but assumes
they already exist; it's not the bootstrap path for a brand-new machine.)
By default core-cli talks to http://127.0.0.1:8000 (trusted-localhost,
no auth). If your CORE API is elsewhere, or on a different port, point at it:
export CORE_API_URL=http://<core-host>:8000
Full setup detail (LLM configuration, manual install path, verifying the connection): getting-started.md.
Step 3 — Make sure your project is a git repository
CORE reads the project root from the nearest .git/ directory above your
working directory. If your project is not tracked by git yet:
cd /path/to/myproject
git init
git add .
git commit -m "initial commit"
If it already has a .git/ directory, no action needed.
Step 4 — Phase A: deliver the machinery floor
The machinery floor is the constitutional infrastructure — META schemas,
taxonomies, a constitution stub, and enforcement configuration. It ships
bundled inside the core-runtime wheel that the CORE API is running.
core project onboard /path/to/myproject --write
Expected output:
Onboarding repository (Phase A — machinery floor): /path/to/myproject
Onboarding complete (write).
The command is safe to run on any project. It refuses to overwrite an
existing .intent/ (ADR-111 D3), so there is no accidental clobber risk.
Preview only (no write) — omit --write to see what would be delivered:
core project onboard /path/to/myproject
Step 5 — Phase B: induce and ratify rules
Scout samples your source code, proposes governance rules, and requires you to ratify each one before writing anything.
core project scout /path/to/myproject --write
With an LLM configured
If the CORE API instance has an LLM provider configured and reachable, Scout calls it with a structural signal report (line counts, exception patterns, decorator inventory, …) and proposes rules fitted to your specific codebase. Ratify them one by one:
Rule 1 / N
OBSERVATION
ID: scout.no_bare_except
Statement: Code MUST NOT swallow exceptions with a bare 'except:'…
Enforcement: blocking
ENFORCEMENT Engine: regex_gate …
a = accept · r = reject · c = change enforcement level
Action [a/r/c] (a):
Press Enter to accept the default (a), or type r to reject or c to
change the enforcement level before accepting.
Without an LLM (offline / no key)
Scout falls back to a curated four-rule menu — the same universal rules used in the proof that shipped the BYOR capability:
| Rule ID | Statement | Default enforcement |
|---|---|---|
scout.no_bare_except |
No bare except: or except Exception: pass |
blocking |
scout.docstrings |
Public functions and classes must have a docstring | reporting |
scout.no_print |
No print() in importable library code |
reporting |
scout.no_secrets |
No hardcoded credentials in string literals | reporting |
You still ratify each one — the fallback is a menu, not an auto-accept.
What gets written, and where
Rule induction (reading your code, proposing candidates) happens on the CORE
API. The ratified result is written locally, by core-cli, on the
machine you ran core project scout from:
.intent/rules/scout_inducted.json ← the ratified rule declarations
.intent/enforcement/mappings/scout.yaml ← engine + params for each rule
Preview only — omit --write to see what would be written:
core project scout /path/to/myproject
Step 6 — Run the audit
This is the one command in this guide that needs no running services —
it reads .intent/ and your source tree directly. Pass --target (only
valid with --offline) since this guide never cds into the project:
core-admin code audit --offline --target /path/to/myproject
On a project with no violations, you will see:
╭─ Final Verdict ─╮
│ PASS │
╰─────────────────╯
The --offline flag skips engines that require the knowledge graph or an LLM
(knowledge_gate, llm_gate). For pure BYOR enforcement this is the normal
operating mode.
Step 7 — Introduce a violation and verify detection
Create a file with a bare except: — the blocking rule you ratified in
Step 5 — inside /path/to/myproject:
# /path/to/myproject/bad.py
def get_data():
try:
return 42
except:
pass
Run the audit again:
core-admin code audit --offline --target /path/to/myproject
Expected output includes:
┃ Severity ┃ Count ┃
┡━━━━━━━━━━╇━━━━━━━┩
│ BLOCK │ 2 │
╭─ Final Verdict ─╮
│ FAIL │
╰─────────────────╯
Two BLOCK findings because the regex matches both except: and the
pass on the following line. Both match the no_bare_except rule.
Step 8 — Fix the violation and re-verify
Replace the bare except with a typed handler:
# /path/to/myproject/bad.py (fixed)
def get_data():
try:
return 42
except ValueError:
return None
Run the audit again:
core-admin code audit --offline --target /path/to/myproject
╭─ Final Verdict ─╮
│ PASS │
╰─────────────────╯
Your repository is now under constitutional enforcement.
What was just built
myproject/
├── .intent/
│ ├── META/ ← schemas, vocabulary, operational taxonomy
│ ├── constitution/ ← constitution stub (extend per ADR-111)
│ ├── enforcement/
│ │ ├── config/ ← action risk, operational mode config
│ │ └── mappings/
│ │ └── scout.yaml ← your ratified rules → engine mapping
│ ├── rules/
│ │ └── scout_inducted.json ← your ratified rule declarations
│ └── taxonomies/ ← operational capabilities, capability tiers
└── bad.py (or your real source files)
The .intent/ directory is the living constitution of your repository.
Commit it — it belongs in version control alongside the code it governs.
Next steps
Add the GitHub Action — run the audit on every pull request without a local install. See cold-reviewer.md for setup (two files, five minutes).
Promote rules to blocking — any reporting rule can be changed to
blocking by editing scout_inducted.json and the enforcement level in
scout.yaml. Re-run the audit to confirm the escalation works before committing.
Add custom rules — write a rule document in
.intent/rules/your_rules.json following the same schema as
scout_inducted.json, add an entry to .intent/enforcement/mappings/your.yaml,
and the next audit will pick it up automatically.
Run Scout again — as your codebase grows, re-run core project scout
/path/to/myproject --write to surface new patterns. Already-ratified rules are not overwritten;
Scout writes to scout_inducted.json (additive unless you edit it).
Full runtime — if you want the autonomous daemon (continuous audit →
remediation → commit loop), see getting-started.md for
the full Postgres + Qdrant setup and core-admin daemon up.