PR Pipeline — Authorize → Validate → Review → Merge / auth (pull_request) Successful in 5s
PR Pipeline — Authorize → Validate → Review → Merge / validate (pull_request) Successful in 3s
PR Pipeline — Authorize → Validate → Review → Merge / lint (pull_request) Successful in 18s
PR Pipeline — Authorize → Validate → Review → Merge / ai-review (pull_request) Successful in 3s
PR Pipeline — Authorize → Validate → Review → Merge / gate (pull_request) Successful in 1s
The 2026-09-17 purge removed six live credentials that had sat in this repo for weeks, several in .md prose. Nothing blocked that class of commit, so a warning in a stream nobody reads was the only signal. This adds a guard that fails the build instead of warning. Guard - scripts/secret-scan.sh: bash + coreutils + grep/sed/awk + git only (the Gitea Actions runner executes job steps inside the runner container — BusyBox grep, no node/python). Modes: --tree (git-tracked, default), --path DIR (no git), --staged (pre-commit), --diff REF. Exit 1 on a finding, 2 on config error. - scripts/secret-patterns.tsv: checked-in pattern list — sk-, sk-or-v1-, sk_live_, literal Bearer tokens, PVEAPIToken=, raw Authorization values, PEM private-key blocks, prose credential lines, and password/api_key/secret/token assignments carrying a literal value. Prose is scanned exactly like code. - scripts/secret-allowlist.tsv: one entry per deliberate synthetic example, each with a reason. A missing reason is a hard error (fail closed). The 2026-09-17 purge's `«vault: ...»` placeholders are listed explicitly rather than filtered by a general "vault"/"synthetic" rule, so a new occurrence still needs a reviewed, reasoned entry. - A small inert-value classifier drops env refs, paths, dotted code access, variable names and right-truncated redactions; it does not know the words "synthetic"/"example", so a fabrication is always an explicit exception. - Findings are printed with the credential masked; a scan never echoes a full secret into the log. Wiring - .gitea/workflows/pr-pipeline.yaml lint job: explicit "Committed-credential scan" step plus the self-test. A finding fails the required `pr-pipeline / lint` context, which the merge gate depends on. - scripts/prose-lint.sh (the local gate): a "Secret scan" section, so `bash scripts/prose-lint.sh` before pushing is equivalent to CI. Tests - tests/test_secret_scan.sh: 20 cases. Plants pattern-matching fixtures in temp trees (outside every allowlisted path) and asserts the guard FAILS, including the --staged commit-time path; asserts the tree is quiet; asserts allowlisted text at an unlisted path still fails (path-explicit, not word-based); asserts a reasonless allowlist entry exits 2. Verified: guard run against 8245716^ (the pre-fix revision, before the purge) fails on the real OpenRouter/LiteLLM/Zulip/Proxmox/Stirling credentials; guard run over the current tree is clean.
144 lines
6.0 KiB
Markdown
144 lines
6.0 KiB
Markdown
# AGENTS.md — Prose Contracts Repo
|
|
|
|
All agents operating on prose contracts MUST follow this workflow. No exceptions.
|
|
|
|
## The Rule
|
|
|
|
**No agent pushes directly to `main`. All changes go through PRs with automated validation.**
|
|
|
|
## Why
|
|
|
|
Two incidents taught us this:
|
|
|
|
1. **The .117→.19 IP fix** — A stale IP in a contract was "fixed" without verification, breaking Zulip. The fix was right but the approach was wrong. Automated consistency checks would have caught it.
|
|
|
|
2. **The /grafana/ route regression** — An nginx route that was deliberately removed (Jul 2) was re-added to a contract diagram. CI linting now catches this automatically.
|
|
|
|
## Agent Workflow
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ 1. Clone repo → create branch → make changes │
|
|
│ 2. Push branch → open PR │
|
|
│ 3. CI pipeline runs automatically: │
|
|
│ ✅ validate: frontmatter (kind, name, description) │
|
|
│ ✅ lint: structure + regression detection │
|
|
│ ✅ ai-review: LiteLLM reviews diff vs ground truth │
|
|
│ 4. All green → merge PR to main │
|
|
│ 5. Agents run contracts from main │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Branch Protection (enforced by Gitea)
|
|
|
|
| Rule | Enforcement |
|
|
|------|------------|
|
|
| Direct push to `main` | ❌ Blocked (except abiba-bot for emergencies) |
|
|
| PR merge without passing CI | ❌ Blocked — all 3 checks must be green |
|
|
| Status check contexts | `pr-pipeline / validate`, `pr-pipeline / lint`, `pr-pipeline / ai-review` |
|
|
|
|
## What the CI checks for
|
|
|
|
### Stage 1 — Validate
|
|
- YAML frontmatter is valid (proper `---` delimiters)
|
|
- `kind` field is one of: `function`, `responsibility`, `gateway`, `pattern`, `test`, `template`
|
|
- `name` and `description` fields are present
|
|
|
|
### Stage 2 — Lint
|
|
- Responsibility contracts have `## Maintains`
|
|
- Function contracts have `## Parameters` and `## Returns`
|
|
- **Regression rules (automatic rejection):**
|
|
- `/grafana/` nginx route — was reverted Jul 2, must not reappear
|
|
- `CT 122` or `CT 123` as CT ID labels — don't exist in the cluster
|
|
- These rules are hardcoded in `scripts/prose-lint.sh`
|
|
- **Committed-credential guard:** `scripts/secret-scan.sh` FAILS the build on
|
|
credential-shaped strings (patterns in `scripts/secret-patterns.tsv`, prose
|
|
included). Tolerated literals are listed one-per-example with a reason in
|
|
`scripts/secret-allowlist.tsv`; never allowlist a live credential. It runs in
|
|
the CI lint job, in `scripts/prose-lint.sh`, and via
|
|
`bash scripts/secret-scan.sh --staged` before committing.
|
|
|
|
### Stage 3 — AI Review
|
|
- Diff is sent to `syslog-auto` model via LiteLLM
|
|
- Review checks against infrastructure-control ground truth:
|
|
- CT IDs match the PVE cluster inventory in `infrastructure-control.prose.md` Appendix B (100-120 with gaps; no 122/123)
|
|
- Grafana is direct LAN :3001, NOT behind nginx
|
|
- Zulip is CT 117 on storepve (bridge IP .19)
|
|
- Strix Halo :8080 is firewalled to .116 only
|
|
- Review is posted to the PR
|
|
|
|
## Authorization
|
|
|
|
### Who can change what
|
|
|
|
| Contract | Sensitivity | Who can change |
|
|
|----------|------------|----------------|
|
|
| `infrastructure-control.prose.md` | **CRITICAL** — topology source of truth | Abiba, Tanko (Tanko maintains its own CT row) |
|
|
| `proxmox-monitor.prose.md` | **CRITICAL** — deployed monitoring | Abiba only |
|
|
| `hermes-config-template.prose.md` | **HIGH** — all agent configs | Abiba, Mumuni, Tanko |
|
|
| `zulip-health.prose.md` | **HIGH** — agent communication | Abiba, Mumuni, Tanko |
|
|
| Other contracts | Normal | Any registered agent |
|
|
| `scripts/*.sh` | **HIGH** — runtime scripts | Abiba only |
|
|
|
|
### Enforcement (self-policing)
|
|
|
|
The CI does not block based on author identity today (Gitea doesn't support CODEOWNERS natively). Instead, agents self-enforce:
|
|
|
|
1. Before changing a CRITICAL contract, check with Abiba
|
|
2. If you're unsure, tag `@abiba-bot` in the PR description
|
|
3. Abiba reviews CRITICAL contract changes before merge
|
|
|
|
## Emergency Bypass
|
|
|
|
In an emergency (service down, fix must ship immediately):
|
|
1. Push to a branch
|
|
2. Open PR with `[EMERGENCY]` in the title
|
|
3. CI still runs — but if it fails and the fix is verified, abiba-bot can bypass and push directly to main
|
|
4. Post-incident: open a follow-up PR to fix any CI violations
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# Clone
|
|
git clone https://git.sysloggh.net/SyslogSolution/prose-contracts.git
|
|
cd prose-contracts
|
|
|
|
# Create branch
|
|
git checkout -b fix/my-change
|
|
|
|
# Make changes, test locally
|
|
bash scripts/prose-lint.sh # run lint locally before pushing
|
|
|
|
# Push and open PR
|
|
git add -A
|
|
git commit -m "fix: description of change"
|
|
git push origin fix/my-change
|
|
|
|
# Open PR at https://git.sysloggh.net/SyslogSolution/prose-contracts/pulls
|
|
# CI runs automatically — wait for all checks to pass
|
|
# Merge when green
|
|
```
|
|
|
|
## Verification Before Acting
|
|
|
|
**Contracts are leads, not facts.** Live-state fields (IPs, ports, credentials) drift.
|
|
Before acting on any value from a contract, verify it against the live system.
|
|
|
|
Follow the `verify-before-mutate` protocol:
|
|
```bash
|
|
safe-mutate --verify "CMD" [--expect "PATTERN"] --mutate "CMD" [--reason "WHY"]
|
|
```
|
|
|
|
## Writing New Contracts
|
|
|
|
Read the [Authoring Guide](docs/AUTHORING-GUIDE.md) before writing any new contract.
|
|
It covers the full process: verify → draft → lint → review → ship, with templates
|
|
and style rules.
|
|
|
|
## Maintaining this file
|
|
|
|
Keep this file for knowledge useful to almost every future agent session in this project.
|
|
Do not repeat what the codebase already shows; point to the authoritative file or command instead.
|
|
Prefer rewriting or pruning existing entries over appending new ones.
|
|
When updating this file, preserve this bar for all agents and keep entries concise.
|