docs: main is protected, ship by PR (local-dev#42)
Gitea will refuse direct pushes to main in this repo. Rule 5's reviewed lane was enforced on the merge tools (tbd_ship / pr_merge, gitea-mcp v0.17.0/v0.18.0) but not on a direct commit; this overrides rule 5's "risk/low straight to main" for this repo and says why. Derived adapter files regenerated where the repo has them. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SZkhi49hfeSqBp5aZJNXsM
This commit is contained in:
+16
-4
@@ -37,9 +37,9 @@
|
|||||||
|
|
||||||
### Git
|
### Git
|
||||||
- Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`
|
- Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`
|
||||||
- **Trunk-Based Development:** commit directly to main. One logical change per commit.
|
- **Trunk-Based Development:** one logical change per commit, merged to main by PR (`main` is protected, see below).
|
||||||
- Run `task check` locally before every push. CI is the quality gate, not branch protection.
|
- Run `task check` locally before every push. CI is the quality gate, not branch protection.
|
||||||
- No feature branches, no PRs for solo/agent work.
|
- Short-lived branches and PRs are how work reaches `main` here (`tbd_ship`/`pr_merge`); no long-lived feature branches.
|
||||||
- Exception: if a parallel agent session is active on this repo, use a short-lived
|
- Exception: if a parallel agent session is active on this repo, use a short-lived
|
||||||
`agent/<description>` branch and merge within the same session.
|
`agent/<description>` branch and merge within the same session.
|
||||||
|
|
||||||
@@ -79,8 +79,8 @@ When acting as a coding agent on this project:
|
|||||||
3. If unsure about a convention, check `DECISIONS.md` or ask
|
3. If unsure about a convention, check `DECISIONS.md` or ask
|
||||||
4. Never modify files outside the project root without explicit permission
|
4. Never modify files outside the project root without explicit permission
|
||||||
5. When adding a dependency, explain why in the commit message
|
5. When adding a dependency, explain why in the commit message
|
||||||
6. Commit directly to main. Run `task check` before every push. Never create
|
6. Ship by PR (`main` is protected, see below). Run `task check` before every push. Never create
|
||||||
feature branches unless a parallel agent is simultaneously active on this repo.
|
long-lived feature branches.
|
||||||
7. For client projects: never send code or context to cloud APIs — use local models via LiteLLM
|
7. For client projects: never send code or context to cloud APIs — use local models via LiteLLM
|
||||||
|
|
||||||
## Current state — v0.2.5 (2026-05-17)
|
## Current state — v0.2.5 (2026-05-17)
|
||||||
@@ -105,3 +105,15 @@ Current main: `e31fd3f`. CI green. Deployed via Flux.
|
|||||||
|
|
||||||
2. **Issue #19** — end-to-end mirror flow verification.
|
2. **Issue #19** — end-to-end mirror flow verification.
|
||||||
`repo_mirror_push` is implemented but the full flow (create repo → add push mirror → verify sync to GitHub) has not been tested manually. Do this before relying on it in production.
|
`repo_mirror_push` is implemented but the full flow (create repo → add push mirror → verify sync to GitHub) has not been tested manually. Do this before relying on it in production.
|
||||||
|
|
||||||
|
## `main` is protected: ship by PR (local-dev#42, decided 2026-10-08)
|
||||||
|
|
||||||
|
Gitea refuses direct pushes to `main` in this repo, and a PR merges only after its CI status (`CD / Lint / Test / Vet (pull_request)`) passes.
|
||||||
|
**Why:** local-dev rule 5's reviewed lane (risk/medium+ is reviewed by a human) was enforced on the merge tools
|
||||||
|
(`tbd_ship`, `pr_merge`: gitea-mcp v0.17.0/v0.18.0, merge only for an issue labelled exactly `risk/low` with green CI)
|
||||||
|
but not on a direct commit to `main`, which bypassed it. This overrides rule 5's "risk/low commits straight to main"
|
||||||
|
for this repo; control code is where the lane matters.
|
||||||
|
**How:** `tbd_ship` for a one-file change, or branch -> PR -> `pr_merge` (name the `risk/low` issue); above `risk/low`
|
||||||
|
open the PR and a human merges it in the Gitea UI after review. Tags are not protected. CD jobs push image tags to
|
||||||
|
the **infra** repo with deploy keys, never to this repo's `main`. Undo (Mathias only): Settings -> Branches, or
|
||||||
|
`PATCH /repos/mathias/gitea-mcp/branch_protections/main {"enable_push": true}`.
|
||||||
|
|||||||
+16
-428
@@ -3,430 +3,6 @@ Follow all conventions from both the root agent context and project context.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Agent context — Mathias workspace
|
|
||||||
|
|
||||||
<!-- Canonical root context for all AI coding agents.
|
|
||||||
Lives at: ~/dev/.context/AGENT.md
|
|
||||||
Applies to every project under ~/dev/ unless overridden.
|
|
||||||
|
|
||||||
Run `task context:sync` from ~/dev/ to regenerate harness-specific files.
|
|
||||||
Project-level context in .context/PROJECT.md layers on top of this. -->
|
|
||||||
|
|
||||||
## Who I am
|
|
||||||
|
|
||||||
I'm Mathias, a digital product manager and technology consultant based in Sweden.
|
|
||||||
I build software, research emerging tech, and deliver consulting engagements
|
|
||||||
for clients under NDA. I work across AI/ML, financial automation, web applications,
|
|
||||||
and climate/sustainability tech.
|
|
||||||
|
|
||||||
## How I work with agents
|
|
||||||
|
|
||||||
- I think like a product manager — I care about *why* before *how*
|
|
||||||
- I want agents to be opinionated and push back, not just execute blindly
|
|
||||||
- I prefer concise responses; skip ceremony and get to the point
|
|
||||||
- When I say "build this", I mean production-quality with tests, not a demo
|
|
||||||
- Ask me before making irreversible changes or adding heavy dependencies
|
|
||||||
- I work with confidential client data — never send it to cloud APIs unless I explicitly say it's OK
|
|
||||||
|
|
||||||
## Behavior rules
|
|
||||||
|
|
||||||
These rules apply to every task across every project, regardless of harness.
|
|
||||||
|
|
||||||
0. **Pre-task ritual — before ANY implementation (non-negotiable).** Run this before writing a single line:
|
|
||||||
- **Query the brain** (`brain_query`) for the domain + symptom. If the result changes your approach, surface it before acting. 5 seconds beats 5 hours.
|
|
||||||
- **Load the relevant skill** — see trigger table in *Engineering Skills* below.
|
|
||||||
- **Write the failing test first.** Name the test before the function. If the target is untestable (e.g. `main()` wiring), extract the logic into a testable function first. No implementation without a red test.
|
|
||||||
- **State the observable success criterion** — what specific behavior, output, or passing test proves this is done?
|
|
||||||
|
|
||||||
**TDD is non-negotiable.** "Tests pass" is not proof of correctness — only proof the tests ran. Write tests that would catch the bug before writing code that fixes it.
|
|
||||||
|
|
||||||
1. **No assumptions.** Don't hide confusion — surface it. Surface tradeoffs explicitly.
|
|
||||||
Think before coding; if the problem is unclear, ask or state assumptions before acting.
|
|
||||||
2. **Minimum viable code.** Solve with the smallest change that works. Nothing
|
|
||||||
speculative, no "while we're here" cleanups, no premature abstractions. Simplicity first.
|
|
||||||
3. **Surgical changes.** Touch only what the task requires. Leave unrelated code,
|
|
||||||
files, and formatting alone. Diffs should be small and reviewable.
|
|
||||||
4. **Goal-driven execution.** Define clear success criteria up front for every task.
|
|
||||||
Loop — implement, verify, refine — until those criteria are met. Don't claim
|
|
||||||
completion without evidence (tests pass, command output, observed behavior).
|
|
||||||
5. **Trunk-Based Development — commit directly to main.** Every commit is one
|
|
||||||
logical change (one tool, one fix, one test) with passing tests. Main is always
|
|
||||||
deployable. Never create long-lived feature branches.
|
|
||||||
|
|
||||||
**The PR trigger is RISK, not concurrency.** `risk/low` work commits straight to
|
|
||||||
main with CI as the gate — that is most work and is unchanged. `risk/medium` and
|
|
||||||
above opens a PR, even solo, even with nobody waiting to review it. This mirrors
|
|
||||||
the tiered merge model in `assessor-loop`
|
|
||||||
`method/transfer-to-a-regulated-organisation.md` §4.1, so the estate practises
|
|
||||||
what it sells instead of merging everything on green and claiming otherwise.
|
|
||||||
|
|
||||||
**Concurrency is NOT a reason to branch.** Measured 2026-08-29 across three real
|
|
||||||
collisions between parallel sessions: two were not branching problems at all — a
|
|
||||||
generated artifact written to the same path by both (branching turns that into a
|
|
||||||
merge conflict on a 33k-line JSON), and uncommitted work in a shared checkout
|
|
||||||
(a per-session worktree fixes it) — and the third was an ordinary non-fast-forward
|
|
||||||
push resolved by rebase in seconds. Use a worktree per session on shared repos and
|
|
||||||
timestamp generated artifacts. Do not reach for a branch.
|
|
||||||
|
|
||||||
Branch+PR flow carries documented costs on solo repos: merging auto-closes gated
|
|
||||||
issues, review comments live on a thread separate from the issue's, and a
|
|
||||||
merged-and-deleted branch cannot be amended (brain:
|
|
||||||
`gitea-pr-merge-autoclose-and-review-comment-footguns`). Those are the price of
|
|
||||||
the `risk/medium`+ lane — paid deliberately, not incurred by accident.
|
|
||||||
|
|
||||||
**Exception — external contributor or client four-eyes requirement:** Use PR flow
|
|
||||||
when a human reviewer outside the project is required. Document the reason in
|
|
||||||
PROJECT.md.
|
|
||||||
|
|
||||||
**Exception — a GitOps repo where a branch cannot be reconciled.** In a repo whose
|
|
||||||
deployment watches one branch (`mathias/infra`: Flux watches `main`), a branch is
|
|
||||||
invisible to the thing that applies it. A `risk/medium` change to `alerts.yaml` or
|
|
||||||
`litellm-config.yaml` can be linted on a branch but never *exercised* there, so a
|
|
||||||
PR would buy a review of a state that was never deployed.
|
|
||||||
|
|
||||||
There, `risk/medium`+ work stays on `main` and instead requires a **recorded
|
|
||||||
adversarial review pass before the push** — `skills:pr-gatekeeper` or equivalent,
|
|
||||||
reviewing the staged diff. **The reviewer's verdict goes in the issue, not a PR
|
|
||||||
thread.** No verdict recorded means the lane was not used, and saying "the gate was
|
|
||||||
green" is not the same claim.
|
|
||||||
|
|
||||||
Be honest about what this is: a recorded *independent* pass, not four-eyes in the
|
|
||||||
sense `assessor-loop` §4.1 means for a regulated organisation, because the reviewer
|
|
||||||
is an agent. It is more than the repo had and less than the model it mirrors; do not
|
|
||||||
describe it as the stronger property. (infra#470, decided 2026-09-29.)
|
|
||||||
|
|
||||||
This lane depends on the `risk/` label being right. A label that triggers a heavier
|
|
||||||
lane is load-bearing, not cosmetic — if work that belongs in it is unlabelled, the
|
|
||||||
lane silently does not apply.
|
|
||||||
|
|
||||||
6. **Close the loop — every substantive task ends with the same ritual.** Shipping
|
|
||||||
the code is not the end of the task; capturing it is. Run this unprompted:
|
|
||||||
- **Tag + bump SemVer** on the change (annotated tag; minor for a feature or
|
|
||||||
new/changed ADR, patch for a fix; docs in the same commit). Check the repo's
|
|
||||||
actual last tag — stated versions in docs drift stale.
|
|
||||||
- **Push** main and the tag (CI is the gate).
|
|
||||||
- **Persist generalizable learnings to the brain** (`brain_write`, wing/hall) —
|
|
||||||
the reusable patterns and the footguns that would bite anyone again, never
|
|
||||||
project status. See *Knowledge base — when to write* below.
|
|
||||||
- **File discovered-but-deferred work as tracker issues** on the project's own
|
|
||||||
repo — token-budget gaps, recorded ADR limitations, v2 follow-ups. Don't let
|
|
||||||
"out of scope, recorded" rot in a commit message; make it a ticket with a
|
|
||||||
source pointer.
|
|
||||||
- Surface the brain entries and issue numbers in the closing summary so the
|
|
||||||
trail is auditable.
|
|
||||||
|
|
||||||
**Chat is not a durable store. Never rely on Mathias remembering anything.**
|
|
||||||
Every gap, caveat, risk, open question and next step must land in one of four
|
|
||||||
places before the turn ends: **the brain**, **a repo** (code, docs, ADR),
|
|
||||||
**a tracker issue**, or **a plan file**. A finding stated only in conversation is
|
|
||||||
lost the moment the session closes — and this estate has repeatedly proved that a
|
|
||||||
remembered convention is an unreliable control.
|
|
||||||
|
|
||||||
This applies hardest to what is tempting to leave as a closing remark: "worth
|
|
||||||
knowing X isn't enforced yet", "this is a convention, not a gate", "we should
|
|
||||||
revisit Y". Those are exactly the items that need a locator. If it is worth
|
|
||||||
saying in a summary, it is worth writing where it can be found again without you.
|
|
||||||
|
|
||||||
7. **Finish what's closable — only involve me for a genuine call.** If a
|
|
||||||
tracker issue, PR, or checklist item is demonstrably resolved — the fix
|
|
||||||
landed, the state changed, you verified it live — close/merge/check it off
|
|
||||||
in the same turn. Don't leave it open "for the record" or park it waiting
|
|
||||||
on me to confirm something that already happened; that's bookkeeping, not
|
|
||||||
a decision. Reserve asking me (`AskUserQuestion`, a `DECISION NEEDED`
|
|
||||||
comment, or otherwise pausing) for when it's genuinely mine to call:
|
|
||||||
irreversible or prod/host-impacting actions, a real judgment call with no
|
|
||||||
clearly-better option, ambiguous intent, or anything secret-writes/
|
|
||||||
destructive-git already gate. Before claiming something is closable,
|
|
||||||
verify it live (re-check the issue, re-run the command) rather than
|
|
||||||
trusting your own earlier summary (brain: `filed-issue-not-same-as-fixed`).
|
|
||||||
|
|
||||||
8. **Control code needs an adversary, not a checklist.** Code whose job is to
|
|
||||||
refuse, deny, gate or contain is not adequately verified by a green gate.
|
|
||||||
Rule 5 makes CI the quality gate and that holds for ordinary work — but a
|
|
||||||
passing suite only asserts what its author already thought of, and for a
|
|
||||||
control the author is precisely the person whose imagination just failed.
|
|
||||||
Measured on `dispatch#75`: five review rounds, five real escapes verified by
|
|
||||||
execution, and **CI was green on every one of them** while `git rm
|
|
||||||
././.dispatch-allow` deleted the file the control existed to protect. So
|
|
||||||
route this work through a review with authority to block, instructed to
|
|
||||||
verify by execution rather than by reading the implementation. Expect to be
|
|
||||||
blocked more than once; that is the control working, not the reviewer being
|
|
||||||
difficult. (brain: `a-green-gate-is-not-evidence-for-control-code`; the
|
|
||||||
practices live in the `pr-gatekeeper` agent.)
|
|
||||||
|
|
||||||
9. **A test's name must not claim more than its assertions prove.** Name a test
|
|
||||||
after the mechanism it observes, not the goal it serves.
|
|
||||||
`DoesNotInheritEnv` is honest; `IsContained` is a claim the test cannot
|
|
||||||
support. This is not pedantry about naming — when name and assertion
|
|
||||||
diverge, the name wins in the reader's head and the gap becomes invisible,
|
|
||||||
which is what lets the defect survive review. Found three times in one PR,
|
|
||||||
twice in tests written to fix the previous instance of it; each passed while
|
|
||||||
the thing it was named for was exploitable. If you cannot name the mechanism,
|
|
||||||
you do not yet know what the test observes.
|
|
||||||
|
|
||||||
## CAD sprint patterns
|
|
||||||
|
|
||||||
Two CAD sprint archetypes (defs: brain `wiki/homelab/decisions/continuous-agentic-development-cad-concept-2026-06-16`):
|
|
||||||
- **Inception Sprint** — birth a new repo. Oath-gated close; tag only once every clause holds. Brain: `.../inception-sprint-and-oath`.
|
|
||||||
- **Sweep Sprint** — clear an existing backlog. Parallel read-only research per issue → sequential one-issue-per-commit execution. Brain: `.../sweep-sprint-and-oath`.
|
|
||||||
|
|
||||||
Shared: decisions that are yours, not the agent's, surface as `DECISION NEEDED` (question/options/recommendation/blocks) — ask live if you're present, else comment and wait, never act past it. Live-breaking regressions get `git revert`, never forward-fixed; host-network work skips even read-only research (solo, synchronous, you present). Rule 6's closing ritual only fires once every Oath clause genuinely holds — blocked = descoped-and-tracked, never marked done.
|
|
||||||
|
|
||||||
## Default stack
|
|
||||||
|
|
||||||
| Layer | Default | Fallback | Last resort |
|
|
||||||
|-------|---------|----------|-------------|
|
|
||||||
| Language | Go | Python | TypeScript, Java, C |
|
|
||||||
| UI | HTMX + Templ | Server-rendered HTML | React (only if SPA is justified) |
|
|
||||||
| Build | Task (taskfile.dev) | Make | — |
|
|
||||||
| Containers | Docker Compose (dev), k3s (prod) | — | — |
|
|
||||||
| DB | PostgreSQL + sqlc | SQLite | — |
|
|
||||||
| Search | pgvector (vector), BM25 | Qdrant (when >1M vectors or hybrid retrieval) | — |
|
|
||||||
| Logging | slog (structured) | stdlib `logging` w/ structured `extra=` (or structlog) | — |
|
|
||||||
| Testing | Table-driven, testify | — | — |
|
|
||||||
| Agents (Go) | google.golang.org/adk + pkg/litellm adapter | — | — |
|
|
||||||
|
|
||||||
Exploratory: Rust, Zig — I'll tell you when I want these.
|
|
||||||
|
|
||||||
## Code conventions
|
|
||||||
|
|
||||||
- **Go style**: golines, gofumpt, golangci-lint
|
|
||||||
- **Python style** (fallback language): ruff (format+lint, one tool), mypy --strict (non-negotiable,
|
|
||||||
matches Go's static typing discipline), pytest + pytest-cov (table-driven via
|
|
||||||
`@pytest.mark.parametrize`), uv (venv+deps+lock, one tool), pydantic-settings (typed env-var config
|
|
||||||
— same principle as Go's typed structs), src-layout + `pyproject.toml` only (no `setup.py`)
|
|
||||||
- **Errors**: `fmt.Errorf("operation: %w", err)` — never naked, never log-and-return.
|
|
||||||
Python: `raise X from e` (exception chaining, same principle) — never bare `except`, never silent `pass`
|
|
||||||
- **Naming**: stdlib conventions, no stuttering
|
|
||||||
- **Architecture**: prefer stdlib over frameworks, constructor injection, env-var config parsed into typed structs
|
|
||||||
- **Git**: conventional commits (`feat:`, `fix:`, `chore:`), commit directly to main,
|
|
||||||
one logical change per commit, CI is the quality gate
|
|
||||||
- **Never**: long-lived feature branches, PRs for solo work, direct push without
|
|
||||||
passing `task check` locally first
|
|
||||||
- **Security**: no secrets in code, govulncheck before adding deps, SOPS for encrypted config
|
|
||||||
- **Dependencies**: prefer stdlib. testify, slog, templ, sqlc, google.golang.org/adk (agent projects only) are pre-approved; anything else needs justification in the commit message
|
|
||||||
|
|
||||||
## Secret handling (every harness, every command)
|
|
||||||
|
|
||||||
Tool output is persisted: terminal → `~/.claude/projects` transcripts →
|
|
||||||
claudewatcher → brain/wiki → gitea history. A secret printed once is
|
|
||||||
searchable forever, and clearing it means rotating the key. So:
|
|
||||||
|
|
||||||
1. **Never print, echo, log, or transform a secret to inspect it.** No
|
|
||||||
`base64`/`xxd`/`cat` of a key, and never pipe a secret through a transform
|
|
||||||
to defeat `op run`'s output masking (it masks raw values; base64 hides them
|
|
||||||
from the mask — that exact trick leaked a key on 2026-06-11).
|
|
||||||
2. **Secrets stay in the subprocess.** Reference them only as env vars consumed
|
|
||||||
*inside* `op run --env-file ~/.op-env -- <cmd>`. Never place a literal secret
|
|
||||||
in a command's argv (it lands in the tool call and the transcript).
|
|
||||||
3. **Existence check without revealing the value:** `[ -n "$X" ] && echo set` —
|
|
||||||
never `${X:-...}` (returns the value when set) and never echo a substring of it.
|
|
||||||
4. **Cross-host secrets:** run the secret-consuming command on the host that has
|
|
||||||
the secret; do not forward a raw key over ssh argv/stdout.
|
|
||||||
5. If a secret does leak into output, say so immediately and flag it for rotation —
|
|
||||||
don't bury it.
|
|
||||||
|
|
||||||
## Infrastructure
|
|
||||||
|
|
||||||
Three machines on Tailscale:
|
|
||||||
|
|
||||||
| Machine | Role | Key specs |
|
|
||||||
|---------|------|-----------|
|
|
||||||
| koala | GPU inference, heavy compute | RTX 5070, runs k3s + llama-swap + shared postgres18/pgvector |
|
|
||||||
| iguana | Services, builds | M2 Ultra Mac |
|
|
||||||
| flamingo | Daily driver, edge | Apple Silicon Mac, ~/dev is here |
|
|
||||||
|
|
||||||
- **Model routing**: LiteLLM in front of llama-swap (local) + cloud APIs (when permitted)
|
|
||||||
- **Orchestration**: k3s on koala only — single control-plane node, no workers (verified 2026-08-30). iguana and flamingo are on the tailnet but are not cluster members
|
|
||||||
- **Networking**: Tailscale mesh
|
|
||||||
|
|
||||||
## Project landscape
|
|
||||||
|
|
||||||
All development repos live at `~/dev/` (softlink from `~/Documents/local-dev/`).
|
|
||||||
|
|
||||||
Organized in thematic folders:
|
|
||||||
|
|
||||||
| Folder | Focus | Count |
|
|
||||||
|--------|-------|-------|
|
|
||||||
| `GO/` | Go web frameworks, API integrations, learning projects | ~10 |
|
|
||||||
| `AI/` | ML research, AI frameworks (FinRL, DSPy, crawl4ai) | ~6 |
|
|
||||||
| `AGENTS/` | Autonomous agents, coding agents, MCP servers, infra | ~15 |
|
|
||||||
| `QKX/` | Invoice processing, financial automation, payment systems | ~13 |
|
|
||||||
| `XT/` | Climate data, sustainability (Klimatkollen, Garbo) | ~2 |
|
|
||||||
|
|
||||||
See `~/dev/PROJECT_SUMMARY.md` for detailed descriptions of each project.
|
|
||||||
|
|
||||||
### Key active projects
|
|
||||||
|
|
||||||
- **super-koala** (`AGENTS/`) — multi-component agent stack with LangGraph, DSPy, MCP
|
|
||||||
- **azure-tiger** (`QKX/`) — invoice extraction → ISO 20022 payment instructions
|
|
||||||
- **gocrwl** (`AGENTS/`) — Go web crawler with containerized deployment
|
|
||||||
- **koala-ai-stack** (`AGENTS/`) — local AI server infrastructure management
|
|
||||||
- **klimatkollen** (`XT/`) — Swedish municipal climate data platform
|
|
||||||
|
|
||||||
## Knowledge base — actively use it
|
|
||||||
|
|
||||||
A persistent brain (BM25 search + LLM-synthesised Q&A) survives across sessions,
|
|
||||||
hosts, and harnesses. It holds 100+ hard-won entries: infra incident postmortems,
|
|
||||||
Go pitfalls, framework gotchas, design principles, ADRs. **It is not optional
|
|
||||||
reference material — query it actively, not just when explicitly told.**
|
|
||||||
|
|
||||||
### When to query (treat as a reflex)
|
|
||||||
|
|
||||||
- **Before** starting a non-trivial task — search for prior art with the symptom
|
|
||||||
AND the system component ("how did we solve X in Y?"). 5 seconds beats 5 hours.
|
|
||||||
- **When debugging** — search for the error string, the stack frame, the affected
|
|
||||||
service. Past you may have already paid this tax.
|
|
||||||
- **Before adopting** a pattern, library, framework, or model name — check if it
|
|
||||||
was tried and rejected, or what the integration footguns are.
|
|
||||||
- **When making architectural decisions** — search for the domain + "ADR" or
|
|
||||||
"decision" to find prior reasoning before re-deriving it.
|
|
||||||
- **When a recommendation feels novel** — challenge yourself: "has this been
|
|
||||||
documented?" The brain often has it.
|
|
||||||
|
|
||||||
### When to write
|
|
||||||
|
|
||||||
After you discover something that **future-you would forget** and that **isn't
|
|
||||||
recoverable from the code, git log, or PR description alone**:
|
|
||||||
|
|
||||||
- Bugs whose root cause is non-obvious and generalisable beyond this project.
|
|
||||||
- Framework / library / model-name quirks that bit you and would bite anyone.
|
|
||||||
- Design principles validated under fire (e.g. "every `_get` needs a `_list`").
|
|
||||||
- Postmortems for incidents: what broke, why, how diagnosed, what to do next time.
|
|
||||||
|
|
||||||
DON'T write project status, sprint progress, PR summaries, or "what I did this
|
|
||||||
session" — those rot fast and the originals are in git/gitea anyway. Brain
|
|
||||||
entries that age well are about *why*, *how to avoid*, and *what to do when*.
|
|
||||||
|
|
||||||
### How to access (per harness)
|
|
||||||
|
|
||||||
| Harness | Query | Write |
|
|
||||||
|---------|-------|-------|
|
|
||||||
| **Claude Code, Claude Desktop** | `brain_query` (BM25), `brain_answer` (LLM-synth + sources) MCP tools | `brain_write` MCP tool |
|
|
||||||
| **Crush, Pi, Antigravity, other MCP-capable** | same MCP server: `ingestion-brain` (via the `mcp__*_brain__*` namespace once authenticated) | same |
|
|
||||||
| **Anything HTTP-only (curl, scripts)** | Tailnet only: `POST https://koala.tail30472e.ts.net:8443/query` with `{"query":"..."}`, sending `BRAIN_MCP_TOKEN` (not yet enforced on REST, hyperguild#149). The public `brain-mcp.d-ma.be` serves `/mcp` only (infra#517). | `POST .../write` with `{"content":"...","wing":"...","hall":"..."}` |
|
|
||||||
| **Browser / human inspection** | `https://git.d-ma.be/mathias/hyperguild` → `knowledge/` and `wiki/` markdown files |
|
|
||||||
|
|
||||||
- **Scoping — the brain holds no confidential client content.** Nothing on the read path
|
|
||||||
filters by client: every token holder can read every wing, and `wing` on `brain_query`
|
|
||||||
is an optional argument, not an access control. Client confidentiality is enforced only
|
|
||||||
at write time (`capture`'s I1 gate refuses internal/confidential). So never `brain_write`
|
|
||||||
client material; generalise it first or keep it in the client's own repo. (hyperguild#144,
|
|
||||||
decided 2026-10-01. This line previously claimed client projects were filtered; they
|
|
||||||
were not.)
|
|
||||||
- **Routing**: brain_answer's LLM uses berget.ai as primary, iguana ollama as
|
|
||||||
fallback. Both are configurable in the `supervisor/ingestion-deployment.yaml`
|
|
||||||
on the koala k3s cluster; don't hardcode local-only model names into the
|
|
||||||
berget URL (see knowledge entry on namespace mismatches).
|
|
||||||
|
|
||||||
### Quick reflex checks
|
|
||||||
|
|
||||||
If you find yourself about to say any of these out loud, you owe yourself a brain query first:
|
|
||||||
|
|
||||||
- "I think the issue might be..."
|
|
||||||
- "Let me try X and see..."
|
|
||||||
- "I'll just write a script to..."
|
|
||||||
- "This is probably a new bug..."
|
|
||||||
- "Has anyone done this before?" — *yes, probably, go check.*
|
|
||||||
|
|
||||||
## Client work rules
|
|
||||||
|
|
||||||
When working on a project tagged with a client name:
|
|
||||||
1. Never send code, data, or context to cloud APIs — use local models only
|
|
||||||
2. Never reference other client projects or their data
|
|
||||||
3. Keep all artifacts within the client's git org / directory
|
|
||||||
4. Treat everything as confidential unless told otherwise
|
|
||||||
|
|
||||||
## Harness-agnostic principles
|
|
||||||
|
|
||||||
Supported harnesses (2026-09-01) — Cursor and Aider were dropped; do not
|
|
||||||
re-add adapters for them:
|
|
||||||
- Claude Code, Antigravity, OpenCode, Mistral Vibe, Crush, Pi Coding Agent
|
|
||||||
- Open WebUI, Charmbracelet Mods, or any tool that takes a system prompt string
|
|
||||||
- Any other tool that reads `AGENTS.md`, which is the de facto convention
|
|
||||||
|
|
||||||
The canonical source is always `.context/AGENT.md` (root) and `.context/PROJECT.md` (per-project).
|
|
||||||
Derived files are committed (see *How context propagates* below) so a `git pull` on any host yields full agent context with no setup.
|
|
||||||
|
|
||||||
## How context propagates
|
|
||||||
|
|
||||||
Canonical sources of truth:
|
|
||||||
- Universal: `~/dev/.context/AGENT.md` (this file)
|
|
||||||
- Project: `<repo>/.context/PROJECT.md` (per-repo)
|
|
||||||
|
|
||||||
Derived files (committed, regenerated by `task context:sync`):
|
|
||||||
- `CLAUDE.md`, `AGENTS.md`, `.context/system-prompt.txt`
|
|
||||||
|
|
||||||
Repos still carrying `.cursorrules`, `.aider.conventions.md` or
|
|
||||||
`.aider.conf.yml` are stale: nothing regenerates them. Delete them the next time
|
|
||||||
you touch the repo, along with their entries in that repo's `task check`
|
|
||||||
assertion.
|
|
||||||
|
|
||||||
Workflow:
|
|
||||||
1. Edit a canonical file. Run `task context:sync`. Commit canonical and
|
|
||||||
derived together. Push.
|
|
||||||
2. On any other host, `git pull` brings both. Which file a harness reads does
|
|
||||||
NOT split cleanly by lookup strategy, so do not reason from the strategy:
|
|
||||||
- `CLAUDE.md` — Claude Code (walks up the tree)
|
|
||||||
- `AGENTS.md` — Crush, Pi, Antigravity (cwd-only), OpenCode (project root,
|
|
||||||
global at `~/.config/opencode/AGENTS.md`), and Mistral Vibe, which **walks
|
|
||||||
up** from cwd and takes the first `AGENTS.md` it finds
|
|
||||||
(user-level `~/.vibe/AGENTS.md`)
|
|
||||||
- `.context/system-prompt.txt` — Open WebUI, Mods, raw API calls; not read by
|
|
||||||
any harness, it is for pasting
|
|
||||||
|
|
||||||
Vibe tree-walks for `AGENTS.md`, so "tree-walkers use CLAUDE.md" is false.
|
|
||||||
Each repo's `AGENTS.md` embeds the canonical and is the first one found from
|
|
||||||
inside that repo, so this works — but by construction, not by luck.
|
|
||||||
3. `task check` runs `context:sync` then asserts `git status --porcelain`
|
|
||||||
is empty over the derived files (catches both modified-tracked drift
|
|
||||||
and missing-untracked adapters). A drift fails the check with a
|
|
||||||
message telling you to stage the regenerated files.
|
|
||||||
|
|
||||||
Behavior rules in this file and per-project rules in `PROJECT.md` apply on every
|
|
||||||
host and every harness.
|
|
||||||
|
|
||||||
**Where a project rule contradicts a rule here, the project rule governs — and it
|
|
||||||
must state its reason.** Both halves matter: the project file wins because it knows
|
|
||||||
something this file cannot (infra's Flux constraint is real and not visible from
|
|
||||||
here), and it must say why because a bare override is indistinguishable from drift.
|
|
||||||
|
|
||||||
This replaces the previous wording, which said rules here and in `PROJECT.md` both
|
|
||||||
apply *"unconditionally"*. That was not a tiebreak but a contradiction, and it went
|
|
||||||
unnoticed until rule 5 and infra's "never create feature branches" had to be obeyed
|
|
||||||
at the same time (infra#470). A standard can hold two individually sensible clauses
|
|
||||||
that are jointly unsatisfiable, and survive for as long as everyone reads it
|
|
||||||
charitably — the same failure the Inception Sprint Oath's G6 had.
|
|
||||||
|
|
||||||
## Engineering Skills
|
|
||||||
|
|
||||||
Shared engineering skills live in the **`mathias/skills`** repo (`git.d-ma.be/mathias/skills`). Load at task start — not "on demand" but on schedule, before writing code. Browse `SKILLS_INDEX.md` for the full list.
|
|
||||||
|
|
||||||
**Claude Code** installs the repo as a plugin from the marketplace the same repo hosts. That path also carries the `secret-guard` and `pre-task-ritual` hooks and the brain/gitea/infra MCP servers, which symlinks cannot:
|
|
||||||
|
|
||||||
```
|
|
||||||
/plugin marketplace add https://git.d-ma.be/mathias/skills.git
|
|
||||||
/plugin install skills@mathias
|
|
||||||
```
|
|
||||||
|
|
||||||
Skills are **namespaced** under Claude Code: load `tdd` as `skills:tdd`. The trigger table below uses bare names — prefix them.
|
|
||||||
|
|
||||||
**Crush, Pi and OpenCode** have no plugin system and are wired by `install.sh` as one directory symlink per harness via `~/.agents/skills`. Do not clone the checkout into `~/dev`: that folder is iCloud-shared between the Macs and spans two path roots (`/Users/mathias` vs `/home/mathias` on koala), so a checkout there is a git working tree with two concurrent writers, and any absolute symlink stored in it is broken on one side by construction (skills#23). Leave `SKILLS_CHECKOUT_DIR` at its host-local default.
|
|
||||||
|
|
||||||
Check a host with `task doctor` in the skills repo; it also runs automatically at session start via the plugin.
|
|
||||||
|
|
||||||
**Skill trigger table — load before starting, not after getting stuck:**
|
|
||||||
|
|
||||||
| Task type | Load |
|
|
||||||
|-----------|------|
|
|
||||||
| Any feature or bug fix | `tdd` |
|
|
||||||
| Refactor or design | `clean-code` or `solid` |
|
|
||||||
| Debug | `problem-analysis` |
|
|
||||||
| Review code or PRs | `code-review` |
|
|
||||||
| Frame a problem before coding | `problem-analysis` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Project context
|
# Project context
|
||||||
|
|
||||||
<!-- Canonical project context. Edit this, run `task context:sync`.
|
<!-- Canonical project context. Edit this, run `task context:sync`.
|
||||||
@@ -466,9 +42,9 @@ Check a host with `task doctor` in the skills repo; it also runs automatically a
|
|||||||
|
|
||||||
### Git
|
### Git
|
||||||
- Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`
|
- Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`
|
||||||
- **Trunk-Based Development:** commit directly to main. One logical change per commit.
|
- **Trunk-Based Development:** one logical change per commit, merged to main by PR (`main` is protected, see below).
|
||||||
- Run `task check` locally before every push. CI is the quality gate, not branch protection.
|
- Run `task check` locally before every push. CI is the quality gate, not branch protection.
|
||||||
- No feature branches, no PRs for solo/agent work.
|
- Short-lived branches and PRs are how work reaches `main` here (`tbd_ship`/`pr_merge`); no long-lived feature branches.
|
||||||
- Exception: if a parallel agent session is active on this repo, use a short-lived
|
- Exception: if a parallel agent session is active on this repo, use a short-lived
|
||||||
`agent/<description>` branch and merge within the same session.
|
`agent/<description>` branch and merge within the same session.
|
||||||
|
|
||||||
@@ -508,8 +84,8 @@ When acting as a coding agent on this project:
|
|||||||
3. If unsure about a convention, check `DECISIONS.md` or ask
|
3. If unsure about a convention, check `DECISIONS.md` or ask
|
||||||
4. Never modify files outside the project root without explicit permission
|
4. Never modify files outside the project root without explicit permission
|
||||||
5. When adding a dependency, explain why in the commit message
|
5. When adding a dependency, explain why in the commit message
|
||||||
6. Commit directly to main. Run `task check` before every push. Never create
|
6. Ship by PR (`main` is protected, see below). Run `task check` before every push. Never create
|
||||||
feature branches unless a parallel agent is simultaneously active on this repo.
|
long-lived feature branches.
|
||||||
7. For client projects: never send code or context to cloud APIs — use local models via LiteLLM
|
7. For client projects: never send code or context to cloud APIs — use local models via LiteLLM
|
||||||
|
|
||||||
## Current state — v0.2.5 (2026-05-17)
|
## Current state — v0.2.5 (2026-05-17)
|
||||||
@@ -535,4 +111,16 @@ Current main: `e31fd3f`. CI green. Deployed via Flux.
|
|||||||
2. **Issue #19** — end-to-end mirror flow verification.
|
2. **Issue #19** — end-to-end mirror flow verification.
|
||||||
`repo_mirror_push` is implemented but the full flow (create repo → add push mirror → verify sync to GitHub) has not been tested manually. Do this before relying on it in production.
|
`repo_mirror_push` is implemented but the full flow (create repo → add push mirror → verify sync to GitHub) has not been tested manually. Do this before relying on it in production.
|
||||||
|
|
||||||
|
## `main` is protected: ship by PR (local-dev#42, decided 2026-10-08)
|
||||||
|
|
||||||
|
Gitea refuses direct pushes to `main` in this repo, and a PR merges only after its CI status (`CD / Lint / Test / Vet (pull_request)`) passes.
|
||||||
|
**Why:** local-dev rule 5's reviewed lane (risk/medium+ is reviewed by a human) was enforced on the merge tools
|
||||||
|
(`tbd_ship`, `pr_merge`: gitea-mcp v0.17.0/v0.18.0, merge only for an issue labelled exactly `risk/low` with green CI)
|
||||||
|
but not on a direct commit to `main`, which bypassed it. This overrides rule 5's "risk/low commits straight to main"
|
||||||
|
for this repo; control code is where the lane matters.
|
||||||
|
**How:** `tbd_ship` for a one-file change, or branch -> PR -> `pr_merge` (name the `risk/low` issue); above `risk/low`
|
||||||
|
open the PR and a human merges it in the Gitea UI after review. Tags are not protected. CD jobs push image tags to
|
||||||
|
the **infra** repo with deploy keys, never to this repo's `main`. Undo (Mathias only): Settings -> Branches, or
|
||||||
|
`PATCH /repos/mathias/gitea-mcp/branch_protections/main {"enable_push": true}`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,427 +1,3 @@
|
|||||||
# Agent context — Mathias workspace
|
|
||||||
|
|
||||||
<!-- Canonical root context for all AI coding agents.
|
|
||||||
Lives at: ~/dev/.context/AGENT.md
|
|
||||||
Applies to every project under ~/dev/ unless overridden.
|
|
||||||
|
|
||||||
Run `task context:sync` from ~/dev/ to regenerate harness-specific files.
|
|
||||||
Project-level context in .context/PROJECT.md layers on top of this. -->
|
|
||||||
|
|
||||||
## Who I am
|
|
||||||
|
|
||||||
I'm Mathias, a digital product manager and technology consultant based in Sweden.
|
|
||||||
I build software, research emerging tech, and deliver consulting engagements
|
|
||||||
for clients under NDA. I work across AI/ML, financial automation, web applications,
|
|
||||||
and climate/sustainability tech.
|
|
||||||
|
|
||||||
## How I work with agents
|
|
||||||
|
|
||||||
- I think like a product manager — I care about *why* before *how*
|
|
||||||
- I want agents to be opinionated and push back, not just execute blindly
|
|
||||||
- I prefer concise responses; skip ceremony and get to the point
|
|
||||||
- When I say "build this", I mean production-quality with tests, not a demo
|
|
||||||
- Ask me before making irreversible changes or adding heavy dependencies
|
|
||||||
- I work with confidential client data — never send it to cloud APIs unless I explicitly say it's OK
|
|
||||||
|
|
||||||
## Behavior rules
|
|
||||||
|
|
||||||
These rules apply to every task across every project, regardless of harness.
|
|
||||||
|
|
||||||
0. **Pre-task ritual — before ANY implementation (non-negotiable).** Run this before writing a single line:
|
|
||||||
- **Query the brain** (`brain_query`) for the domain + symptom. If the result changes your approach, surface it before acting. 5 seconds beats 5 hours.
|
|
||||||
- **Load the relevant skill** — see trigger table in *Engineering Skills* below.
|
|
||||||
- **Write the failing test first.** Name the test before the function. If the target is untestable (e.g. `main()` wiring), extract the logic into a testable function first. No implementation without a red test.
|
|
||||||
- **State the observable success criterion** — what specific behavior, output, or passing test proves this is done?
|
|
||||||
|
|
||||||
**TDD is non-negotiable.** "Tests pass" is not proof of correctness — only proof the tests ran. Write tests that would catch the bug before writing code that fixes it.
|
|
||||||
|
|
||||||
1. **No assumptions.** Don't hide confusion — surface it. Surface tradeoffs explicitly.
|
|
||||||
Think before coding; if the problem is unclear, ask or state assumptions before acting.
|
|
||||||
2. **Minimum viable code.** Solve with the smallest change that works. Nothing
|
|
||||||
speculative, no "while we're here" cleanups, no premature abstractions. Simplicity first.
|
|
||||||
3. **Surgical changes.** Touch only what the task requires. Leave unrelated code,
|
|
||||||
files, and formatting alone. Diffs should be small and reviewable.
|
|
||||||
4. **Goal-driven execution.** Define clear success criteria up front for every task.
|
|
||||||
Loop — implement, verify, refine — until those criteria are met. Don't claim
|
|
||||||
completion without evidence (tests pass, command output, observed behavior).
|
|
||||||
5. **Trunk-Based Development — commit directly to main.** Every commit is one
|
|
||||||
logical change (one tool, one fix, one test) with passing tests. Main is always
|
|
||||||
deployable. Never create long-lived feature branches.
|
|
||||||
|
|
||||||
**The PR trigger is RISK, not concurrency.** `risk/low` work commits straight to
|
|
||||||
main with CI as the gate — that is most work and is unchanged. `risk/medium` and
|
|
||||||
above opens a PR, even solo, even with nobody waiting to review it. This mirrors
|
|
||||||
the tiered merge model in `assessor-loop`
|
|
||||||
`method/transfer-to-a-regulated-organisation.md` §4.1, so the estate practises
|
|
||||||
what it sells instead of merging everything on green and claiming otherwise.
|
|
||||||
|
|
||||||
**Concurrency is NOT a reason to branch.** Measured 2026-08-29 across three real
|
|
||||||
collisions between parallel sessions: two were not branching problems at all — a
|
|
||||||
generated artifact written to the same path by both (branching turns that into a
|
|
||||||
merge conflict on a 33k-line JSON), and uncommitted work in a shared checkout
|
|
||||||
(a per-session worktree fixes it) — and the third was an ordinary non-fast-forward
|
|
||||||
push resolved by rebase in seconds. Use a worktree per session on shared repos and
|
|
||||||
timestamp generated artifacts. Do not reach for a branch.
|
|
||||||
|
|
||||||
Branch+PR flow carries documented costs on solo repos: merging auto-closes gated
|
|
||||||
issues, review comments live on a thread separate from the issue's, and a
|
|
||||||
merged-and-deleted branch cannot be amended (brain:
|
|
||||||
`gitea-pr-merge-autoclose-and-review-comment-footguns`). Those are the price of
|
|
||||||
the `risk/medium`+ lane — paid deliberately, not incurred by accident.
|
|
||||||
|
|
||||||
**Exception — external contributor or client four-eyes requirement:** Use PR flow
|
|
||||||
when a human reviewer outside the project is required. Document the reason in
|
|
||||||
PROJECT.md.
|
|
||||||
|
|
||||||
**Exception — a GitOps repo where a branch cannot be reconciled.** In a repo whose
|
|
||||||
deployment watches one branch (`mathias/infra`: Flux watches `main`), a branch is
|
|
||||||
invisible to the thing that applies it. A `risk/medium` change to `alerts.yaml` or
|
|
||||||
`litellm-config.yaml` can be linted on a branch but never *exercised* there, so a
|
|
||||||
PR would buy a review of a state that was never deployed.
|
|
||||||
|
|
||||||
There, `risk/medium`+ work stays on `main` and instead requires a **recorded
|
|
||||||
adversarial review pass before the push** — `skills:pr-gatekeeper` or equivalent,
|
|
||||||
reviewing the staged diff. **The reviewer's verdict goes in the issue, not a PR
|
|
||||||
thread.** No verdict recorded means the lane was not used, and saying "the gate was
|
|
||||||
green" is not the same claim.
|
|
||||||
|
|
||||||
Be honest about what this is: a recorded *independent* pass, not four-eyes in the
|
|
||||||
sense `assessor-loop` §4.1 means for a regulated organisation, because the reviewer
|
|
||||||
is an agent. It is more than the repo had and less than the model it mirrors; do not
|
|
||||||
describe it as the stronger property. (infra#470, decided 2026-09-29.)
|
|
||||||
|
|
||||||
This lane depends on the `risk/` label being right. A label that triggers a heavier
|
|
||||||
lane is load-bearing, not cosmetic — if work that belongs in it is unlabelled, the
|
|
||||||
lane silently does not apply.
|
|
||||||
|
|
||||||
6. **Close the loop — every substantive task ends with the same ritual.** Shipping
|
|
||||||
the code is not the end of the task; capturing it is. Run this unprompted:
|
|
||||||
- **Tag + bump SemVer** on the change (annotated tag; minor for a feature or
|
|
||||||
new/changed ADR, patch for a fix; docs in the same commit). Check the repo's
|
|
||||||
actual last tag — stated versions in docs drift stale.
|
|
||||||
- **Push** main and the tag (CI is the gate).
|
|
||||||
- **Persist generalizable learnings to the brain** (`brain_write`, wing/hall) —
|
|
||||||
the reusable patterns and the footguns that would bite anyone again, never
|
|
||||||
project status. See *Knowledge base — when to write* below.
|
|
||||||
- **File discovered-but-deferred work as tracker issues** on the project's own
|
|
||||||
repo — token-budget gaps, recorded ADR limitations, v2 follow-ups. Don't let
|
|
||||||
"out of scope, recorded" rot in a commit message; make it a ticket with a
|
|
||||||
source pointer.
|
|
||||||
- Surface the brain entries and issue numbers in the closing summary so the
|
|
||||||
trail is auditable.
|
|
||||||
|
|
||||||
**Chat is not a durable store. Never rely on Mathias remembering anything.**
|
|
||||||
Every gap, caveat, risk, open question and next step must land in one of four
|
|
||||||
places before the turn ends: **the brain**, **a repo** (code, docs, ADR),
|
|
||||||
**a tracker issue**, or **a plan file**. A finding stated only in conversation is
|
|
||||||
lost the moment the session closes — and this estate has repeatedly proved that a
|
|
||||||
remembered convention is an unreliable control.
|
|
||||||
|
|
||||||
This applies hardest to what is tempting to leave as a closing remark: "worth
|
|
||||||
knowing X isn't enforced yet", "this is a convention, not a gate", "we should
|
|
||||||
revisit Y". Those are exactly the items that need a locator. If it is worth
|
|
||||||
saying in a summary, it is worth writing where it can be found again without you.
|
|
||||||
|
|
||||||
7. **Finish what's closable — only involve me for a genuine call.** If a
|
|
||||||
tracker issue, PR, or checklist item is demonstrably resolved — the fix
|
|
||||||
landed, the state changed, you verified it live — close/merge/check it off
|
|
||||||
in the same turn. Don't leave it open "for the record" or park it waiting
|
|
||||||
on me to confirm something that already happened; that's bookkeeping, not
|
|
||||||
a decision. Reserve asking me (`AskUserQuestion`, a `DECISION NEEDED`
|
|
||||||
comment, or otherwise pausing) for when it's genuinely mine to call:
|
|
||||||
irreversible or prod/host-impacting actions, a real judgment call with no
|
|
||||||
clearly-better option, ambiguous intent, or anything secret-writes/
|
|
||||||
destructive-git already gate. Before claiming something is closable,
|
|
||||||
verify it live (re-check the issue, re-run the command) rather than
|
|
||||||
trusting your own earlier summary (brain: `filed-issue-not-same-as-fixed`).
|
|
||||||
|
|
||||||
8. **Control code needs an adversary, not a checklist.** Code whose job is to
|
|
||||||
refuse, deny, gate or contain is not adequately verified by a green gate.
|
|
||||||
Rule 5 makes CI the quality gate and that holds for ordinary work — but a
|
|
||||||
passing suite only asserts what its author already thought of, and for a
|
|
||||||
control the author is precisely the person whose imagination just failed.
|
|
||||||
Measured on `dispatch#75`: five review rounds, five real escapes verified by
|
|
||||||
execution, and **CI was green on every one of them** while `git rm
|
|
||||||
././.dispatch-allow` deleted the file the control existed to protect. So
|
|
||||||
route this work through a review with authority to block, instructed to
|
|
||||||
verify by execution rather than by reading the implementation. Expect to be
|
|
||||||
blocked more than once; that is the control working, not the reviewer being
|
|
||||||
difficult. (brain: `a-green-gate-is-not-evidence-for-control-code`; the
|
|
||||||
practices live in the `pr-gatekeeper` agent.)
|
|
||||||
|
|
||||||
9. **A test's name must not claim more than its assertions prove.** Name a test
|
|
||||||
after the mechanism it observes, not the goal it serves.
|
|
||||||
`DoesNotInheritEnv` is honest; `IsContained` is a claim the test cannot
|
|
||||||
support. This is not pedantry about naming — when name and assertion
|
|
||||||
diverge, the name wins in the reader's head and the gap becomes invisible,
|
|
||||||
which is what lets the defect survive review. Found three times in one PR,
|
|
||||||
twice in tests written to fix the previous instance of it; each passed while
|
|
||||||
the thing it was named for was exploitable. If you cannot name the mechanism,
|
|
||||||
you do not yet know what the test observes.
|
|
||||||
|
|
||||||
## CAD sprint patterns
|
|
||||||
|
|
||||||
Two CAD sprint archetypes (defs: brain `wiki/homelab/decisions/continuous-agentic-development-cad-concept-2026-06-16`):
|
|
||||||
- **Inception Sprint** — birth a new repo. Oath-gated close; tag only once every clause holds. Brain: `.../inception-sprint-and-oath`.
|
|
||||||
- **Sweep Sprint** — clear an existing backlog. Parallel read-only research per issue → sequential one-issue-per-commit execution. Brain: `.../sweep-sprint-and-oath`.
|
|
||||||
|
|
||||||
Shared: decisions that are yours, not the agent's, surface as `DECISION NEEDED` (question/options/recommendation/blocks) — ask live if you're present, else comment and wait, never act past it. Live-breaking regressions get `git revert`, never forward-fixed; host-network work skips even read-only research (solo, synchronous, you present). Rule 6's closing ritual only fires once every Oath clause genuinely holds — blocked = descoped-and-tracked, never marked done.
|
|
||||||
|
|
||||||
## Default stack
|
|
||||||
|
|
||||||
| Layer | Default | Fallback | Last resort |
|
|
||||||
|-------|---------|----------|-------------|
|
|
||||||
| Language | Go | Python | TypeScript, Java, C |
|
|
||||||
| UI | HTMX + Templ | Server-rendered HTML | React (only if SPA is justified) |
|
|
||||||
| Build | Task (taskfile.dev) | Make | — |
|
|
||||||
| Containers | Docker Compose (dev), k3s (prod) | — | — |
|
|
||||||
| DB | PostgreSQL + sqlc | SQLite | — |
|
|
||||||
| Search | pgvector (vector), BM25 | Qdrant (when >1M vectors or hybrid retrieval) | — |
|
|
||||||
| Logging | slog (structured) | stdlib `logging` w/ structured `extra=` (or structlog) | — |
|
|
||||||
| Testing | Table-driven, testify | — | — |
|
|
||||||
| Agents (Go) | google.golang.org/adk + pkg/litellm adapter | — | — |
|
|
||||||
|
|
||||||
Exploratory: Rust, Zig — I'll tell you when I want these.
|
|
||||||
|
|
||||||
## Code conventions
|
|
||||||
|
|
||||||
- **Go style**: golines, gofumpt, golangci-lint
|
|
||||||
- **Python style** (fallback language): ruff (format+lint, one tool), mypy --strict (non-negotiable,
|
|
||||||
matches Go's static typing discipline), pytest + pytest-cov (table-driven via
|
|
||||||
`@pytest.mark.parametrize`), uv (venv+deps+lock, one tool), pydantic-settings (typed env-var config
|
|
||||||
— same principle as Go's typed structs), src-layout + `pyproject.toml` only (no `setup.py`)
|
|
||||||
- **Errors**: `fmt.Errorf("operation: %w", err)` — never naked, never log-and-return.
|
|
||||||
Python: `raise X from e` (exception chaining, same principle) — never bare `except`, never silent `pass`
|
|
||||||
- **Naming**: stdlib conventions, no stuttering
|
|
||||||
- **Architecture**: prefer stdlib over frameworks, constructor injection, env-var config parsed into typed structs
|
|
||||||
- **Git**: conventional commits (`feat:`, `fix:`, `chore:`), commit directly to main,
|
|
||||||
one logical change per commit, CI is the quality gate
|
|
||||||
- **Never**: long-lived feature branches, PRs for solo work, direct push without
|
|
||||||
passing `task check` locally first
|
|
||||||
- **Security**: no secrets in code, govulncheck before adding deps, SOPS for encrypted config
|
|
||||||
- **Dependencies**: prefer stdlib. testify, slog, templ, sqlc, google.golang.org/adk (agent projects only) are pre-approved; anything else needs justification in the commit message
|
|
||||||
|
|
||||||
## Secret handling (every harness, every command)
|
|
||||||
|
|
||||||
Tool output is persisted: terminal → `~/.claude/projects` transcripts →
|
|
||||||
claudewatcher → brain/wiki → gitea history. A secret printed once is
|
|
||||||
searchable forever, and clearing it means rotating the key. So:
|
|
||||||
|
|
||||||
1. **Never print, echo, log, or transform a secret to inspect it.** No
|
|
||||||
`base64`/`xxd`/`cat` of a key, and never pipe a secret through a transform
|
|
||||||
to defeat `op run`'s output masking (it masks raw values; base64 hides them
|
|
||||||
from the mask — that exact trick leaked a key on 2026-06-11).
|
|
||||||
2. **Secrets stay in the subprocess.** Reference them only as env vars consumed
|
|
||||||
*inside* `op run --env-file ~/.op-env -- <cmd>`. Never place a literal secret
|
|
||||||
in a command's argv (it lands in the tool call and the transcript).
|
|
||||||
3. **Existence check without revealing the value:** `[ -n "$X" ] && echo set` —
|
|
||||||
never `${X:-...}` (returns the value when set) and never echo a substring of it.
|
|
||||||
4. **Cross-host secrets:** run the secret-consuming command on the host that has
|
|
||||||
the secret; do not forward a raw key over ssh argv/stdout.
|
|
||||||
5. If a secret does leak into output, say so immediately and flag it for rotation —
|
|
||||||
don't bury it.
|
|
||||||
|
|
||||||
## Infrastructure
|
|
||||||
|
|
||||||
Three machines on Tailscale:
|
|
||||||
|
|
||||||
| Machine | Role | Key specs |
|
|
||||||
|---------|------|-----------|
|
|
||||||
| koala | GPU inference, heavy compute | RTX 5070, runs k3s + llama-swap + shared postgres18/pgvector |
|
|
||||||
| iguana | Services, builds | M2 Ultra Mac |
|
|
||||||
| flamingo | Daily driver, edge | Apple Silicon Mac, ~/dev is here |
|
|
||||||
|
|
||||||
- **Model routing**: LiteLLM in front of llama-swap (local) + cloud APIs (when permitted)
|
|
||||||
- **Orchestration**: k3s on koala only — single control-plane node, no workers (verified 2026-08-30). iguana and flamingo are on the tailnet but are not cluster members
|
|
||||||
- **Networking**: Tailscale mesh
|
|
||||||
|
|
||||||
## Project landscape
|
|
||||||
|
|
||||||
All development repos live at `~/dev/` (softlink from `~/Documents/local-dev/`).
|
|
||||||
|
|
||||||
Organized in thematic folders:
|
|
||||||
|
|
||||||
| Folder | Focus | Count |
|
|
||||||
|--------|-------|-------|
|
|
||||||
| `GO/` | Go web frameworks, API integrations, learning projects | ~10 |
|
|
||||||
| `AI/` | ML research, AI frameworks (FinRL, DSPy, crawl4ai) | ~6 |
|
|
||||||
| `AGENTS/` | Autonomous agents, coding agents, MCP servers, infra | ~15 |
|
|
||||||
| `QKX/` | Invoice processing, financial automation, payment systems | ~13 |
|
|
||||||
| `XT/` | Climate data, sustainability (Klimatkollen, Garbo) | ~2 |
|
|
||||||
|
|
||||||
See `~/dev/PROJECT_SUMMARY.md` for detailed descriptions of each project.
|
|
||||||
|
|
||||||
### Key active projects
|
|
||||||
|
|
||||||
- **super-koala** (`AGENTS/`) — multi-component agent stack with LangGraph, DSPy, MCP
|
|
||||||
- **azure-tiger** (`QKX/`) — invoice extraction → ISO 20022 payment instructions
|
|
||||||
- **gocrwl** (`AGENTS/`) — Go web crawler with containerized deployment
|
|
||||||
- **koala-ai-stack** (`AGENTS/`) — local AI server infrastructure management
|
|
||||||
- **klimatkollen** (`XT/`) — Swedish municipal climate data platform
|
|
||||||
|
|
||||||
## Knowledge base — actively use it
|
|
||||||
|
|
||||||
A persistent brain (BM25 search + LLM-synthesised Q&A) survives across sessions,
|
|
||||||
hosts, and harnesses. It holds 100+ hard-won entries: infra incident postmortems,
|
|
||||||
Go pitfalls, framework gotchas, design principles, ADRs. **It is not optional
|
|
||||||
reference material — query it actively, not just when explicitly told.**
|
|
||||||
|
|
||||||
### When to query (treat as a reflex)
|
|
||||||
|
|
||||||
- **Before** starting a non-trivial task — search for prior art with the symptom
|
|
||||||
AND the system component ("how did we solve X in Y?"). 5 seconds beats 5 hours.
|
|
||||||
- **When debugging** — search for the error string, the stack frame, the affected
|
|
||||||
service. Past you may have already paid this tax.
|
|
||||||
- **Before adopting** a pattern, library, framework, or model name — check if it
|
|
||||||
was tried and rejected, or what the integration footguns are.
|
|
||||||
- **When making architectural decisions** — search for the domain + "ADR" or
|
|
||||||
"decision" to find prior reasoning before re-deriving it.
|
|
||||||
- **When a recommendation feels novel** — challenge yourself: "has this been
|
|
||||||
documented?" The brain often has it.
|
|
||||||
|
|
||||||
### When to write
|
|
||||||
|
|
||||||
After you discover something that **future-you would forget** and that **isn't
|
|
||||||
recoverable from the code, git log, or PR description alone**:
|
|
||||||
|
|
||||||
- Bugs whose root cause is non-obvious and generalisable beyond this project.
|
|
||||||
- Framework / library / model-name quirks that bit you and would bite anyone.
|
|
||||||
- Design principles validated under fire (e.g. "every `_get` needs a `_list`").
|
|
||||||
- Postmortems for incidents: what broke, why, how diagnosed, what to do next time.
|
|
||||||
|
|
||||||
DON'T write project status, sprint progress, PR summaries, or "what I did this
|
|
||||||
session" — those rot fast and the originals are in git/gitea anyway. Brain
|
|
||||||
entries that age well are about *why*, *how to avoid*, and *what to do when*.
|
|
||||||
|
|
||||||
### How to access (per harness)
|
|
||||||
|
|
||||||
| Harness | Query | Write |
|
|
||||||
|---------|-------|-------|
|
|
||||||
| **Claude Code, Claude Desktop** | `brain_query` (BM25), `brain_answer` (LLM-synth + sources) MCP tools | `brain_write` MCP tool |
|
|
||||||
| **Crush, Pi, Antigravity, other MCP-capable** | same MCP server: `ingestion-brain` (via the `mcp__*_brain__*` namespace once authenticated) | same |
|
|
||||||
| **Anything HTTP-only (curl, scripts)** | Tailnet only: `POST https://koala.tail30472e.ts.net:8443/query` with `{"query":"..."}`, sending `BRAIN_MCP_TOKEN` (not yet enforced on REST, hyperguild#149). The public `brain-mcp.d-ma.be` serves `/mcp` only (infra#517). | `POST .../write` with `{"content":"...","wing":"...","hall":"..."}` |
|
|
||||||
| **Browser / human inspection** | `https://git.d-ma.be/mathias/hyperguild` → `knowledge/` and `wiki/` markdown files |
|
|
||||||
|
|
||||||
- **Scoping — the brain holds no confidential client content.** Nothing on the read path
|
|
||||||
filters by client: every token holder can read every wing, and `wing` on `brain_query`
|
|
||||||
is an optional argument, not an access control. Client confidentiality is enforced only
|
|
||||||
at write time (`capture`'s I1 gate refuses internal/confidential). So never `brain_write`
|
|
||||||
client material; generalise it first or keep it in the client's own repo. (hyperguild#144,
|
|
||||||
decided 2026-10-01. This line previously claimed client projects were filtered; they
|
|
||||||
were not.)
|
|
||||||
- **Routing**: brain_answer's LLM uses berget.ai as primary, iguana ollama as
|
|
||||||
fallback. Both are configurable in the `supervisor/ingestion-deployment.yaml`
|
|
||||||
on the koala k3s cluster; don't hardcode local-only model names into the
|
|
||||||
berget URL (see knowledge entry on namespace mismatches).
|
|
||||||
|
|
||||||
### Quick reflex checks
|
|
||||||
|
|
||||||
If you find yourself about to say any of these out loud, you owe yourself a brain query first:
|
|
||||||
|
|
||||||
- "I think the issue might be..."
|
|
||||||
- "Let me try X and see..."
|
|
||||||
- "I'll just write a script to..."
|
|
||||||
- "This is probably a new bug..."
|
|
||||||
- "Has anyone done this before?" — *yes, probably, go check.*
|
|
||||||
|
|
||||||
## Client work rules
|
|
||||||
|
|
||||||
When working on a project tagged with a client name:
|
|
||||||
1. Never send code, data, or context to cloud APIs — use local models only
|
|
||||||
2. Never reference other client projects or their data
|
|
||||||
3. Keep all artifacts within the client's git org / directory
|
|
||||||
4. Treat everything as confidential unless told otherwise
|
|
||||||
|
|
||||||
## Harness-agnostic principles
|
|
||||||
|
|
||||||
Supported harnesses (2026-09-01) — Cursor and Aider were dropped; do not
|
|
||||||
re-add adapters for them:
|
|
||||||
- Claude Code, Antigravity, OpenCode, Mistral Vibe, Crush, Pi Coding Agent
|
|
||||||
- Open WebUI, Charmbracelet Mods, or any tool that takes a system prompt string
|
|
||||||
- Any other tool that reads `AGENTS.md`, which is the de facto convention
|
|
||||||
|
|
||||||
The canonical source is always `.context/AGENT.md` (root) and `.context/PROJECT.md` (per-project).
|
|
||||||
Derived files are committed (see *How context propagates* below) so a `git pull` on any host yields full agent context with no setup.
|
|
||||||
|
|
||||||
## How context propagates
|
|
||||||
|
|
||||||
Canonical sources of truth:
|
|
||||||
- Universal: `~/dev/.context/AGENT.md` (this file)
|
|
||||||
- Project: `<repo>/.context/PROJECT.md` (per-repo)
|
|
||||||
|
|
||||||
Derived files (committed, regenerated by `task context:sync`):
|
|
||||||
- `CLAUDE.md`, `AGENTS.md`, `.context/system-prompt.txt`
|
|
||||||
|
|
||||||
Repos still carrying `.cursorrules`, `.aider.conventions.md` or
|
|
||||||
`.aider.conf.yml` are stale: nothing regenerates them. Delete them the next time
|
|
||||||
you touch the repo, along with their entries in that repo's `task check`
|
|
||||||
assertion.
|
|
||||||
|
|
||||||
Workflow:
|
|
||||||
1. Edit a canonical file. Run `task context:sync`. Commit canonical and
|
|
||||||
derived together. Push.
|
|
||||||
2. On any other host, `git pull` brings both. Which file a harness reads does
|
|
||||||
NOT split cleanly by lookup strategy, so do not reason from the strategy:
|
|
||||||
- `CLAUDE.md` — Claude Code (walks up the tree)
|
|
||||||
- `AGENTS.md` — Crush, Pi, Antigravity (cwd-only), OpenCode (project root,
|
|
||||||
global at `~/.config/opencode/AGENTS.md`), and Mistral Vibe, which **walks
|
|
||||||
up** from cwd and takes the first `AGENTS.md` it finds
|
|
||||||
(user-level `~/.vibe/AGENTS.md`)
|
|
||||||
- `.context/system-prompt.txt` — Open WebUI, Mods, raw API calls; not read by
|
|
||||||
any harness, it is for pasting
|
|
||||||
|
|
||||||
Vibe tree-walks for `AGENTS.md`, so "tree-walkers use CLAUDE.md" is false.
|
|
||||||
Each repo's `AGENTS.md` embeds the canonical and is the first one found from
|
|
||||||
inside that repo, so this works — but by construction, not by luck.
|
|
||||||
3. `task check` runs `context:sync` then asserts `git status --porcelain`
|
|
||||||
is empty over the derived files (catches both modified-tracked drift
|
|
||||||
and missing-untracked adapters). A drift fails the check with a
|
|
||||||
message telling you to stage the regenerated files.
|
|
||||||
|
|
||||||
Behavior rules in this file and per-project rules in `PROJECT.md` apply on every
|
|
||||||
host and every harness.
|
|
||||||
|
|
||||||
**Where a project rule contradicts a rule here, the project rule governs — and it
|
|
||||||
must state its reason.** Both halves matter: the project file wins because it knows
|
|
||||||
something this file cannot (infra's Flux constraint is real and not visible from
|
|
||||||
here), and it must say why because a bare override is indistinguishable from drift.
|
|
||||||
|
|
||||||
This replaces the previous wording, which said rules here and in `PROJECT.md` both
|
|
||||||
apply *"unconditionally"*. That was not a tiebreak but a contradiction, and it went
|
|
||||||
unnoticed until rule 5 and infra's "never create feature branches" had to be obeyed
|
|
||||||
at the same time (infra#470). A standard can hold two individually sensible clauses
|
|
||||||
that are jointly unsatisfiable, and survive for as long as everyone reads it
|
|
||||||
charitably — the same failure the Inception Sprint Oath's G6 had.
|
|
||||||
|
|
||||||
## Engineering Skills
|
|
||||||
|
|
||||||
Shared engineering skills live in the **`mathias/skills`** repo (`git.d-ma.be/mathias/skills`). Load at task start — not "on demand" but on schedule, before writing code. Browse `SKILLS_INDEX.md` for the full list.
|
|
||||||
|
|
||||||
**Claude Code** installs the repo as a plugin from the marketplace the same repo hosts. That path also carries the `secret-guard` and `pre-task-ritual` hooks and the brain/gitea/infra MCP servers, which symlinks cannot:
|
|
||||||
|
|
||||||
```
|
|
||||||
/plugin marketplace add https://git.d-ma.be/mathias/skills.git
|
|
||||||
/plugin install skills@mathias
|
|
||||||
```
|
|
||||||
|
|
||||||
Skills are **namespaced** under Claude Code: load `tdd` as `skills:tdd`. The trigger table below uses bare names — prefix them.
|
|
||||||
|
|
||||||
**Crush, Pi and OpenCode** have no plugin system and are wired by `install.sh` as one directory symlink per harness via `~/.agents/skills`. Do not clone the checkout into `~/dev`: that folder is iCloud-shared between the Macs and spans two path roots (`/Users/mathias` vs `/home/mathias` on koala), so a checkout there is a git working tree with two concurrent writers, and any absolute symlink stored in it is broken on one side by construction (skills#23). Leave `SKILLS_CHECKOUT_DIR` at its host-local default.
|
|
||||||
|
|
||||||
Check a host with `task doctor` in the skills repo; it also runs automatically at session start via the plugin.
|
|
||||||
|
|
||||||
**Skill trigger table — load before starting, not after getting stuck:**
|
|
||||||
|
|
||||||
| Task type | Load |
|
|
||||||
|-----------|------|
|
|
||||||
| Any feature or bug fix | `tdd` |
|
|
||||||
| Refactor or design | `clean-code` or `solid` |
|
|
||||||
| Debug | `problem-analysis` |
|
|
||||||
| Review code or PRs | `code-review` |
|
|
||||||
| Frame a problem before coding | `problem-analysis` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Project context
|
# Project context
|
||||||
|
|
||||||
<!-- Canonical project context. Edit this, run `task context:sync`.
|
<!-- Canonical project context. Edit this, run `task context:sync`.
|
||||||
@@ -461,9 +37,9 @@ Check a host with `task doctor` in the skills repo; it also runs automatically a
|
|||||||
|
|
||||||
### Git
|
### Git
|
||||||
- Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`
|
- Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`
|
||||||
- **Trunk-Based Development:** commit directly to main. One logical change per commit.
|
- **Trunk-Based Development:** one logical change per commit, merged to main by PR (`main` is protected, see below).
|
||||||
- Run `task check` locally before every push. CI is the quality gate, not branch protection.
|
- Run `task check` locally before every push. CI is the quality gate, not branch protection.
|
||||||
- No feature branches, no PRs for solo/agent work.
|
- Short-lived branches and PRs are how work reaches `main` here (`tbd_ship`/`pr_merge`); no long-lived feature branches.
|
||||||
- Exception: if a parallel agent session is active on this repo, use a short-lived
|
- Exception: if a parallel agent session is active on this repo, use a short-lived
|
||||||
`agent/<description>` branch and merge within the same session.
|
`agent/<description>` branch and merge within the same session.
|
||||||
|
|
||||||
@@ -503,8 +79,8 @@ When acting as a coding agent on this project:
|
|||||||
3. If unsure about a convention, check `DECISIONS.md` or ask
|
3. If unsure about a convention, check `DECISIONS.md` or ask
|
||||||
4. Never modify files outside the project root without explicit permission
|
4. Never modify files outside the project root without explicit permission
|
||||||
5. When adding a dependency, explain why in the commit message
|
5. When adding a dependency, explain why in the commit message
|
||||||
6. Commit directly to main. Run `task check` before every push. Never create
|
6. Ship by PR (`main` is protected, see below). Run `task check` before every push. Never create
|
||||||
feature branches unless a parallel agent is simultaneously active on this repo.
|
long-lived feature branches.
|
||||||
7. For client projects: never send code or context to cloud APIs — use local models via LiteLLM
|
7. For client projects: never send code or context to cloud APIs — use local models via LiteLLM
|
||||||
|
|
||||||
## Current state — v0.2.5 (2026-05-17)
|
## Current state — v0.2.5 (2026-05-17)
|
||||||
@@ -529,3 +105,15 @@ Current main: `e31fd3f`. CI green. Deployed via Flux.
|
|||||||
|
|
||||||
2. **Issue #19** — end-to-end mirror flow verification.
|
2. **Issue #19** — end-to-end mirror flow verification.
|
||||||
`repo_mirror_push` is implemented but the full flow (create repo → add push mirror → verify sync to GitHub) has not been tested manually. Do this before relying on it in production.
|
`repo_mirror_push` is implemented but the full flow (create repo → add push mirror → verify sync to GitHub) has not been tested manually. Do this before relying on it in production.
|
||||||
|
|
||||||
|
## `main` is protected: ship by PR (local-dev#42, decided 2026-10-08)
|
||||||
|
|
||||||
|
Gitea refuses direct pushes to `main` in this repo, and a PR merges only after its CI status (`CD / Lint / Test / Vet (pull_request)`) passes.
|
||||||
|
**Why:** local-dev rule 5's reviewed lane (risk/medium+ is reviewed by a human) was enforced on the merge tools
|
||||||
|
(`tbd_ship`, `pr_merge`: gitea-mcp v0.17.0/v0.18.0, merge only for an issue labelled exactly `risk/low` with green CI)
|
||||||
|
but not on a direct commit to `main`, which bypassed it. This overrides rule 5's "risk/low commits straight to main"
|
||||||
|
for this repo; control code is where the lane matters.
|
||||||
|
**How:** `tbd_ship` for a one-file change, or branch -> PR -> `pr_merge` (name the `risk/low` issue); above `risk/low`
|
||||||
|
open the PR and a human merges it in the Gitea UI after review. Tags are not protected. CD jobs push image tags to
|
||||||
|
the **infra** repo with deploy keys, never to this repo's `main`. Undo (Mathias only): Settings -> Branches, or
|
||||||
|
`PATCH /repos/mathias/gitea-mcp/branch_protections/main {"enable_push": true}`.
|
||||||
|
|||||||
@@ -37,9 +37,9 @@
|
|||||||
|
|
||||||
### Git
|
### Git
|
||||||
- Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`
|
- Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`
|
||||||
- **Trunk-Based Development:** commit directly to main. One logical change per commit.
|
- **Trunk-Based Development:** one logical change per commit, merged to main by PR (`main` is protected, see below).
|
||||||
- Run `task check` locally before every push. CI is the quality gate, not branch protection.
|
- Run `task check` locally before every push. CI is the quality gate, not branch protection.
|
||||||
- No feature branches, no PRs for solo/agent work.
|
- Short-lived branches and PRs are how work reaches `main` here (`tbd_ship`/`pr_merge`); no long-lived feature branches.
|
||||||
- Exception: if a parallel agent session is active on this repo, use a short-lived
|
- Exception: if a parallel agent session is active on this repo, use a short-lived
|
||||||
`agent/<description>` branch and merge within the same session.
|
`agent/<description>` branch and merge within the same session.
|
||||||
|
|
||||||
@@ -79,8 +79,8 @@ When acting as a coding agent on this project:
|
|||||||
3. If unsure about a convention, check `DECISIONS.md` or ask
|
3. If unsure about a convention, check `DECISIONS.md` or ask
|
||||||
4. Never modify files outside the project root without explicit permission
|
4. Never modify files outside the project root without explicit permission
|
||||||
5. When adding a dependency, explain why in the commit message
|
5. When adding a dependency, explain why in the commit message
|
||||||
6. Commit directly to main. Run `task check` before every push. Never create
|
6. Ship by PR (`main` is protected, see below). Run `task check` before every push. Never create
|
||||||
feature branches unless a parallel agent is simultaneously active on this repo.
|
long-lived feature branches.
|
||||||
7. For client projects: never send code or context to cloud APIs — use local models via LiteLLM
|
7. For client projects: never send code or context to cloud APIs — use local models via LiteLLM
|
||||||
|
|
||||||
## Current state — v0.2.5 (2026-05-17)
|
## Current state — v0.2.5 (2026-05-17)
|
||||||
@@ -105,3 +105,15 @@ Current main: `e31fd3f`. CI green. Deployed via Flux.
|
|||||||
|
|
||||||
2. **Issue #19** — end-to-end mirror flow verification.
|
2. **Issue #19** — end-to-end mirror flow verification.
|
||||||
`repo_mirror_push` is implemented but the full flow (create repo → add push mirror → verify sync to GitHub) has not been tested manually. Do this before relying on it in production.
|
`repo_mirror_push` is implemented but the full flow (create repo → add push mirror → verify sync to GitHub) has not been tested manually. Do this before relying on it in production.
|
||||||
|
|
||||||
|
## `main` is protected: ship by PR (local-dev#42, decided 2026-10-08)
|
||||||
|
|
||||||
|
Gitea refuses direct pushes to `main` in this repo, and a PR merges only after its CI status (`CD / Lint / Test / Vet (pull_request)`) passes.
|
||||||
|
**Why:** local-dev rule 5's reviewed lane (risk/medium+ is reviewed by a human) was enforced on the merge tools
|
||||||
|
(`tbd_ship`, `pr_merge`: gitea-mcp v0.17.0/v0.18.0, merge only for an issue labelled exactly `risk/low` with green CI)
|
||||||
|
but not on a direct commit to `main`, which bypassed it. This overrides rule 5's "risk/low commits straight to main"
|
||||||
|
for this repo; control code is where the lane matters.
|
||||||
|
**How:** `tbd_ship` for a one-file change, or branch -> PR -> `pr_merge` (name the `risk/low` issue); above `risk/low`
|
||||||
|
open the PR and a human merges it in the Gitea UI after review. Tags are not protected. CD jobs push image tags to
|
||||||
|
the **infra** repo with deploy keys, never to this repo's `main`. Undo (Mathias only): Settings -> Branches, or
|
||||||
|
`PATCH /repos/mathias/gitea-mcp/branch_protections/main {"enable_push": true}`.
|
||||||
|
|||||||
Reference in New Issue
Block a user