Files
factory/AGENTS.md
arcodangeandClaude Fable 5 d16f7164cb docs(prd): independent verification — the closer never self-certifies
Operator addition to the closure protocol: the QA gate is held by an
independent verifier subagent — context-free, prompted to REFUTE, repo
+ issues + journals as its only inputs; verdict posted on the milestone,
unresolved refutation blocks. New qa-strategy section extends no-self-
grading to POC exit gates and autonomy promotions (verdict attached to
the artifact it gates), mirroring at process level what the pipelines
do at data level (dual extraction, seeded-wrong fixtures).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-11 20:19:19 +02:00

137 lines
13 KiB
Markdown

# Arcodange Lab — Agent Guide & Operating Rules
The Arcodange lab is a self-hosted home/company platform running on three Raspberry Pi (pi1/pi2/pi3) behind a home Livebox, driven from a MacBook Pro M4 control node. **factory** is the cornerstone admin repo: it provisions the cluster (k3s), defines what gets deployed (ArgoCD app-of-apps), manages cloud/forge state (OpenTofu), and provisions databases (PostgreSQL). Two sibling repos carry workloads: **tools** (platform services — Vault, Prometheus, Grafana, CrowdSec, poolers) and **cms** (the public Nuxt site `arcodange.fr`). Everything is deployed into k3s namespaces via ArgoCD, every secret comes from Vault, and public traffic enters through a Cloudflared Zero-Trust tunnel into the internal Traefik.
```mermaid
%%{init: {'theme':'base'}}%%
flowchart TB
subgraph control["Control node (MacBook Pro M4)"]
ansible["Ansible"]:::proc
tofu["OpenTofu"]:::proc
end
factory["factory repo<br>orchestrator: ansible + argocd + iac + postgres + doc"]:::src
tools["tools repo<br>Vault, Prometheus, Grafana, CrowdSec, poolers"]:::src
cms["cms repo<br>Nuxt site arcodange.fr"]:::src
subgraph cluster["k3s cluster (pi1 server, pi2/pi3 agents)"]
argocd["ArgoCD app-of-apps"]:::proc
vault["Vault + VSO"]:::store
nsapps["namespaces: tools / cms / webapp / erp / ..."]:::proc
traefik["internal Traefik"]:::proc
end
cflared["Cloudflared Zero-Trust tunnel"]:::proc
public["public: *.arcodange.fr"]:::store
ansible -- "provision k3s + base" --> cluster
tofu -- "state in GCS" --> factory
factory -- "defines Applications" --> argocd
argocd -- "deploy charts" --> tools
argocd -- "deploy charts" --> cms
tools --> nsapps
cms --> nsapps
vault -- "inject secrets" --> nsapps
nsapps --> traefik
cflared -- "ingress" --> traefik
public --> cflared
classDef src fill:#2563eb,stroke:#1e40af,color:#fff
classDef proc fill:#059669,stroke:#047857,color:#fff
classDef store fill:#7c3aed,stroke:#6d28d9,color:#fff
```
1. The **control node** (MacBook Pro M4) runs Ansible and OpenTofu — the two hands that build everything.
2. **Ansible** provisions the **k3s cluster** (pi1 server, pi2/pi3 agents) and its base layer.
3. **OpenTofu** manages forge/cloud state, persisted in **GCS** (`gs://arcodange-tf`), serving the **factory** repo's intent.
4. **factory** defines the **ArgoCD app-of-apps**, which is the single deployment authority.
5. **ArgoCD** deploys the Helm charts of **tools** and **cms** into per-app k3s **namespaces**.
6. **Vault** (with the Vault Secrets Operator) injects secrets into those namespace pods — no secrets live in git.
7. Workload pods route through the **internal Traefik**.
8. The **Cloudflared Zero-Trust tunnel** is the only public ingress, forwarding `*.arcodange.fr` traffic into internal Traefik.
---
## Repos at a glance
| Repo | Purpose | Key dirs | How deployed |
|---|---|---|---|
| **factory** | Cornerstone admin repo: provisions the cluster, defines deployments, owns infra state and DBs, holds canonical docs | [ansible/](ansible/) · [argocd/](argocd/) · [iac/](iac/) · [postgres/](postgres/) · [doc/](doc/) | Ansible (cluster + base) + OpenTofu (forge/cloud/PG state); ArgoCD reads its app-of-apps |
| **tools** | Platform services in the `tools` namespace | [hashicorp-vault](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/hashicorp-vault), [prometheus](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/prometheus), [grafana](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/grafana), [crowdsec](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/crowdsec), [pgbouncer](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/pgbouncer) | Helm/Kustomize charts deployed via ArgoCD |
| **cms** | Public Nuxt static site `arcodange.fr` + its zone/email IaC | [chart](https://gitea.arcodange.lab/arcodange-org/cms/src/branch/main/chart), [cloudflare](https://gitea.arcodange.lab/arcodange-org/cms/src/branch/main/cloudflare), [zoho](https://gitea.arcodange.lab/arcodange-org/cms/src/branch/main/zoho) | Helm chart via ArgoCD; Cloudflare/Zoho via OpenTofu |
Self-hosted Gitea is at `gitea.arcodange.lab` (org `arcodange-org`). **Pick the forge tool from the remote**: these repos live on Gitea, so use the `mcp__gitea__*` MCP tools for PRs/issues/releases — `gh` will silently fail.
## The `<app>` join key
One kebab-case identifier — `<app>` — is reused **identically** across the Gitea repo, the PG database + `<app>_role`, Vault (`postgres/creds/<app>`, k8s auth role `<app>`, policies `<app>` / `<app>-ops`, `gitea_cicd_<app>`), the k8s namespace + ServiceAccount, the ArgoCD Application, the GCS state prefix `<app>/main`, and DNS (`<app>.arcodange.lab` / `<app>.arcodange.fr`). Bricks wire together **by name convention, not explicit config**, so a single typo breaks the chain silently. Source of truth: [doc/runbooks/new-web-app/conventions.md](doc/runbooks/new-web-app/conventions.md); concept page (going forward): [vibe/guidebooks/lab-ecosystem/naming-conventions.md](vibe/guidebooks/lab-ecosystem/naming-conventions.md).
## Where knowledge lives
Start at the knowledge-base front door: [vibe/README.md](vibe/README.md). The six `vibe/` folders:
- [vibe/ADR/](vibe/ADR/README.md) — architecture decision records (the *why*); canonical home going forward.
- [vibe/PRD/](vibe/PRD/README.md) — product/project requirement docs, each with a mandatory `STATUS.md`.
- [vibe/investigations/](vibe/investigations/README.md) — numbered investigations (`INV-NNN-slug`), with notebooks when data-heavy.
- [vibe/guidebooks/](vibe/guidebooks/README.md) — tree-docs that map the lab's components (the *how it fits together*).
- [vibe/runbooks/](vibe/runbooks/README.md) — step-by-step operational procedures with `[AGENT]` / `[HUMAN]` markers.
- [vibe/shareouts/](vibe/shareouts/README.md) — handouts and presentations (FRENCH; the one exception to the English rule).
Historical infra docs still live under [doc/](doc/) (ADRs, the new-web-app runbook) — see also `CLUSTER_RECOVERY.md` (at the lab root, **outside** this repo) for tested power-cut recovery.
**Active backlog:** the [AI back-office PRD](vibe/PRD/ai-back-office/README.md) is decomposed into self-contained issues on dated milestones — entry point: its [STATUS backlog map](vibe/PRD/ai-back-office/STATUS.md). Resume protocol: pick the top unblocked issue of the earliest open [erp milestone](https://gitea.arcodange.lab/arcodange-org/erp/milestones); related: [telegram-gateway issues](https://gitea.arcodange.lab/arcodange/telegram-gateway/issues) (owner `arcodange`) and [factory#22](https://gitea.arcodange.lab/arcodange-org/factory/issues/22) (ADR tracking). The erp repo's own `AGENTS.md` carries the same pointers.
## Operating rules for agents
### No-tombstone rule (FOREMOST)
Write every file as **currently true**. NEVER leave "Correction (date): …", "previously X, now Y", changelogs, or "updated to …" notes — git history is the audit trail. The **only** allowed exception is a forward-looking `> [!CAUTION]` about a live operational risk.
### Mermaid preferences
Begin each block with an init directive selecting `theme base` (or `forest`). Define a `classDef` palette legible on both light and dark backgrounds (dark fills + light text), e.g. `classDef src fill:#2563eb,stroke:#1e40af,color:#fff`. Use HTML `<br>` for line breaks (never `\n`). Put a leading space before any label starting with a slash, and escape angle brackets inside labels. **Validate every diagram with the Mermaid MCP** before committing, and **immediately after each diagram add a numbered ordered list restating the same flow in words**.
### Tree-docs (guidebooks & big PRDs)
Guidebooks and large PRDs are written as navigable trees: every file's **first line is a breadcrumb** (ancestors are relative links, current page is bold-unlinked, separator ` > `); **every folder has a `README.md` index hub** (a table of its children — link + one-line summary + status — sorted by importance/sequence, not alphabetically); **cross-references are bidirectional** (if A links B, B links A); use numbered file prefixes only for ordered narratives; **stamp `Last Updated: 2026-06-23` at each tree root**.
### Optimized ADR format (MADR-lite)
Sections: **Context / Decision / Consequences / Alternatives / QA & validation / References**. Once an ADR is **Accepted, the body is immutable** — only the status field mutates (Proposed → Accepted → Superseded). The canonical home going forward is [vibe/ADR/](vibe/ADR/README.md); [doc/adr/](doc/adr/README.md) stays as the historical record.
### Investigations
Prefer a **single `INV-NNN-slug.md`** when the finding fits in one file. When data-heavy, write a short stub `.md` beside a same-named folder containing notebooks (`.ipynb` + paired `.py`), a `_data/` dir, and a plain-language `notebook_simple.md` (visuals anyone can read).
### PRD convention
**One subfolder per PRD.** A `STATUS.md` is **mandatory** and must be updated whenever something ships. Big PRDs use tree-docs and **must detail a QA strategy**. The flow is Problem → … → QA strategy → STATUS.md.
### PR crosslinking (bidirectional)
**Every PR body references the ADR/PRD it advances.** In return, the ADR's **References** section and the PRD's **STATUS.md** link back to that PR. Links must be bidirectional — never one-way.
### Guidebook maintenance
Altering a component that is documented in `guidebooks/` **requires updating that guidebook page in the same change**. A code/infra change that leaves its guidebook stale is incomplete.
### Milestone closure & doc freshness
Docs describe intent; **STATUS.md + git describe reality**. Writer's half: a milestone is closed only after the closure sweep, which **opens with a QA gate run by an independent, context-free verifier subagent prompted to refute** — the closer never self-certifies (acceptance criteria re-verified with evidence, test suites green, status flips backed by eval results — nothing documented as done before it is proven done) and **ends with doc-surface QA** (link/anchor check, deprecation grep for the claims the milestone retired, fresh-reader smoke test by a context-free subagent); in between: flip the PRD `STATUS.md` phase row, re-baseline the roadmap at the boundary, truth-pass every leaf/`AGENTS.md`/`SKILL.md` claim the increment invalidated (no-tombstone, bump Last Updated on changed files only). Canonical checklist: the [ai-back-office STATUS closure protocol](vibe/PRD/ai-back-office/STATUS.md). Continuously: a PR that makes a documented claim false updates that doc **in the same PR**. Reader's half: before acting on any versionable claim, verify in trust order — **live system > code/git log > STATUS > leaves > memories/plans**; a page whose Last Updated predates the newest closed milestone in its area is suspect.
### Language policy
**English** for everything in `vibe/` and for `AGENTS.md`/`CLAUDE.md` (this tree is for LLM agents). The single exception: **shareouts handouts are FRENCH**.
## The cohort + workflow
These personas are spawned via the Agent tool or a Workflow; document and reuse them across sessions:
| Persona | Role |
|---|---|
| **Lab Cartographer** | Explores the three repos and maps them into guidebooks (tree-docs). Read-mostly; never edits infra. |
| **ADR Scribe** | Writes optimized MADR-lite ADRs; enforces immutability (status-only mutation) and PR crosslinks. |
| **PRD Architect** | Writes PRDs (Problem → … → QA strategy → STATUS.md); uses tree-docs for big ones. |
| **Runbook Engineer** | Writes step-by-step runbooks with `[AGENT]` (read-only, safe) and `[HUMAN]` (prod-mutating, needs approval) markers. |
| **Investigator** | Writes investigations: a single `INV-NNN-slug.md` when possible, else a stub `.md` beside a same-named folder with `.ipynb` + paired `.py`, a `_data/` dir, and `notebook_simple.md`. |
| **Diagram Smith** | Authors and validates every mermaid diagram with the Mermaid MCP validator; enforces the mermaid preferences + the ordered-list-after-diagram rule. |
| **Continuity Warden** | The adversarial reviewer: checks the no-tombstone rule, breadcrumb depth, bidirectional links, dead links, naming, STATUS/PR crosslinks, and the Last Updated stamp. |
**Recommended workflow for substantial `vibe/` contributions:**
1. **Scaffold** — folders + README hubs + templates.
2. **Author** — personas in parallel, each on distinct files.
3. **Validate** — Diagram Smith runs the Mermaid MCP on every diagram.
4. **Review** — Continuity Warden runs the adversarial checklist.
5. **Assemble** — wire cross-refs, run the dead-link self-test, stamp `Last Updated`.