The gate scanned only `*.md *.yml *.yaml *.json *.toml *.sh *.ts *.js`. A `postgres.env`, a `.container` quadlet, an `.ini`, or an extension-less dotfile — exactly the shapes the dotfiles tier is made of — was never read at all. Discovered while deciding what else belongs in `dotfiles/`: `~/.config` carries `containers/systemd/postgres.env` with a real `POSTGRES_PASSWORD`, which the ignore list did not cover either (`git check-ignore` says so: `.env` does not match `postgres.env`). Token SHAPES now scan the entire staged diff, every file type; bcrypt hashes and `Authorization: Basic` headers joined the shape list. Credential ASSIGNMENTS are scanned in non-prose files only — the auto-learned memory tier is prose *about* credentials (7 such hits measured on the tracked tree), and a gate that fires there gets overridden until it means nothing. Markdown keeps shape detection, which is what it had before. Three invariants, each pinned by a test in tests/secret-gate.bats (13 tests, black-box: the hook is copied into a scratch repo and git itself runs it): - Fail closed. The first version of this rewrite passed `-I` to `git diff`, which is not valid there; the empty file list made every secret commit sail through, and a blind gate looks exactly like a clean tree. If the hook cannot read the staged diff it now refuses the commit. - Only ADDED lines can leak. The gate refused the redaction commit in this branch because the diff still carried the removed password — a gate that blocks the removal of a secret holds the leak hostage. - Path-valued variables (`…_FILE=`, `…_PATH=`) and placeholder/indirected values are not secrets, so the gate stays worth listening to. `.gitignore` quarantines `*.env` at any name, and gains the theme-media rule whose untracking lands in the next commit. `AGENTS.md` states the new contract. Refs #40
9.6 KiB
AGENTS.md — ~/.omp command center
System-configuration command center for Frank's fleet: agent config, knowledge base,
small system utilities, and (soon) dotfiles. You — the reader — are probably an agent
running on one of Frank's workstations with this repo as your working directory. Read
omp-docs/index.md before you touch anything. This file is the operating contract;
omp-docs/ is the knowledge base (OKF v0.1 format: directory of markdown files
with YAML frontmatter, spec at omp-docs/references/okf-spec.md).
What this repo is
- The live agent-config directory AND a git repo pushed to Gitea
(
dank/command-centeron the Dankclaw instance). There is no staging copy: the checkout is the deployment. Branch switches swap live config files out from under the running agent — keep feature branches short-lived, and never end a work session checked out on anything butmain. - Contents that belong here:
AGENTS.md,omp-docs/(knowledge base),scripts/(small system utilities),agent/+plugins/(harness config and plugin manifests). Everything else under this directory is runtime state and is gitignored on principle — see.gitignore. History must mean "the configuration changed", not "the agent breathed".
Trunk-based SDLC (since 2026-10-18; replaces the old git-flow regime)
mainis the only long-lived branch. Frank is the only dev;developand the release-branch ceremony are retired — nodevelop, no back-merges, norelease/X.Y.Zbranches.- Before every work item: fetch and branch off the freshest
origin/main. If main moves under you, merge it into your branch (never rebase — Frank dislikes the history rewrite). - Work item = Gitea issue on
dank/command-center(Gitea at 192.168.0.143:3000, ownerdank). Small fixes may go straight to a branch; anything with more than one moving part gets an issue first. - Branch naming:
<type>/<issue#>-<slug>, type ∈ feat|fix|docs|chore. - Pull requests target
maindirectly. Merge with--no-ffso feature arcs stay visible in history. Never merge a PR without an explicit OK from Frank. - The moment a PR is open, open it in Frank's browser —
xdg-open <PR-URL>lands a tab in his Firefox; never make him hunt for it in Gitea. - Attribution footer (issues and PRs) — every issue or PR body you open ends
with a
---line, thencwd: <working directory> · session: <omp session id>(session id from/status;cwdis that session's own working directory — not the build checkout, when they differ). Machine-made artifacts stay attributable to the box and session that spawned them. - Releases: tag
vX.Y.Zstraight onmainafter verifying; tags are the deploy markers every other machine pulls to. Hotfixes are ordinary branches off the tag you are fixing forward from. - Trash convention: never
rmanything user-authored — move it to.AGENTTRASH/(gitignored). Deletion is a pruning session with Frank present.
Commit hygiene (mirrors dank/serverAppConfigs)
The law is imported from the serverAppConfigs repo — checkout on the dev box at
~/Projects/serverAppConfig, see its AGENTS.md § Git Workflow and
§ Testing ("All edits flow through git", "tests are mandatory before
commit"). Applied here:
git pullbefore starting new work; start from a clean tree. The checkout IS the deployment here — a dirty tracked file is a half-deployed config serving the live agent.- Commit atomically and early: one commit = one complete functional unit, never a pile of half-edited files; docs land in the same commit as the change they document.
bats tests/green + the pre-commit secret gate = the commit precondition; the full suite runs at pre-push.- Push to the local Gitea as soon as a unit is green — every other
workstation pulls from there.
git * --no-verifyis forbidden (the secret gate is the point of the hook). - Messages: imperative mood, what + why,
Refs #Nwhen a Gitea issue exists.
Live-sync tiers (the machine is a committer too — Refs #11)
The checkout is the deployment, so several tracked files are live state written continuously through symlinks by the harness, auto-learn, and desktop GUIs. "Dirty tree" is judged by the writer:
| Tier | Paths | Commit law |
|---|---|---|
| Human/agent prose | AGENTS.md, omp-docs/, scripts/, plugins/, agent/skills/ |
branch → PR → Frank OK (unchanged) |
| Machine live | agent/memories/, agent/managed-skills/, agent/config.yml, agent/models.yml |
scripts/live-sync.sh heartbeat |
| Desktop live | dotfiles/ |
same heartbeat |
The heartbeat (systemd user timer, hourly) stages ONLY allowlisted tiers,
commits with hooks armed — the secret gate included — merges origin/main,
pushes, and reaps wt/* worktrees older than 7 days. Non-allowlist dirt is
never committed, only surfaced. Conflicts abort and alert (ntfy); it never
force-pushes, auto-resolves, or uses --no-verify. Details:
omp-docs/tools/live-sync.md.
Agent skills
The engineering skills (triage, to-tickets, to-spec, research,
implement, …) read their per-repo configuration from three pages in the
knowledge base. Those pages own their facts; this is only the pointer.
Issue tracker
Work items are Gitea issues on dank/command-center (Dankclaw,
192.168.0.143:3000) — no gh, no tea. See
omp-docs/skills/issue-tracker.md.
Triage labels
The five canonical roles, each label string equal to its name. See
omp-docs/skills/triage-labels.md.
Domain docs
Single-context, inside the existing knowledge base: omp-docs/glossary.md and
omp-docs/decisions/. See omp-docs/skills/domain-docs.md.
Secret gate (HARD rule)
The pre-commit hook (.githooks/pre-commit, enabled via
git config core.hooksPath .githooks) blocks the commit in two ways. Token
shapes (sk-…, ghp_…, github_pat_…, glpat-…, xox…, AKIA…, PEM
headers, JWTs, bcrypt hashes, Authorization: Basic headers) are scanned across
the entire staged diff — every file type, because the first version of the
gate whitelisted extensions and never read a .env, .container, .ini, or
extension-less dotfile at all. Credential assignments (PASSWORD=…,
secret_key: …) are scanned in non-prose files only; markdown keeps shape
detection, because the auto-learned memory tier is prose about credentials and a
gate that fires there gets overridden until it means nothing. The gate fails
closed: if it cannot read the staged diff it refuses the commit rather than
passing it. Quarantine list — never tracked, ever: auth.json, .env and
*.env (postgres.env, frank-job-hunt.env are the ones that carry real
values), trust.json, *.pem, *.key, identity*. Deliberate override:
ALLOW_SECRETS=1 git commit …. Remember why: a secret in a tracked file is a
secret in every agent's context window on every machine that pulls this repo, and
redacting it later does not un-leak the history.
Machine facts (verify before trusting)
| box | role | GPU | notes |
|---|---|---|---|
Dev workstation (blankpad) |
client box, never runs inference | Lenovo laptop, AMD Ryzen 5 PRO 5650U / Cezanne APU (unified memory, s2idle sleep only, no dGPU) | this repo lives here at ~/.omp; the earlier "iGPU + dGPU / ~/GitPackages/llama.cpp" description belonged to the sold dankware box |
| Dankclaw | bench target, secondary models + service stack | Strix Halo APU (unified memory) + RTX 3090 (moved over from Dankserver 2026-09) | prod services frank-dragoon (halogen-flash-server, :7770, iGPU) and frank-zealot (llama.cpp PrismML fork, :7771, 3090); runbooks in the serverAppConfigs vault (~/Projects/serverAppConfig/serverAppConfigs-docs/LLM/); ssh: ssh dankclaw |
| Dankserver | main-model box | 2× Radeon AI PRO R9700 — non-unified (was RTX 3090 until 2026-09) | runs frank-archon (radiance, TP=2, :7770) — this repo's operator session runs on it; ssh: ssh dankserver |
Environment gotchas
- Gitea API needs a token:
GITEA_TOKENenv (gitignored.env). NoteaCLI installed anywhere. Remote URLs carry NO embedded token (2026-10-09 credential-store cutover) — git and the API both read~/.config/secrets/gitea(store --file, URL-line format; seeomp-docs/tools/gitea/client-access.md). That file is never pushed; it is not a substitute for the secret gate. - Passwordless sudo (
sudo -n) works on Dankclaw and Dankserver for systemctl verbs only; user units need no sudo at all. - SSH into both servers is BatchMode-capable from here:
ssh -o BatchMode=yes dankclaw '<read-only command>'— read-only probes are always allowed; state changes on remote boxes are not, without a per-run OK. frank-archon(Dankserver),frank-dragoon, andfrank-zealot(Dankclaw) are production services shared with this repo's operator: never stop, mask, or restart them (or any production service on any box) without an explicit, current, per-run OK from Frank.
File-organization doctrine (why this repo stays readable)
- One concept, one file; directories get an
index.mdlisting their contents — that's the OKF contract, and it is also the anti-rot mechanism: nothing may appear in a directory that itsindex.mddoesn't list. - Root of the repo is capped: config, docs, scripts, harness dirs. A new top-level entry requires a commit message that justifies itself.
- Runtime state (dbs, logs, caches, sessions) is gitignored on principle — if you feel the urge to commit one of those, stop and reread this section.