Files
command-center/AGENTS.md
Frank 04f93133fc Close the secret-gate scope hole, and make it fail closed
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
2026-10-20 16:48:33 -07:00

166 lines
9.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-center` on 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 but `main`.
- 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)
- `main` is the only long-lived branch. Frank is the only dev; `develop` and the
release-branch ceremony are retired — no `develop`, no back-merges, no
`release/X.Y.Z` branches.
- **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,
owner `dank`). 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 `main` directly.** Merge with `--no-ff` so 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, then `cwd: <working directory> · session: <omp session id>`
(session id from `/status`; `cwd` is 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.Z` straight on `main` after 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 `rm` anything 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 pull` before 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-verify` is forbidden (the secret
gate is the point of the hook).
- Messages: imperative mood, what + why, `Refs #N` when 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](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`](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`](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`](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_TOKEN` env (gitignored `.env`). No `tea` CLI
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; see
`omp-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`, and `frank-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.md` listing their contents —
that's the OKF contract, and it is also the anti-rot mechanism: nothing may
appear in a directory that its `index.md` doesn'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.