Template
Compare commits
14
Commits
eval/t1/c1/r0
...
v0.1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c2bfb05aa | ||
|
|
4038339727 | ||
|
|
2b3851a09e | ||
|
|
607c5fec3a | ||
|
|
d8d29664a3 | ||
|
|
020edc5b3a | ||
|
|
add5428e2f | ||
|
|
cfd55aeedb | ||
|
|
c704fed563 | ||
|
|
2ba2698ea6 | ||
|
|
06d4ac6076 | ||
|
|
c6f8ea477b | ||
|
|
2c6d1e1149 | ||
|
|
b9c64c2694 |
@@ -1,2 +0,0 @@
|
|||||||
read: .aider.conventions.md
|
|
||||||
auto-commits: false
|
|
||||||
@@ -1,255 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
**Exception — parallel agents on same repo:** If another agent is known to be
|
|
||||||
actively working on the same repo simultaneously, create a short-lived branch
|
|
||||||
(`agent/<description>`), finish the task, and merge to main within the same
|
|
||||||
session. Do not leave agent branches open between sessions.
|
|
||||||
|
|
||||||
**Exception — external contributor or client four-eyes requirement:** Use
|
|
||||||
PR flow only when a human reviewer outside the project is required. Document
|
|
||||||
the reason in PROJECT.md.
|
|
||||||
|
|
||||||
## 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) | — | — |
|
|
||||||
| 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
|
|
||||||
- **Errors**: `fmt.Errorf("operation: %w", err)` — never naked, never log-and-return
|
|
||||||
- **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
|
|
||||||
|
|
||||||
## 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 | Mac mini, ~/dev is here |
|
|
||||||
|
|
||||||
- **Model routing**: LiteLLM in front of llama-swap (local) + cloud APIs (when permitted)
|
|
||||||
- **Orchestration**: k3s cluster across all three machines
|
|
||||||
- **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)** | `POST https://brain-mcp.d-ma.be/query` with `{"query":"..."}` (auth via `BRAIN_MCP_TOKEN`) | `POST .../write` with `{"content":"...","filename":"..."}` |
|
|
||||||
| **Browser / human inspection** | `https://gitea.d-ma.be/mathias/hyperguild` → `knowledge/` and `wiki/` markdown files |
|
|
||||||
|
|
||||||
- **Scoping**: defaults to `public` collection; client projects filter to `{client}` + `public`.
|
|
||||||
- **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
|
|
||||||
|
|
||||||
This context is designed to work with any AI coding tool:
|
|
||||||
- Claude Code, Cursor, Aider, Open WebUI, Charmbracelet Mods/Crush
|
|
||||||
- Pi Coding Agent, Mistral Vibe, Antigravity
|
|
||||||
- Any tool that accepts a system prompt or reads a markdown context file
|
|
||||||
|
|
||||||
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`, `.cursorrules`, `.aider.conventions.md`,
|
|
||||||
`.context/system-prompt.txt`
|
|
||||||
|
|
||||||
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. Claude Code (tree-walking)
|
|
||||||
uses `CLAUDE.md`; Crush / Pi / Antigravity (cwd-only) use `AGENTS.md`;
|
|
||||||
Cursor uses `.cursorrules`; Aider uses `.aider.conventions.md`.
|
|
||||||
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
|
|
||||||
unconditionally on every host, every harness.
|
|
||||||
|
|
||||||
## Engineering Skills
|
|
||||||
|
|
||||||
Shared engineering skills are available in `~/dev/.skills/`. Load on demand via the index.
|
|
||||||
|
|
||||||
See `~/dev/.skills/SKILLS_INDEX.md` for the full list with descriptions and "use when" triggers.
|
|
||||||
|
|
||||||
Key skills:
|
|
||||||
- **TDD**: always write tests first — load `tdd` skill
|
|
||||||
- **Code Review**: load `code-review` skill before any review
|
|
||||||
- **SOLID/Clean Code**: load `solid` or `clean-code` skill for design work
|
|
||||||
- **Problem first**: load `problem-analysis` skill before coding non-trivial features
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# __PROJECT_NAME__
|
|
||||||
|
|
||||||
## Identity
|
|
||||||
|
|
||||||
- **Name**: __PROJECT_NAME__
|
|
||||||
- **Owner**: Mathias
|
|
||||||
- **Client**: personal
|
|
||||||
- **Repo**: gitea.d-ma.be/mathias/__PROJECT_NAME__
|
|
||||||
- **Status**: active
|
|
||||||
|
|
||||||
## Stack
|
|
||||||
|
|
||||||
Go + ADK + LiteLLM. See `~/dev/.context/AGENT.md` for cross-project conventions.
|
|
||||||
|
|
||||||
## Agent
|
|
||||||
|
|
||||||
TODO: describe what this agent does, what tools it has, and what it's responsible for.
|
|
||||||
|
|
||||||
## Observability
|
|
||||||
|
|
||||||
Traces → Jaeger via `OTLP_ENDPOINT`. Set `ADK_SERVICE_NAME=__PROJECT_NAME__` per deployment.
|
|
||||||
Spans emitted: `invoke_agent`, `generate_content`. Tool spans require custom callbacks.
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# __PROJECT_NAME__
|
|
||||||
|
|
||||||
## Identity
|
|
||||||
|
|
||||||
- **Name**: __PROJECT_NAME__
|
|
||||||
- **Owner**: Mathias
|
|
||||||
- **Client**: personal
|
|
||||||
- **Repo**: gitea.d-ma.be/mathias/__PROJECT_NAME__
|
|
||||||
- **Status**: active
|
|
||||||
|
|
||||||
## Stack
|
|
||||||
|
|
||||||
Go + ADK + LiteLLM. See `~/dev/.context/AGENT.md` for cross-project conventions.
|
|
||||||
|
|
||||||
## Agent
|
|
||||||
|
|
||||||
TODO: describe what this agent does, what tools it has, and what it's responsible for.
|
|
||||||
|
|
||||||
## Observability
|
|
||||||
|
|
||||||
Traces → Jaeger via `OTLP_ENDPOINT`. Set `ADK_SERVICE_NAME=__PROJECT_NAME__` per deployment.
|
|
||||||
Spans emitted: `invoke_agent`, `generate_content`. Tool spans require custom callbacks.
|
|
||||||
@@ -1,262 +0,0 @@
|
|||||||
You are a coding assistant working on a specific project.
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
**Exception — parallel agents on same repo:** If another agent is known to be
|
|
||||||
actively working on the same repo simultaneously, create a short-lived branch
|
|
||||||
(`agent/<description>`), finish the task, and merge to main within the same
|
|
||||||
session. Do not leave agent branches open between sessions.
|
|
||||||
|
|
||||||
**Exception — external contributor or client four-eyes requirement:** Use
|
|
||||||
PR flow only when a human reviewer outside the project is required. Document
|
|
||||||
the reason in PROJECT.md.
|
|
||||||
|
|
||||||
## 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) | — | — |
|
|
||||||
| 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
|
|
||||||
- **Errors**: `fmt.Errorf("operation: %w", err)` — never naked, never log-and-return
|
|
||||||
- **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
|
|
||||||
|
|
||||||
## 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 | Mac mini, ~/dev is here |
|
|
||||||
|
|
||||||
- **Model routing**: LiteLLM in front of llama-swap (local) + cloud APIs (when permitted)
|
|
||||||
- **Orchestration**: k3s cluster across all three machines
|
|
||||||
- **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)** | `POST https://brain-mcp.d-ma.be/query` with `{"query":"..."}` (auth via `BRAIN_MCP_TOKEN`) | `POST .../write` with `{"content":"...","filename":"..."}` |
|
|
||||||
| **Browser / human inspection** | `https://gitea.d-ma.be/mathias/hyperguild` → `knowledge/` and `wiki/` markdown files |
|
|
||||||
|
|
||||||
- **Scoping**: defaults to `public` collection; client projects filter to `{client}` + `public`.
|
|
||||||
- **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
|
|
||||||
|
|
||||||
This context is designed to work with any AI coding tool:
|
|
||||||
- Claude Code, Cursor, Aider, Open WebUI, Charmbracelet Mods/Crush
|
|
||||||
- Pi Coding Agent, Mistral Vibe, Antigravity
|
|
||||||
- Any tool that accepts a system prompt or reads a markdown context file
|
|
||||||
|
|
||||||
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`, `.cursorrules`, `.aider.conventions.md`,
|
|
||||||
`.context/system-prompt.txt`
|
|
||||||
|
|
||||||
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. Claude Code (tree-walking)
|
|
||||||
uses `CLAUDE.md`; Crush / Pi / Antigravity (cwd-only) use `AGENTS.md`;
|
|
||||||
Cursor uses `.cursorrules`; Aider uses `.aider.conventions.md`.
|
|
||||||
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
|
|
||||||
unconditionally on every host, every harness.
|
|
||||||
|
|
||||||
## Engineering Skills
|
|
||||||
|
|
||||||
Shared engineering skills are available in `~/dev/.skills/`. Load on demand via the index.
|
|
||||||
|
|
||||||
See `~/dev/.skills/SKILLS_INDEX.md` for the full list with descriptions and "use when" triggers.
|
|
||||||
|
|
||||||
Key skills:
|
|
||||||
- **TDD**: always write tests first — load `tdd` skill
|
|
||||||
- **Code Review**: load `code-review` skill before any review
|
|
||||||
- **SOLID/Clean Code**: load `solid` or `clean-code` skill for design work
|
|
||||||
- **Problem first**: load `problem-analysis` skill before coding non-trivial features
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# __PROJECT_NAME__
|
|
||||||
|
|
||||||
## Identity
|
|
||||||
|
|
||||||
- **Name**: __PROJECT_NAME__
|
|
||||||
- **Owner**: Mathias
|
|
||||||
- **Client**: personal
|
|
||||||
- **Repo**: gitea.d-ma.be/mathias/__PROJECT_NAME__
|
|
||||||
- **Status**: active
|
|
||||||
|
|
||||||
## Stack
|
|
||||||
|
|
||||||
Go + ADK + LiteLLM. See `~/dev/.context/AGENT.md` for cross-project conventions.
|
|
||||||
|
|
||||||
## Agent
|
|
||||||
|
|
||||||
TODO: describe what this agent does, what tools it has, and what it's responsible for.
|
|
||||||
|
|
||||||
## Observability
|
|
||||||
|
|
||||||
Traces → Jaeger via `OTLP_ENDPOINT`. Set `ADK_SERVICE_NAME=__PROJECT_NAME__` per deployment.
|
|
||||||
Spans emitted: `invoke_agent`, `generate_content`. Tool spans require custom callbacks.
|
|
||||||
|
|
||||||
---
|
|
||||||
-258
@@ -1,258 +0,0 @@
|
|||||||
# Cursor rules — auto-generated
|
|
||||||
# Do not edit. Run: task context:sync
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
**Exception — parallel agents on same repo:** If another agent is known to be
|
|
||||||
actively working on the same repo simultaneously, create a short-lived branch
|
|
||||||
(`agent/<description>`), finish the task, and merge to main within the same
|
|
||||||
session. Do not leave agent branches open between sessions.
|
|
||||||
|
|
||||||
**Exception — external contributor or client four-eyes requirement:** Use
|
|
||||||
PR flow only when a human reviewer outside the project is required. Document
|
|
||||||
the reason in PROJECT.md.
|
|
||||||
|
|
||||||
## 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) | — | — |
|
|
||||||
| 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
|
|
||||||
- **Errors**: `fmt.Errorf("operation: %w", err)` — never naked, never log-and-return
|
|
||||||
- **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
|
|
||||||
|
|
||||||
## 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 | Mac mini, ~/dev is here |
|
|
||||||
|
|
||||||
- **Model routing**: LiteLLM in front of llama-swap (local) + cloud APIs (when permitted)
|
|
||||||
- **Orchestration**: k3s cluster across all three machines
|
|
||||||
- **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)** | `POST https://brain-mcp.d-ma.be/query` with `{"query":"..."}` (auth via `BRAIN_MCP_TOKEN`) | `POST .../write` with `{"content":"...","filename":"..."}` |
|
|
||||||
| **Browser / human inspection** | `https://gitea.d-ma.be/mathias/hyperguild` → `knowledge/` and `wiki/` markdown files |
|
|
||||||
|
|
||||||
- **Scoping**: defaults to `public` collection; client projects filter to `{client}` + `public`.
|
|
||||||
- **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
|
|
||||||
|
|
||||||
This context is designed to work with any AI coding tool:
|
|
||||||
- Claude Code, Cursor, Aider, Open WebUI, Charmbracelet Mods/Crush
|
|
||||||
- Pi Coding Agent, Mistral Vibe, Antigravity
|
|
||||||
- Any tool that accepts a system prompt or reads a markdown context file
|
|
||||||
|
|
||||||
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`, `.cursorrules`, `.aider.conventions.md`,
|
|
||||||
`.context/system-prompt.txt`
|
|
||||||
|
|
||||||
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. Claude Code (tree-walking)
|
|
||||||
uses `CLAUDE.md`; Crush / Pi / Antigravity (cwd-only) use `AGENTS.md`;
|
|
||||||
Cursor uses `.cursorrules`; Aider uses `.aider.conventions.md`.
|
|
||||||
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
|
|
||||||
unconditionally on every host, every harness.
|
|
||||||
|
|
||||||
## Engineering Skills
|
|
||||||
|
|
||||||
Shared engineering skills are available in `~/dev/.skills/`. Load on demand via the index.
|
|
||||||
|
|
||||||
See `~/dev/.skills/SKILLS_INDEX.md` for the full list with descriptions and "use when" triggers.
|
|
||||||
|
|
||||||
Key skills:
|
|
||||||
- **TDD**: always write tests first — load `tdd` skill
|
|
||||||
- **Code Review**: load `code-review` skill before any review
|
|
||||||
- **SOLID/Clean Code**: load `solid` or `clean-code` skill for design work
|
|
||||||
- **Problem first**: load `problem-analysis` skill before coding non-trivial features
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# __PROJECT_NAME__
|
|
||||||
|
|
||||||
## Identity
|
|
||||||
|
|
||||||
- **Name**: __PROJECT_NAME__
|
|
||||||
- **Owner**: Mathias
|
|
||||||
- **Client**: personal
|
|
||||||
- **Repo**: gitea.d-ma.be/mathias/__PROJECT_NAME__
|
|
||||||
- **Status**: active
|
|
||||||
|
|
||||||
## Stack
|
|
||||||
|
|
||||||
Go + ADK + LiteLLM. See `~/dev/.context/AGENT.md` for cross-project conventions.
|
|
||||||
|
|
||||||
## Agent
|
|
||||||
|
|
||||||
TODO: describe what this agent does, what tools it has, and what it's responsible for.
|
|
||||||
|
|
||||||
## Observability
|
|
||||||
|
|
||||||
Traces → Jaeger via `OTLP_ENDPOINT`. Set `ADK_SERVICE_NAME=__PROJECT_NAME__` per deployment.
|
|
||||||
Spans emitted: `invoke_agent`, `generate_content`. Tool spans require custom callbacks.
|
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build an OCI image with buildctl and push it to the local registry.
|
||||||
|
#
|
||||||
|
# CANONICAL COPY: mathias/infra ci-templates/build-image.sh (infra#309).
|
||||||
|
# Every repo carries a byte-identical copy at .gitea/build-image.sh, written by
|
||||||
|
# `task ci:sync` and held in place by the drift gate. DO NOT EDIT THE COPIES --
|
||||||
|
# edit here, re-sync, and the gate will tell you which repos still disagree.
|
||||||
|
#
|
||||||
|
# Why this exists: this step was hand-copied into a dozen repos, so migrating it
|
||||||
|
# off rootless buildah (infra#241) meant eleven near-identical commits, eleven
|
||||||
|
# verifications, and three separate rediscoveries of the same fix in the two
|
||||||
|
# repo templates and the gitea-ci skill. The differences between the copies were
|
||||||
|
# incidental -- a label here, a build-arg there -- never intentional. Those are
|
||||||
|
# inputs now, not forks of the implementation.
|
||||||
|
#
|
||||||
|
# Why buildctl and not buildah: the gitea-runner pod runs with
|
||||||
|
# `capabilities: drop [ALL]` and `allowPrivilegeEscalation: false`, which
|
||||||
|
# unconditionally blocks the setuid-root exec that rootless buildah's uid/gid
|
||||||
|
# remapping needs, and the pod has no OCI runtime at all. buildctl is a thin
|
||||||
|
# client -- it hands the build to the host's buildkitd over a socket, so the
|
||||||
|
# privileged layer work happens on the host and this pod's securityContext is
|
||||||
|
# not in the picture. buildah is absent from the runner image as of baked-v14
|
||||||
|
# and its absence is asserted at pod start (infra#308).
|
||||||
|
#
|
||||||
|
# Inputs, all via environment:
|
||||||
|
#
|
||||||
|
# IMAGE (required) image name, e.g. cobalt-dingo
|
||||||
|
# REGISTRY default localhost:5000
|
||||||
|
# SHA_TAG default `git rev-parse --short HEAD`
|
||||||
|
# VERSION_TAG optional extra tag, e.g. v1.2.3 on a tag build
|
||||||
|
# PUSH_LATEST default 1; set 0 to skip the :latest tag
|
||||||
|
# CONTEXT default . -- build context directory
|
||||||
|
# DOCKERFILE_DIR default $CONTEXT -- directory holding the Dockerfile
|
||||||
|
# DOCKERFILE_NAME optional, when the file is not named `Dockerfile`
|
||||||
|
# TARGET optional, multi-stage build target (--opt target=)
|
||||||
|
# BUILD_ARGS optional, space-separated K=V pairs
|
||||||
|
# BUILD_SECRETS optional, space-separated buildkit secret specs, each
|
||||||
|
# `id=NAME,src=PATH`. Pass the SPEC only -- the caller
|
||||||
|
# writes the secret to PATH itself. A secret VALUE must
|
||||||
|
# never be passed here: it would land in this script's
|
||||||
|
# argv and therefore in the CI log.
|
||||||
|
# EXTRA_LABELS optional, space-separated K=V pairs
|
||||||
|
# GIT_SHA optional, stamped as org.opencontainers.image.revision
|
||||||
|
# IMAGE_SOURCE optional, stamped as org.opencontainers.image.source
|
||||||
|
#
|
||||||
|
# Note on shell style below: every conditional is a full `if` block, never
|
||||||
|
# `[ -n "$X" ] && cmd`. Under `set -e` the && form exits the script whenever the
|
||||||
|
# test is false, which is a real bug this estate has already shipped once.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [ -z "${IMAGE:-}" ]; then
|
||||||
|
echo "build-image: IMAGE is required" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
REGISTRY="${REGISTRY:-localhost:5000}"
|
||||||
|
BUILDKIT_HOST="${BUILDKIT_HOST:-unix:///run/buildkit/buildkitd.sock}"
|
||||||
|
CONTEXT="${CONTEXT:-.}"
|
||||||
|
DOCKERFILE_DIR="${DOCKERFILE_DIR:-$CONTEXT}"
|
||||||
|
PUSH_LATEST="${PUSH_LATEST:-1}"
|
||||||
|
VERSION_TAG="${VERSION_TAG:-}"
|
||||||
|
|
||||||
|
if [ -z "${SHA_TAG:-}" ]; then
|
||||||
|
SHA_TAG="$(git rev-parse --short HEAD)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
REF="${REGISTRY}/${IMAGE}:${SHA_TAG}"
|
||||||
|
|
||||||
|
TAR_FILE="$(mktemp)"
|
||||||
|
trap 'rm -f "$TAR_FILE"' EXIT
|
||||||
|
|
||||||
|
args=(
|
||||||
|
--frontend dockerfile.v0
|
||||||
|
--local "context=${CONTEXT}"
|
||||||
|
--local "dockerfile=${DOCKERFILE_DIR}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ -n "${DOCKERFILE_NAME:-}" ]; then
|
||||||
|
args+=(--opt "filename=${DOCKERFILE_NAME}")
|
||||||
|
fi
|
||||||
|
if [ -n "${TARGET:-}" ]; then
|
||||||
|
args+=(--opt "target=${TARGET}")
|
||||||
|
fi
|
||||||
|
for spec in ${BUILD_SECRETS:-}; do
|
||||||
|
# Opaque id=NAME,src=PATH -- buildkit reads the file, nothing is echoed.
|
||||||
|
args+=(--secret "${spec}")
|
||||||
|
done
|
||||||
|
if [ -n "${GIT_SHA:-}" ]; then
|
||||||
|
args+=(--opt "label:org.opencontainers.image.revision=${GIT_SHA}")
|
||||||
|
fi
|
||||||
|
if [ -n "${IMAGE_SOURCE:-}" ]; then
|
||||||
|
args+=(--opt "label:org.opencontainers.image.source=${IMAGE_SOURCE}")
|
||||||
|
fi
|
||||||
|
for kv in ${BUILD_ARGS:-}; do
|
||||||
|
args+=(--opt "build-arg:${kv}")
|
||||||
|
done
|
||||||
|
for kv in ${EXTRA_LABELS:-}; do
|
||||||
|
args+=(--opt "label:${kv}")
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "build-image: building ${REF}"
|
||||||
|
buildctl --addr "$BUILDKIT_HOST" build "${args[@]}" --output "type=oci,dest=${TAR_FILE}"
|
||||||
|
|
||||||
|
# buildctl has no registry-push equivalent to `buildah push`, so skopeo copies
|
||||||
|
# the OCI archive up -- once per tag, from the same archive, so every tag points
|
||||||
|
# at bit-identical content.
|
||||||
|
push_tag() {
|
||||||
|
echo "build-image: pushing ${1}"
|
||||||
|
skopeo copy --dest-tls-verify=false "oci-archive:${TAR_FILE}" "docker://${1}"
|
||||||
|
}
|
||||||
|
|
||||||
|
push_tag "$REF"
|
||||||
|
if [ "$PUSH_LATEST" = "1" ]; then
|
||||||
|
push_tag "${REGISTRY}/${IMAGE}:latest"
|
||||||
|
fi
|
||||||
|
if [ -n "$VERSION_TAG" ]; then
|
||||||
|
push_tag "${REGISTRY}/${IMAGE}:${VERSION_TAG}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✓ build-image: ${REF} pushed"
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
name: CD
|
||||||
|
|
||||||
|
"on":
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
tags: ["v*"]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE: __PROJECT_NAME__
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Lint / Test / Vet
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: Install toolchain
|
||||||
|
run: |
|
||||||
|
go version
|
||||||
|
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh \
|
||||||
|
| sh -s -- -b "$(go env GOPATH)/bin" v2.11.4
|
||||||
|
|
||||||
|
- name: Run checks
|
||||||
|
run: task check
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build & Import
|
||||||
|
needs: check
|
||||||
|
runs-on: self-hosted
|
||||||
|
# __PROJECT_NAME__/__MODULE_PATH__ are placeholders substituted only when
|
||||||
|
# a repo is generated from this template -- on the raw template repo
|
||||||
|
# itself they're still literal, and `IMAGE: __PROJECT_NAME__` contains
|
||||||
|
# uppercase letters, which the registry rejects outright ("repository
|
||||||
|
# name must be lowercase"). Build/deploy can never succeed here; skip
|
||||||
|
# them on this repo so its own CI reads green on what it can actually
|
||||||
|
# validate (the check job).
|
||||||
|
if: github.event_name != 'pull_request' && github.repository != 'mathias/template-go-agent'
|
||||||
|
outputs:
|
||||||
|
image-tag: ${{ steps.meta.outputs.sha-tag }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Derive image tags
|
||||||
|
id: meta
|
||||||
|
run: |
|
||||||
|
SHA=$(git rev-parse --short HEAD)
|
||||||
|
echo "sha-tag=${SHA}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
# infra#309: the implementation lives in .gitea/build-image.sh, a
|
||||||
|
# byte-identical copy of mathias/infra's ci-templates/build-image.sh.
|
||||||
|
# Do not edit it here -- edit the canonical file, run `task ci:sync`,
|
||||||
|
# and infra's drift gate will name any repo still carrying an old copy.
|
||||||
|
#
|
||||||
|
# This matters more in a template than anywhere else: every repo
|
||||||
|
# generated from here inherits its CI, so a template that carries the
|
||||||
|
# build LOGIC manufactures a new copy of every bug in it. That is
|
||||||
|
# precisely how rootless buildah (infra#241) reached a dozen repos, and
|
||||||
|
# why the fix then had to be rediscovered here weeks later. A template
|
||||||
|
# that carries a CALL manufactures nothing.
|
||||||
|
#
|
||||||
|
# NEW REPOS: add yourself to mathias/infra's ci-templates/adopters.txt so
|
||||||
|
# the gate keeps this copy current. Until you do, the copy is unmanaged
|
||||||
|
# and will silently fall behind.
|
||||||
|
#
|
||||||
|
# `bash <script>` rather than executing it directly: files written via
|
||||||
|
# the Gitea API land 0644, and CI that depends on a file mode is a trap.
|
||||||
|
- name: Build and push to local registry
|
||||||
|
env:
|
||||||
|
SHA_TAG: ${{ steps.meta.outputs.sha-tag }}
|
||||||
|
GIT_SHA: ${{ github.sha }}
|
||||||
|
run: bash .gitea/build-image.sh
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
name: Deploy via GitOps
|
||||||
|
needs: build
|
||||||
|
runs-on: self-hosted
|
||||||
|
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||||
|
steps:
|
||||||
|
- name: Update image tag in infra repo
|
||||||
|
env:
|
||||||
|
IMAGE_TAG: ${{ needs.build.outputs.image-tag }}
|
||||||
|
DEPLOY_KEY: ${{ secrets.INFRA_DEPLOY_KEY }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
# infra#311: fail on an empty tag BEFORE patching anything. The
|
||||||
|
# grep verification further down CANNOT catch it -- with IMAGE_TAG
|
||||||
|
# empty its pattern matches the very corruption it exists to reject.
|
||||||
|
# The result is `image: <repo>:` in the manifest, which is invalid
|
||||||
|
# YAML, so Flux fails the ENTIRE apps Kustomization and every app
|
||||||
|
# stops reconciling, not just this one. That happened on 2026-08-25.
|
||||||
|
[ -n "${IMAGE_TAG:-}" ] || { echo "IMAGE_TAG is empty - refusing to patch"; exit 1; }
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "$DEPLOY_KEY" > ~/.ssh/id_infra
|
||||||
|
chmod 600 ~/.ssh/id_infra
|
||||||
|
ssh-keyscan -p 30022 10.0.1.20 >> ~/.ssh/known_hosts 2>/dev/null
|
||||||
|
export GIT_SSH_COMMAND="ssh -i ~/.ssh/id_infra -o IdentitiesOnly=yes"
|
||||||
|
rm -rf /tmp/infra
|
||||||
|
git clone -b main ssh://git@10.0.1.20:30022/mathias/infra.git /tmp/infra
|
||||||
|
cd /tmp/infra
|
||||||
|
DEPLOYMENT="k3s/apps/__PROJECT_NAME__/deployment.yaml"
|
||||||
|
sed -i "s|image: localhost:5000/__PROJECT_NAME__:.*|image: localhost:5000/__PROJECT_NAME__:${IMAGE_TAG}|" "$DEPLOYMENT"
|
||||||
|
grep -q "localhost:5000/__PROJECT_NAME__:${IMAGE_TAG}" "$DEPLOYMENT" \
|
||||||
|
|| { echo "✗ image tag patch failed"; exit 1; }
|
||||||
|
if git diff --quiet "$DEPLOYMENT"; then
|
||||||
|
echo "ℹ image tag unchanged — skipping push"
|
||||||
|
else
|
||||||
|
git -c user.name="__PROJECT_NAME__ CI" \
|
||||||
|
-c user.email="ci@__PROJECT_NAME__.local" \
|
||||||
|
commit -m "chore(deploy): __PROJECT_NAME__ → ${IMAGE_TAG}" "$DEPLOYMENT"
|
||||||
|
git push origin main
|
||||||
|
echo "✓ pushed to infra repo"
|
||||||
|
fi
|
||||||
|
shred -u ~/.ssh/id_infra
|
||||||
|
|
||||||
|
- name: Trigger Flux reconcile
|
||||||
|
run: |
|
||||||
|
kubectl -n flux-system annotate gitrepository flux-system \
|
||||||
|
reconcile.fluxcd.io/requestedAt="$(date +%s)" --overwrite
|
||||||
|
kubectl -n flux-system annotate kustomization apps \
|
||||||
|
reconcile.fluxcd.io/requestedAt="$(date +%s)" --overwrite
|
||||||
|
|
||||||
|
- name: Verify rollout
|
||||||
|
run: |
|
||||||
|
kubectl rollout status deployment/__PROJECT_NAME__ \
|
||||||
|
--namespace __PROJECT_NAME__ \
|
||||||
|
--timeout=120s \
|
||||||
|
|| {
|
||||||
|
kubectl get pods -n __PROJECT_NAME__ -o wide
|
||||||
|
kubectl get events -n __PROJECT_NAME__ --sort-by='.lastTimestamp' | tail -20
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Canonical golangci-lint configuration for the estate's Go repos (infra#309
|
||||||
|
# pattern: one canonical file here, a byte-identical copy in each adopter,
|
||||||
|
# and the drift gate in THIS repo comparing sha256).
|
||||||
|
#
|
||||||
|
# Why this exists: 26 Go repos, 3 had a lint config, and all three were
|
||||||
|
# different. The 23 without one ran golangci-lint's v2 default set, which
|
||||||
|
# does NOT include a formatter -- so gofmt/gofumpt drift went uncaught until
|
||||||
|
# it was found by hand (dma-cli#12: two files drifted, nothing flagged them).
|
||||||
|
#
|
||||||
|
# The two configs this replaces (cobalt-dingo, coo-agent) were near-identical
|
||||||
|
# copies of each other AND both invalid under v2: they carried v1-style
|
||||||
|
# `linters-settings` and `issues.exclude-rules` keys, which `golangci-lint
|
||||||
|
# config verify` rejects outright. Their revive settings and test exclusions
|
||||||
|
# were therefore not in effect. Consolidating fixes that too.
|
||||||
|
#
|
||||||
|
# Per-repo variation does NOT belong here -- this file must stay byte-identical
|
||||||
|
# across adopters so the gate is a checksum, not a diff a human has to judge.
|
||||||
|
# A repo with a genuine exception (agentsquad excludes its seeded-bug fixture
|
||||||
|
# tree, which exists precisely to contain broken code) keeps its own config and
|
||||||
|
# is not listed as an adopter.
|
||||||
|
|
||||||
|
version: "2"
|
||||||
|
|
||||||
|
linters:
|
||||||
|
default: none
|
||||||
|
enable:
|
||||||
|
- errcheck # unchecked errors
|
||||||
|
- govet # suspicious constructs
|
||||||
|
- staticcheck # broad static analysis
|
||||||
|
- unused # unused code
|
||||||
|
- misspell # spelling
|
||||||
|
- revive # idiomatic Go
|
||||||
|
- exhaustive # exhaustive switch on enums
|
||||||
|
|
||||||
|
settings:
|
||||||
|
revive:
|
||||||
|
rules:
|
||||||
|
- name: var-naming
|
||||||
|
severity: warning
|
||||||
|
|
||||||
|
exclusions:
|
||||||
|
rules:
|
||||||
|
# Test files: table-driven cases legitimately carry unused struct fields
|
||||||
|
# and long functions.
|
||||||
|
- path: _test\.go
|
||||||
|
linters:
|
||||||
|
- revive
|
||||||
|
- unused
|
||||||
|
|
||||||
|
formatters:
|
||||||
|
enable:
|
||||||
|
- gofumpt
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"knowledge": {
|
|
||||||
"url": "http://localhost:3100/mcp",
|
|
||||||
"description": "Project knowledge base — vector + graph retrieval"
|
|
||||||
},
|
|
||||||
"brain": {
|
"brain": {
|
||||||
"type": "http",
|
"type": "http",
|
||||||
"url": "https://brain-mcp.d-ma.be/mcp",
|
"url": "https://brain-mcp.d-ma.be/mcp",
|
||||||
@@ -17,10 +13,6 @@
|
|||||||
"headers": {
|
"headers": {
|
||||||
"Authorization": "Bearer ${GITEA_MCP_TOKEN}"
|
"Authorization": "Bearer ${GITEA_MCP_TOKEN}"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"infra": {
|
|
||||||
"type": "http",
|
|
||||||
"url": "https://infra-mcp.d-ma.be/mcp"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,255 +1,44 @@
|
|||||||
# Agent context — Mathias workspace
|
# __PROJECT_NAME__ — agent context
|
||||||
|
|
||||||
<!-- Canonical root context for all AI coding agents.
|
Standalone context for agents that read `AGENTS.md` from the repo root. Claude Code uses
|
||||||
Lives at: ~/dev/.context/AGENT.md
|
`CLAUDE.md` (and tree-walks to `~/dev/CLAUDE.md`); this file mirrors the same essentials for
|
||||||
Applies to every project under ~/dev/ unless overridden.
|
any other harness.
|
||||||
|
|
||||||
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
|
## Project
|
||||||
|
|
||||||
I'm Mathias, a digital product manager and technology consultant based in Sweden.
|
- **Name**: __PROJECT_NAME__ — a Go agent (ADK + LiteLLM).
|
||||||
I build software, research emerging tech, and deliver consulting engagements
|
- **Owner**: Mathias · **Client**: personal · **Repo**: git.d-ma.be/mathias/__PROJECT_NAME__
|
||||||
for clients under NDA. I work across AI/ML, financial automation, web applications,
|
|
||||||
and climate/sustainability tech.
|
|
||||||
|
|
||||||
## How I work with agents
|
## Harness
|
||||||
|
|
||||||
- I think like a product manager — I care about *why* before *how*
|
Single harness: **Claude Code + hyperguild** — session-based (start a session, it works, it
|
||||||
- I want agents to be opinionated and push back, not just execute blindly
|
stops). There is **no** push-triggered dispatch, listener, or scheduled poller, and none should
|
||||||
- I prefer concise responses; skip ceremony and get to the point
|
be added without a fresh, friction-driven decision. MCP connections in `.mcp.json`:
|
||||||
- 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
|
- **brain** (`brain-mcp.d-ma.be`) — knowledge query/write. `BRAIN_MCP_TOKEN`.
|
||||||
|
- **gitea** (`git-mcp.d-ma.be`) — issues/PRs/repo, the audit trail. `GITEA_MCP_TOKEN`.
|
||||||
|
|
||||||
These rules apply to every task across every project, regardless of harness.
|
## Workflow
|
||||||
|
|
||||||
1. **No assumptions.** Don't hide confusion — surface it. Surface tradeoffs explicitly.
|
1. **Issue** — work is scoped as a Gitea issue (acceptance criteria + report-back format).
|
||||||
Think before coding; if the problem is unclear, ask or state assumptions before acting.
|
2. **Session** — a Claude Code + hyperguild session does the work in this repo.
|
||||||
2. **Minimum viable code.** Solve with the smallest change that works. Nothing
|
3. **PR** — branch off `main`; open a PR; CI (`task check`) is the gate.
|
||||||
speculative, no "while we're here" cleanups, no premature abstractions. Simplicity first.
|
4. **Report-back** — comment the outcome on the issue (issue comments are the audit surface).
|
||||||
3. **Surgical changes.** Touch only what the task requires. Leave unrelated code,
|
5. **Brain capture** — persist durable, non-obvious learnings to the brain.
|
||||||
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.
|
|
||||||
|
|
||||||
**Exception — parallel agents on same repo:** If another agent is known to be
|
## Conventions
|
||||||
actively working on the same repo simultaneously, create a short-lived branch
|
|
||||||
(`agent/<description>`), finish the task, and merge to main within the same
|
|
||||||
session. Do not leave agent branches open between sessions.
|
|
||||||
|
|
||||||
**Exception — external contributor or client four-eyes requirement:** Use
|
- Go: standard tooling; `task check` (lint + vet + `go test -race`) before every PR.
|
||||||
PR flow only when a human reviewer outside the project is required. Document
|
- Errors wrapped (`fmt.Errorf("...: %w", err)`); structured `slog`.
|
||||||
the reason in PROJECT.md.
|
- Trunk-based: commit to `main` via PR; one logical change per commit; CI is the quality gate.
|
||||||
|
- Secrets via env (`BRAIN_MCP_TOKEN`, `GITEA_MCP_TOKEN`, `LITELLM_API_KEY`); never in code.
|
||||||
|
|
||||||
## Default stack
|
## Boundaries
|
||||||
|
|
||||||
| Layer | Default | Fallback | Last resort |
|
Runtime scope (network egress allow-list, file scope, approved operations, failure posture) is
|
||||||
|-------|---------|----------|-------------|
|
in `AGENT_BOUNDARIES.md`. Read it before adding tools, endpoints, or write access.
|
||||||
| 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) | — | — |
|
|
||||||
| 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
|
|
||||||
- **Errors**: `fmt.Errorf("operation: %w", err)` — never naked, never log-and-return
|
|
||||||
- **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
|
|
||||||
|
|
||||||
## 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 | Mac mini, ~/dev is here |
|
|
||||||
|
|
||||||
- **Model routing**: LiteLLM in front of llama-swap (local) + cloud APIs (when permitted)
|
|
||||||
- **Orchestration**: k3s cluster across all three machines
|
|
||||||
- **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)** | `POST https://brain-mcp.d-ma.be/query` with `{"query":"..."}` (auth via `BRAIN_MCP_TOKEN`) | `POST .../write` with `{"content":"...","filename":"..."}` |
|
|
||||||
| **Browser / human inspection** | `https://gitea.d-ma.be/mathias/hyperguild` → `knowledge/` and `wiki/` markdown files |
|
|
||||||
|
|
||||||
- **Scoping**: defaults to `public` collection; client projects filter to `{client}` + `public`.
|
|
||||||
- **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
|
|
||||||
|
|
||||||
This context is designed to work with any AI coding tool:
|
|
||||||
- Claude Code, Cursor, Aider, Open WebUI, Charmbracelet Mods/Crush
|
|
||||||
- Pi Coding Agent, Mistral Vibe, Antigravity
|
|
||||||
- Any tool that accepts a system prompt or reads a markdown context file
|
|
||||||
|
|
||||||
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`, `.cursorrules`, `.aider.conventions.md`,
|
|
||||||
`.context/system-prompt.txt`
|
|
||||||
|
|
||||||
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. Claude Code (tree-walking)
|
|
||||||
uses `CLAUDE.md`; Crush / Pi / Antigravity (cwd-only) use `AGENTS.md`;
|
|
||||||
Cursor uses `.cursorrules`; Aider uses `.aider.conventions.md`.
|
|
||||||
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
|
|
||||||
unconditionally on every host, every harness.
|
|
||||||
|
|
||||||
## Engineering Skills
|
|
||||||
|
|
||||||
Shared engineering skills are available in `~/dev/.skills/`. Load on demand via the index.
|
|
||||||
|
|
||||||
See `~/dev/.skills/SKILLS_INDEX.md` for the full list with descriptions and "use when" triggers.
|
|
||||||
|
|
||||||
Key skills:
|
|
||||||
- **TDD**: always write tests first — load `tdd` skill
|
|
||||||
- **Code Review**: load `code-review` skill before any review
|
|
||||||
- **SOLID/Clean Code**: load `solid` or `clean-code` skill for design work
|
|
||||||
- **Problem first**: load `problem-analysis` skill before coding non-trivial features
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# __PROJECT_NAME__
|
|
||||||
|
|
||||||
## Identity
|
|
||||||
|
|
||||||
- **Name**: __PROJECT_NAME__
|
|
||||||
- **Owner**: Mathias
|
|
||||||
- **Client**: personal
|
|
||||||
- **Repo**: gitea.d-ma.be/mathias/__PROJECT_NAME__
|
|
||||||
- **Status**: active
|
|
||||||
|
|
||||||
## Stack
|
|
||||||
|
|
||||||
Go + ADK + LiteLLM. See `~/dev/.context/AGENT.md` for cross-project conventions.
|
|
||||||
|
|
||||||
## Agent
|
|
||||||
|
|
||||||
TODO: describe what this agent does, what tools it has, and what it's responsible for.
|
|
||||||
|
|
||||||
## Observability
|
## Observability
|
||||||
|
|
||||||
Traces → Jaeger via `OTLP_ENDPOINT`. Set `ADK_SERVICE_NAME=__PROJECT_NAME__` per deployment.
|
Traces → Jaeger via `OTLP_ENDPOINT`; `ADK_SERVICE_NAME=__PROJECT_NAME__`. Spans: `invoke_agent`,
|
||||||
Spans emitted: `invoke_agent`, `generate_content`. Tool spans require custom callbacks.
|
`generate_content`.
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# Agent boundaries — __PROJECT_NAME__
|
||||||
|
|
||||||
|
Operational scope for this agent. Read before extending tools, adding endpoints,
|
||||||
|
or wiring new data sources. Defaults are conservative; widen explicitly.
|
||||||
|
|
||||||
|
## Network scope
|
||||||
|
|
||||||
|
Egress is allow-listed. The agent MAY reach:
|
||||||
|
|
||||||
|
| Endpoint | Purpose | Protocol | Default port |
|
||||||
|
|--------------------------------|----------------------------------|----------|--------------|
|
||||||
|
| `llm-api.d-ma.be` (LiteLLM) | Model inference | HTTPS | 443 |
|
||||||
|
| `brain-mcp.d-ma.be` | Knowledge base (BM25 + synth) | HTTPS | 443 |
|
||||||
|
| `gitea-mcp.d-ma.be` | Repo/issue/PR ops | HTTPS | 443 |
|
||||||
|
| `jaeger.d-ma.be` | OTLP trace export | HTTP | 4318 |
|
||||||
|
| In-cluster DNS (`kube-dns`) | Service discovery | UDP | 53 |
|
||||||
|
|
||||||
|
Egress MUST be blocked to:
|
||||||
|
|
||||||
|
- Public Slack / Discord / Telegram / email
|
||||||
|
- Third-party LLM APIs (OpenAI, Anthropic, Google) unless explicitly added below
|
||||||
|
- Public package registries from runtime (proxy through build only)
|
||||||
|
- Customer/client domains not listed in the engagement scope
|
||||||
|
|
||||||
|
Add new endpoints by editing this table in the same commit that wires them, and enforce them
|
||||||
|
with whatever egress mechanism the deployment uses (e.g. a k8s NetworkPolicy in the project's
|
||||||
|
own deploy manifests).
|
||||||
|
|
||||||
|
## File scope
|
||||||
|
|
||||||
|
Read scope:
|
||||||
|
|
||||||
|
- Mounted ConfigMaps and Secrets under `/etc/__PROJECT_NAME__/`
|
||||||
|
- Working dir under `/var/lib/__PROJECT_NAME__/` (ephemeral)
|
||||||
|
- `/tmp` for streaming artifacts (size-capped via emptyDir)
|
||||||
|
|
||||||
|
Write scope:
|
||||||
|
|
||||||
|
- `/var/lib/__PROJECT_NAME__/` only
|
||||||
|
- `/tmp` only
|
||||||
|
- Never `/`, `/etc`, `/var/log`, host paths, or sibling pod volumes
|
||||||
|
|
||||||
|
Persistent state MUST live in PostgreSQL (`postgres.d-ma.be`) or object storage —
|
||||||
|
not the pod filesystem.
|
||||||
|
|
||||||
|
## Approved operations
|
||||||
|
|
||||||
|
The agent MAY:
|
||||||
|
|
||||||
|
- Issue LLM inference requests via the LiteLLM adapter
|
||||||
|
- Query the brain MCP for prior art / postmortems
|
||||||
|
- Read repos, issues, and PRs via gitea-mcp
|
||||||
|
- Emit OTLP spans for `invoke_agent`, `generate_content`, and any custom tool spans
|
||||||
|
- Write to its own k8s logs (stdout/stderr; structured `slog`)
|
||||||
|
|
||||||
|
The agent MUST NOT, without explicit opt-in in code review:
|
||||||
|
|
||||||
|
- Write to gitea (open PR, push branch, comment) — requires `--write` flag and audit log
|
||||||
|
- Execute shell commands in non-sandboxed contexts
|
||||||
|
- Mutate k8s resources (no kubectl/Helm/Flux operations from runtime)
|
||||||
|
- Forward LLM outputs to external systems before redaction
|
||||||
|
- Hold conversation logs longer than the session TTL (24h default)
|
||||||
|
|
||||||
|
## Failure posture
|
||||||
|
|
||||||
|
- Outbound DNS failure → fail closed, retry with jitter, surface to caller
|
||||||
|
- LLM 5xx → bounded retry (3 attempts), then return error to caller
|
||||||
|
- Brain MCP unavailable → continue without prior-art lookup; log degraded mode
|
||||||
|
- OTLP unavailable → drop spans silently; never block request path
|
||||||
|
|
||||||
|
## Review triggers
|
||||||
|
|
||||||
|
Bump this doc (and the deployment's egress enforcement) when:
|
||||||
|
|
||||||
|
- A new external endpoint is added
|
||||||
|
- A new tool with side effects is wired in
|
||||||
|
- The agent gains write access to anything (repo, DB table, filesystem path)
|
||||||
|
- A client engagement narrows or widens the data scope
|
||||||
@@ -5,18 +5,38 @@
|
|||||||
- **Name**: __PROJECT_NAME__
|
- **Name**: __PROJECT_NAME__
|
||||||
- **Owner**: Mathias
|
- **Owner**: Mathias
|
||||||
- **Client**: personal
|
- **Client**: personal
|
||||||
- **Repo**: gitea.d-ma.be/mathias/__PROJECT_NAME__
|
- **Repo**: git.d-ma.be/mathias/__PROJECT_NAME__
|
||||||
- **Status**: active
|
- **Status**: active
|
||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
|
|
||||||
Go + ADK + LiteLLM. See `~/dev/.context/AGENT.md` for cross-project conventions.
|
Go + ADK + LiteLLM. Claude Code tree-walks to `~/dev/CLAUDE.md` for cross-project conventions.
|
||||||
|
|
||||||
## Agent
|
## Agent
|
||||||
|
|
||||||
TODO: describe what this agent does, what tools it has, and what it's responsible for.
|
TODO: describe what this agent does, what tools it has, and what it's responsible for.
|
||||||
|
|
||||||
|
## Harness & workflow
|
||||||
|
|
||||||
|
Single harness: **Claude Code + hyperguild** — session-based (you start a session, it works,
|
||||||
|
it stops; there is no push-triggered dispatch). MCP connections are in `.mcp.json`: **brain**
|
||||||
|
(knowledge query/write) and **gitea** (issues/PRs/repo — the audit trail). Provide
|
||||||
|
`BRAIN_MCP_TOKEN` and `GITEA_MCP_TOKEN` in the environment.
|
||||||
|
|
||||||
|
The loop for a unit of work:
|
||||||
|
|
||||||
|
1. **Issue** — the work is a Gitea issue (scope, acceptance criteria, report-back format).
|
||||||
|
2. **Session** — a Claude Code + hyperguild session does the work in this repo.
|
||||||
|
3. **PR** — branch off `main`, open a PR; CI (`task check`) is the gate.
|
||||||
|
4. **Report-back** — comment the outcome on the issue (issue comments are the audit surface).
|
||||||
|
5. **Brain capture** — if a learning is durable and non-obvious, write it to the brain.
|
||||||
|
|
||||||
## Observability
|
## Observability
|
||||||
|
|
||||||
Traces → Jaeger via `OTLP_ENDPOINT`. Set `ADK_SERVICE_NAME=__PROJECT_NAME__` per deployment.
|
Traces → Jaeger via `OTLP_ENDPOINT`. Set `ADK_SERVICE_NAME=__PROJECT_NAME__` per deployment.
|
||||||
Spans emitted: `invoke_agent`, `generate_content`. Tool spans require custom callbacks.
|
Spans emitted: `invoke_agent`, `generate_content`. Tool spans require custom callbacks.
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
Runtime scope (network egress, file scope, approved operations) is in `AGENT_BOUNDARIES.md` —
|
||||||
|
read it before adding tools, endpoints, or write access.
|
||||||
|
|||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
FROM golang:1.26-alpine AS build
|
||||||
|
WORKDIR /src
|
||||||
|
RUN apk add --no-cache git
|
||||||
|
COPY go.mod ./
|
||||||
|
RUN go mod download
|
||||||
|
COPY . .
|
||||||
|
RUN CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o /out/app ./cmd/__PROJECT_NAME__
|
||||||
|
|
||||||
|
FROM gcr.io/distroless/static-debian12:nonroot
|
||||||
|
COPY --from=build /out/app /app
|
||||||
|
USER nonroot:nonroot
|
||||||
|
ENTRYPOINT ["/app"]
|
||||||
@@ -11,6 +11,16 @@ go mod tidy
|
|||||||
task run
|
task run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Harness & workflow
|
||||||
|
|
||||||
|
Single harness: **Claude Code + hyperguild** — session-based (start a session, it works, it
|
||||||
|
stops). No push-triggered dispatch. MCP connections live in `.mcp.json`: **brain** (knowledge)
|
||||||
|
and **gitea** (issues/PRs — the audit trail); provide `BRAIN_MCP_TOKEN` and `GITEA_MCP_TOKEN`.
|
||||||
|
|
||||||
|
Work flows: **issue → hyperguild session → PR (`task check` gate) → report-back comment on the
|
||||||
|
issue → brain capture if the learning is durable**. See `AGENTS.md` / `CLAUDE.md` for the full
|
||||||
|
context, `AGENT_BOUNDARIES.md` for runtime scope.
|
||||||
|
|
||||||
## Observability
|
## Observability
|
||||||
|
|
||||||
Set `OTLP_ENDPOINT=http://jaeger.d-ma.be:4318` to emit traces. Each invocation produces:
|
Set `OTLP_ENDPOINT=http://jaeger.d-ma.be:4318` to emit traces. Each invocation produces:
|
||||||
|
|||||||
+5
-11
@@ -23,14 +23,8 @@ tasks:
|
|||||||
- golangci-lint run ./...
|
- golangci-lint run ./...
|
||||||
- go vet ./...
|
- go vet ./...
|
||||||
- go test ./... -race -count=1
|
- go test ./... -race -count=1
|
||||||
|
# The three commands above cannot see cmd/__PROJECT_NAME__ -- Go's ./...
|
||||||
context:sync:
|
# expansion skips directories whose names start with an underscore, which
|
||||||
desc: Regenerate all harness-specific context files
|
# is why #1 and #2 shipped green here and red in every generated repo.
|
||||||
cmds:
|
# This gates the substituted result instead.
|
||||||
- bash scripts/context-sync.sh
|
- bash scripts/tests/generated-child_test.sh
|
||||||
context:sync:claude:
|
|
||||||
cmds: [bash scripts/context-sync.sh claude]
|
|
||||||
context:sync:agents:
|
|
||||||
cmds: [bash scripts/context-sync.sh agents]
|
|
||||||
context:sync:cursor:
|
|
||||||
cmds: [bash scripts/context-sync.sh cursor]
|
|
||||||
|
|||||||
@@ -22,7 +22,13 @@ func main() {
|
|||||||
fmt.Fprintf(os.Stderr, "telemetry: %v\n", err)
|
fmt.Fprintf(os.Stderr, "telemetry: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
defer shutdown(ctx)
|
// Wrapped rather than bare: errcheck flags a deferred call that drops an
|
||||||
|
// error, and SetupTelemetry returns func(context.Context) error. The bare
|
||||||
|
// form shipped for months because this package lives in a directory whose
|
||||||
|
// name starts with an underscore, which Go's ./... expansion skips -- so
|
||||||
|
// the template's own lint never saw it while every generated repo, where
|
||||||
|
// the directory has a real name, inherited the failure.
|
||||||
|
defer func() { _ = shutdown(ctx) }()
|
||||||
|
|
||||||
llm := litellm.New(
|
llm := litellm.New(
|
||||||
env("LITELLM_MODEL", "berget/llama-3.3-70b"),
|
env("LITELLM_MODEL", "berget/llama-3.3-70b"),
|
||||||
|
|||||||
@@ -3,6 +3,51 @@ module __MODULE_PATH__
|
|||||||
go 1.26
|
go 1.26
|
||||||
|
|
||||||
require (
|
require (
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0
|
go.opentelemetry.io/otel v1.45.0
|
||||||
google.golang.org/adk v1.2.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0
|
||||||
|
go.opentelemetry.io/otel/sdk v1.45.0
|
||||||
|
google.golang.org/adk v1.7.0
|
||||||
|
google.golang.org/genai v1.57.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go v0.123.0 // indirect
|
||||||
|
cloud.google.com/go/auth v0.20.0 // indirect
|
||||||
|
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||||
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.35.0 // indirect
|
||||||
|
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.4 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
|
github.com/google/s2a-go v0.1.9 // indirect
|
||||||
|
github.com/google/safehtml v0.1.0 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.15 // indirect
|
||||||
|
github.com/googleapis/gax-go/v2 v2.22.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
|
go.opentelemetry.io/contrib/detectors/gcp v1.45.0 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.21.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/log v0.21.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.45.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/sdk/log v0.21.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.45.0 // indirect
|
||||||
|
go.opentelemetry.io/proto/otlp v1.11.0 // indirect
|
||||||
|
golang.org/x/crypto v0.54.0 // indirect
|
||||||
|
golang.org/x/net v0.57.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.36.0 // indirect
|
||||||
|
golang.org/x/sys v0.47.0 // indirect
|
||||||
|
golang.org/x/text v0.40.0 // indirect
|
||||||
|
google.golang.org/api v0.279.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d // indirect
|
||||||
|
google.golang.org/grpc v1.83.1 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
|
rsc.io/omap v1.2.0 // indirect
|
||||||
|
rsc.io/ordered v1.1.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
|
||||||
|
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
|
||||||
|
cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA=
|
||||||
|
cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q=
|
||||||
|
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||||
|
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||||
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.35.0 h1:bN1gA3of5bXtbnLsRPrwfmbbe7A5UWFlcTHseujLnpc=
|
||||||
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.35.0/go.mod h1:Yj5vHEz/aAepZGliRJsA6uvHAVAQyEwajq9ORCHPxzM=
|
||||||
|
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||||
|
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
|
||||||
|
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
|
||||||
|
github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
|
||||||
|
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||||
|
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||||
|
github.com/google/safehtml v0.1.0 h1:EwLKo8qawTKfsi0orxcQAZzu07cICaBeFMegAU9eaT8=
|
||||||
|
github.com/google/safehtml v0.1.0/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.15 h1:xolVQTEXusUcAA5UgtyRLjelpFFHWlPQ4XfWGc7MBas=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.15/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.22.0 h1:PjIWBpgGIVKGoCXuiCoP64altEJCj3/Ei+kSU5vlZD4=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.22.0/go.mod h1:irWBbALSr0Sk3qlqb9SyJ1h68WjgeFuiOzI4Rqw5+aY=
|
||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
|
go.opentelemetry.io/contrib/detectors/gcp v1.45.0 h1:9jR0ZPRok9ryaOQ2Wx8rg5F7Aon59mxrqbVI60/vlBk=
|
||||||
|
go.opentelemetry.io/contrib/detectors/gcp v1.45.0/go.mod h1:VSme3o2fvSg5bVg0dRzyHaj4Z5EVhG+g2Fde6LKzmQA=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
|
||||||
|
go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
|
||||||
|
go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.21.0 h1:fvNHGyo3CdRv/DQveXqhqBxnKTDyRaC5sMSQxilX/A0=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.21.0/go.mod h1:zyGrjRKL2B/6+Jc/m4/otPoZqV2MY9ZjC/aBraRO7zc=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0 h1:QRefszxJmfPdjXUUm3j6iDzY03mTPXMjqErFqQ67vUg=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0/go.mod h1:Tiz03lTBVBrm7eWZBOidzEaYaJa8tjwGUGv6d8mlTyk=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0 h1:QBajQ2SrwQijzHyZbQlPsuIzpl/ll8DY6wPWsajeGcI=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0/go.mod h1:08ZQLjrPLQ6R4kAXvuOvODEer5Yh4CoFvll5qB2BCI8=
|
||||||
|
go.opentelemetry.io/otel/log v0.21.0 h1:SLsVDGmtyBrdw8/a2Z0bOIxou/+bN4z56GebH7T0LvA=
|
||||||
|
go.opentelemetry.io/otel/log v0.21.0/go.mod h1:iReetQrZL9Wyg84cCkOoCmqDHS5RCFfyxC7J+r8fn8g=
|
||||||
|
go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
|
||||||
|
go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
|
||||||
|
go.opentelemetry.io/otel/sdk/log v0.21.0 h1:QsE7XSR0ktQdKmRKGnR+f1ObGF32WG+7MER/P9KgmYc=
|
||||||
|
go.opentelemetry.io/otel/sdk/log v0.21.0/go.mod h1:m9mApjCoD2/1QuKCAptjv+BrG9WKOvQLVdNx+iBldTo=
|
||||||
|
go.opentelemetry.io/otel/sdk/log/logtest v0.21.0 h1:X+JBBgKlswCGYsmgL0CnoUUtlE//VB345c84jYAYkdQ=
|
||||||
|
go.opentelemetry.io/otel/sdk/log/logtest v0.21.0/go.mod h1:HD1575K8e6sIFBBDd5tZB3t9DlMytWXq9FuR+Y4rfjE=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA=
|
||||||
|
go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
|
||||||
|
go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
|
||||||
|
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
|
||||||
|
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||||
|
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||||
|
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||||
|
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||||
|
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||||
|
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||||
|
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||||
|
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||||
|
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||||
|
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||||
|
google.golang.org/adk v1.7.0 h1:EHSlil6b/KG3OquU+uKDA4cvz/Fyn8Nsch5MysXEehc=
|
||||||
|
google.golang.org/adk v1.7.0/go.mod h1:oT+rWCkv5RmheB18bKTyUeqhdPcwD3kWE4Gs6/UcnNY=
|
||||||
|
google.golang.org/api v0.279.0 h1:hsx2M2OaRcaKtVYK6vXEUnQvdjnend7ZYES+lYaot74=
|
||||||
|
google.golang.org/api v0.279.0/go.mod h1:B9TqLBwJqVjp1mtt7WeoQwWRwvu/400y5lETOql+giQ=
|
||||||
|
google.golang.org/genai v1.57.0 h1:qTyG2ynz5dQy2jF4CvZdLHHVslhR0heMue+zM1a4GNM=
|
||||||
|
google.golang.org/genai v1.57.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk=
|
||||||
|
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
|
||||||
|
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d h1:FarXi840EJWSHYTN3ERkADbPWjl307+FGrA22KAVjjc=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d/go.mod h1:K/+WGbmBY7aNW1HDw1fJnKYo10i0DkAX6pows00dLig=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d h1:IL4hdHzcUv2l/gcg98/Rj3FbtE6axwqslOW8SW0C+S0=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
|
google.golang.org/grpc v1.83.1 h1:HIO0+BEtBP6soyqvqC8sNUjZ7bTs+0hFQuFF+RAy++Y=
|
||||||
|
google.golang.org/grpc v1.83.1/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
|
||||||
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
rsc.io/omap v1.2.0 h1:c1M8jchnHbzmJALzGLclfH3xDWXrPxSUHXzH5C+8Kdw=
|
||||||
|
rsc.io/omap v1.2.0/go.mod h1:C8pkI0AWexHopQtZX+qiUeJGzvc8HkdgnsWK4/mAa00=
|
||||||
|
rsc.io/ordered v1.1.1 h1:1kZM6RkTmceJgsFH/8DLQvkCVEYomVDJfBRLT595Uak=
|
||||||
|
rsc.io/ordered v1.1.1/go.mod h1:evAi8739bWVBRG9aaufsjVc202+6okf8u2QeVL84BCM=
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// Package agent wraps the ADK runner with the project's defaults.
|
||||||
|
//
|
||||||
|
// Keep this thin: construction, defaults, and the Run loop. Tool wiring,
|
||||||
|
// callbacks, and any business logic belong in sibling packages.
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"iter"
|
||||||
|
|
||||||
|
"google.golang.org/adk/agent"
|
||||||
|
"google.golang.org/adk/agent/llmagent"
|
||||||
|
"google.golang.org/adk/model"
|
||||||
|
"google.golang.org/adk/runner"
|
||||||
|
"google.golang.org/adk/session"
|
||||||
|
"google.golang.org/genai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config holds the minimum needed to spin up an agent + runner.
|
||||||
|
type Config struct {
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
Instruction string
|
||||||
|
Model model.LLM
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agent is the runnable unit. Construct with New, drive with Run.
|
||||||
|
type Agent struct {
|
||||||
|
name string
|
||||||
|
runner *runner.Runner
|
||||||
|
}
|
||||||
|
|
||||||
|
// New builds an llmagent and an in-memory-session runner around it.
|
||||||
|
func New(cfg Config) (*Agent, error) {
|
||||||
|
if cfg.Name == "" {
|
||||||
|
return nil, fmt.Errorf("agent: Name required")
|
||||||
|
}
|
||||||
|
if cfg.Model == nil {
|
||||||
|
return nil, fmt.Errorf("agent: Model required")
|
||||||
|
}
|
||||||
|
|
||||||
|
ag, err := llmagent.New(llmagent.Config{
|
||||||
|
Name: cfg.Name,
|
||||||
|
Description: cfg.Description,
|
||||||
|
Model: cfg.Model,
|
||||||
|
Instruction: cfg.Instruction,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("agent: build llmagent: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := runner.New(runner.Config{
|
||||||
|
AppName: cfg.Name,
|
||||||
|
Agent: ag,
|
||||||
|
SessionService: session.InMemoryService(),
|
||||||
|
AutoCreateSession: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("agent: build runner: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Agent{name: cfg.Name, runner: r}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run dispatches a single user turn and returns the event iterator.
|
||||||
|
// The caller is responsible for draining it and handling errors.
|
||||||
|
func (a *Agent) Run(ctx context.Context, userID, sessionID, text string) iter.Seq2[*session.Event, error] {
|
||||||
|
msg := genai.NewContentFromText(text, "user")
|
||||||
|
return a.runner.Run(ctx, userID, sessionID, msg, agent.RunConfig{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the registered agent name (also used as ADK AppName).
|
||||||
|
func (a *Agent) Name() string { return a.name }
|
||||||
@@ -112,7 +112,7 @@ func (m *Model) GenerateContent(ctx context.Context, req *adkmodel.LLMRequest, _
|
|||||||
yield(nil, fmt.Errorf("http: %w", err))
|
yield(nil, fmt.Errorf("http: %w", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
raw, err := io.ReadAll(resp.Body)
|
raw, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,201 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Generates harness-specific context files from .context/PROJECT.md
|
|
||||||
# Project-level script — run from a project directory.
|
|
||||||
#
|
|
||||||
# For Claude Code: generates project-only CLAUDE.md (it inherits root via tree walk)
|
|
||||||
# For everything else: concatenates root AGENT.md + project PROJECT.md
|
|
||||||
#
|
|
||||||
# Usage: ./scripts/context-sync.sh [--force] [adapter...]
|
|
||||||
# Task: task context:sync
|
|
||||||
#
|
|
||||||
# Override root context: ROOT_CONTEXT=~/dev/.context/AGENT.md ./scripts/context-sync.sh
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Parse --force flag and collect adapter names separately
|
|
||||||
FORCE=false
|
|
||||||
ADAPTERS=()
|
|
||||||
for _arg in "$@"; do
|
|
||||||
case "$_arg" in
|
|
||||||
--force) FORCE=true ;;
|
|
||||||
*) ADAPTERS+=("$_arg") ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
PROJECT_FILE=".context/PROJECT.md"
|
|
||||||
|
|
||||||
# Walk up to find root .context/AGENT.md
|
|
||||||
find_root_context() {
|
|
||||||
local dir
|
|
||||||
dir="$(pwd)"
|
|
||||||
while [ "$dir" != "/" ]; do
|
|
||||||
dir="$(dirname "$dir")"
|
|
||||||
if [ -f "$dir/.context/AGENT.md" ]; then
|
|
||||||
echo "$dir/.context/AGENT.md"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
ROOT_CONTEXT="${ROOT_CONTEXT:-$(find_root_context)}"
|
|
||||||
|
|
||||||
if [ ! -f "$PROJECT_FILE" ]; then
|
|
||||||
echo "Error: $PROJECT_FILE not found. Are you in a project root?"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Pre-flight: reject unfilled {{...}} placeholders unless --force
|
|
||||||
if [ "$FORCE" = false ]; then
|
|
||||||
_placeholders=$(grep -n '{{[^}]*}}' "$PROJECT_FILE" 2>/dev/null || true)
|
|
||||||
if [ -n "$_placeholders" ]; then
|
|
||||||
echo "Error: unfilled placeholders in $PROJECT_FILE:" >&2
|
|
||||||
while IFS= read -r _match; do
|
|
||||||
_lineno="${_match%%:*}"
|
|
||||||
_content="${_match#*:}"
|
|
||||||
_token=$(printf '%s' "$_content" | grep -o '{{[^}]*}}' | head -1)
|
|
||||||
echo " $PROJECT_FILE:$_lineno: unfilled placeholder $_token" >&2
|
|
||||||
done <<< "$_placeholders"
|
|
||||||
echo "" >&2
|
|
||||||
echo "Fill these placeholders, then re-run: task context:sync" >&2
|
|
||||||
echo "To bypass validation: bash scripts/context-sync.sh --force" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$ROOT_CONTEXT" ] && [ -f "$ROOT_CONTEXT" ]; then
|
|
||||||
echo " Root context: $ROOT_CONTEXT"
|
|
||||||
else
|
|
||||||
echo " No root AGENT.md found (project context only)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Emit root context + separator
|
|
||||||
root_block() {
|
|
||||||
if [ -n "$ROOT_CONTEXT" ] && [ -f "$ROOT_CONTEXT" ]; then
|
|
||||||
cat "$ROOT_CONTEXT"
|
|
||||||
echo ""
|
|
||||||
echo "---"
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Claude Code ──────────────────────────────────────────────
|
|
||||||
# Claude Code walks up the tree — it finds ~/dev/CLAUDE.md automatically.
|
|
||||||
# Project-level CLAUDE.md only needs project-specific context.
|
|
||||||
generate_claude() {
|
|
||||||
cat "$PROJECT_FILE" > CLAUDE.md
|
|
||||||
echo " → CLAUDE.md (project-only; Claude Code inherits root)"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── AGENTS.md (Crush, Pi, Antigravity) ──────────────────────
|
|
||||||
# These tools read AGENTS.md from cwd but don't walk up.
|
|
||||||
# Concatenate root + project.
|
|
||||||
generate_agents() {
|
|
||||||
{ root_block; cat "$PROJECT_FILE"; } > AGENTS.md
|
|
||||||
echo " → AGENTS.md (root + project; Crush, Pi, Antigravity)"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Cursor ───────────────────────────────────────────────────
|
|
||||||
generate_cursor() {
|
|
||||||
{
|
|
||||||
echo "# Cursor rules — auto-generated"
|
|
||||||
echo "# Do not edit. Run: task context:sync"
|
|
||||||
echo ""
|
|
||||||
root_block
|
|
||||||
cat "$PROJECT_FILE"
|
|
||||||
} > .cursorrules
|
|
||||||
echo " → .cursorrules (root + project)"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Aider ────────────────────────────────────────────────────
|
|
||||||
generate_aider() {
|
|
||||||
{ root_block; cat "$PROJECT_FILE"; } > .aider.conventions.md
|
|
||||||
if [ ! -f .aider.conf.yml ]; then
|
|
||||||
cat > .aider.conf.yml << 'YAML'
|
|
||||||
read: .aider.conventions.md
|
|
||||||
auto-commits: false
|
|
||||||
YAML
|
|
||||||
fi
|
|
||||||
echo " → .aider.conventions.md (root + project)"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Generic system prompt (Open WebUI, Mods, etc.) ──────────
|
|
||||||
generate_system_prompt() {
|
|
||||||
{
|
|
||||||
echo "You are a coding assistant working on a specific project."
|
|
||||||
echo "Follow all conventions from both the root agent context and project context."
|
|
||||||
echo ""
|
|
||||||
echo "---"
|
|
||||||
echo ""
|
|
||||||
root_block
|
|
||||||
cat "$PROJECT_FILE"
|
|
||||||
echo ""
|
|
||||||
echo "---"
|
|
||||||
} > .context/system-prompt.txt
|
|
||||||
echo " → .context/system-prompt.txt (root + project)"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── MCP config ───────────────────────────────────────────────
|
|
||||||
generate_mcp() {
|
|
||||||
# Ensure baseline file exists with project-specific knowledge server
|
|
||||||
if [ ! -f .context/mcp.json ]; then
|
|
||||||
cat > .context/mcp.json << 'JSON'
|
|
||||||
{
|
|
||||||
"mcpServers": {
|
|
||||||
"knowledge": {
|
|
||||||
"url": "http://localhost:3100/mcp",
|
|
||||||
"description": "Project knowledge base — vector + graph retrieval"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JSON
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Merge root mcp-servers.json if found alongside root AGENT.md
|
|
||||||
local root_mcp=""
|
|
||||||
if [ -n "$ROOT_CONTEXT" ] && [ -f "$ROOT_CONTEXT" ]; then
|
|
||||||
local candidate
|
|
||||||
candidate="$(dirname "$ROOT_CONTEXT")/mcp-servers.json"
|
|
||||||
[ -f "$candidate" ] && root_mcp="$candidate"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$root_mcp" ]; then
|
|
||||||
echo " → .context/mcp.json (exists, no root mcp-servers.json found)"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Root servers take precedence over project entries on key conflict
|
|
||||||
local root_servers count updated
|
|
||||||
root_servers=$(jq '.servers' "$root_mcp")
|
|
||||||
count=$(printf '%s' "$root_servers" | jq 'keys | length')
|
|
||||||
updated=$(jq --argjson root "$root_servers" \
|
|
||||||
'.mcpServers = (.mcpServers + $root)' \
|
|
||||||
.context/mcp.json)
|
|
||||||
printf '%s\n' "$updated" > .context/mcp.json
|
|
||||||
echo " → .context/mcp.json (merged $count root servers)"
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Syncing project context from $PROJECT_FILE..."
|
|
||||||
|
|
||||||
if [ ${#ADAPTERS[@]} -eq 0 ]; then
|
|
||||||
generate_claude
|
|
||||||
generate_agents
|
|
||||||
generate_cursor
|
|
||||||
generate_aider
|
|
||||||
generate_system_prompt
|
|
||||||
generate_mcp
|
|
||||||
else
|
|
||||||
for adapter in "${ADAPTERS[@]}"; do
|
|
||||||
case "$adapter" in
|
|
||||||
claude) generate_claude ;;
|
|
||||||
agents) generate_agents ;;
|
|
||||||
cursor) generate_cursor ;;
|
|
||||||
aider) generate_aider ;;
|
|
||||||
prompt|system|openwebui|owui|generic) generate_system_prompt ;;
|
|
||||||
mcp) generate_mcp ;;
|
|
||||||
*) echo "Unknown adapter: $adapter" >&2; exit 1 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Done."
|
|
||||||
Executable
+121
@@ -0,0 +1,121 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Does a repo generated from this template pass its own gate?
|
||||||
|
#
|
||||||
|
# WHY THIS EXISTS
|
||||||
|
#
|
||||||
|
# `task check` on the template is green and has been for months, while children
|
||||||
|
# generated from it were red from their first commit (#1, #2). The template's
|
||||||
|
# own gate cannot see the defect, for a reason that is invisible unless you go
|
||||||
|
# looking:
|
||||||
|
#
|
||||||
|
# Go ignores directories whose names begin with "_" or "." when expanding
|
||||||
|
# package patterns like ./...
|
||||||
|
#
|
||||||
|
# The entrypoint lives in cmd/__PROJECT_NAME__, which begins with an underscore.
|
||||||
|
# So `go build ./...`, `go vet ./...`, `go test ./...` AND
|
||||||
|
# `golangci-lint run ./...` all silently skip the one package every generated
|
||||||
|
# repo is built around. Confirmed:
|
||||||
|
#
|
||||||
|
# $ go list ./...
|
||||||
|
# __MODULE_PATH__/internal/agent
|
||||||
|
# __MODULE_PATH__/pkg/litellm <- no cmd package
|
||||||
|
#
|
||||||
|
# After substitution the directory has a real name, becomes visible, and the
|
||||||
|
# defects appear in the child. Template green, children red, by construction.
|
||||||
|
#
|
||||||
|
# WHY NOT JUST LINT THE DIRECTORY DIRECTLY
|
||||||
|
#
|
||||||
|
# Tried: `golangci-lint run ./cmd/__PROJECT_NAME__` does work (naming a path
|
||||||
|
# directly bypasses the pattern exclusion). But it reports a gofumpt violation
|
||||||
|
# on the import block that does NOT exist in a generated repo -- the formatter's
|
||||||
|
# import grouping depends on the module path, and the template's is a
|
||||||
|
# placeholder rather than a real path. Wiring that into `check` would turn the
|
||||||
|
# template's gate red over an artifact of being a template.
|
||||||
|
#
|
||||||
|
# So this simulates the substitution and gates on the RESULT, which is the only
|
||||||
|
# artifact anyone actually runs. Same discipline as template-go-web's
|
||||||
|
# scripts/tests/is-template_test.sh: a test for transformation-sensitive code
|
||||||
|
# has to undergo the transformation.
|
||||||
|
#
|
||||||
|
# Usage: ./scripts/tests/generated-child_test.sh
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
|
||||||
|
pass=0; fail=0
|
||||||
|
ok() { printf ' ok %s\n' "$1"; pass=$((pass+1)); }
|
||||||
|
bad() { printf ' FAIL %s\n %s\n' "$1" "${2:-}"; fail=$((fail+1)); }
|
||||||
|
|
||||||
|
echo "generated-child tests"
|
||||||
|
|
||||||
|
# A missing tool is a FAILURE, not a skip. An `exit 0` with zero assertions is
|
||||||
|
# indistinguishable from a real pass to anything reading the exit code -- which
|
||||||
|
# is the whole failure mode this harness exists to close.
|
||||||
|
if ! command -v golangci-lint >/dev/null 2>&1; then
|
||||||
|
bad "golangci-lint is available" "not on PATH — the generated child cannot be gated"
|
||||||
|
echo; echo "$pass passed, $fail failed"; exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT
|
||||||
|
|
||||||
|
# Build the child exactly as a scaffolder does: the tracked tree, the entrypoint
|
||||||
|
# directory renamed, every placeholder substituted.
|
||||||
|
#
|
||||||
|
# Tracked files from the WORKING TREE, not `git archive HEAD`. This runs inside
|
||||||
|
# `task check`, so it has to gate what is about to be committed -- archiving
|
||||||
|
# HEAD tests the previous commit and reports stale results against a fix sitting
|
||||||
|
# unstaged, which it did on first use here. `git ls-files` keeps the file set
|
||||||
|
# honest (no build output, no local scratch) while taking current content.
|
||||||
|
( cd "$ROOT" && git ls-files -z ) | while IFS= read -r -d '' f; do
|
||||||
|
mkdir -p "$tmp/$(dirname "$f")"
|
||||||
|
cp "$ROOT/$f" "$tmp/$f"
|
||||||
|
done
|
||||||
|
if [ ! -d "$tmp/cmd/__PROJECT_NAME__" ]; then
|
||||||
|
bad "the template still has a placeholder entrypoint directory" \
|
||||||
|
"cmd/__PROJECT_NAME__ not found — has the layout changed?"
|
||||||
|
echo; echo "$pass passed, $fail failed"; exit 1
|
||||||
|
fi
|
||||||
|
mv "$tmp/cmd/__PROJECT_NAME__" "$tmp/cmd/childproj"
|
||||||
|
find "$tmp" -type f \( -name '*.go' -o -name '*.mod' -o -name '*.yml' -o -name '*.md' -o -name 'Dockerfile' \) \
|
||||||
|
-exec sed -i \
|
||||||
|
-e 's|__MODULE_PATH__|git.d-ma.be/mathias/childproj|g' \
|
||||||
|
-e 's|__PROJECT_NAME__|childproj|g' {} +
|
||||||
|
|
||||||
|
# The whole point: the entrypoint must be visible to Go tooling in the child.
|
||||||
|
# If this regresses, everything below it passes for the wrong reason.
|
||||||
|
if ( cd "$tmp" && go list ./... 2>/dev/null ) | grep -q '/cmd/childproj$'; then
|
||||||
|
ok "the generated child's entrypoint is visible to go list"
|
||||||
|
else
|
||||||
|
bad "the generated child's entrypoint is visible to go list" \
|
||||||
|
"cmd package missing from ./... — the child would be unchecked too"
|
||||||
|
fi
|
||||||
|
|
||||||
|
out="$(cd "$tmp" && go build ./... 2>&1)"
|
||||||
|
[ -z "$out" ] && ok "the generated child builds" \
|
||||||
|
|| bad "the generated child builds" "$(printf '%s' "$out" | head -3)"
|
||||||
|
|
||||||
|
out="$(cd "$tmp" && go vet ./... 2>&1)"
|
||||||
|
[ -z "$out" ] && ok "the generated child passes go vet" \
|
||||||
|
|| bad "the generated child passes go vet" "$(printf '%s' "$out" | head -3)"
|
||||||
|
|
||||||
|
# The one that caught #1/#2: errcheck on the deferred shutdown, in a package the
|
||||||
|
# template's own `golangci-lint run ./...` never analysed.
|
||||||
|
# Graded on the EXIT CODE, never on grepping the output. golangci-lint exits
|
||||||
|
# 0 clean / 1 issues found / >1 the linter itself failed. Matching its
|
||||||
|
# human-readable summary instead scores a CRASHED linter as a pass -- bad
|
||||||
|
# config, unwritable TMPDIR, OOM, or a future change to its output format all
|
||||||
|
# produce non-matching text. Since this harness is the only gate covering the
|
||||||
|
# entrypoint, that would leave it unchecked with CI green, which is precisely
|
||||||
|
# the defect class this file exists to end.
|
||||||
|
out="$(cd "$tmp" && golangci-lint run ./... 2>&1)"; rc=$?
|
||||||
|
case "$rc" in
|
||||||
|
0) ok "the generated child passes golangci-lint" ;;
|
||||||
|
1) bad "the generated child passes golangci-lint" \
|
||||||
|
"$(printf '%s' "$out" | grep -E '\.go:[0-9]+' | head -3)" ;;
|
||||||
|
*) bad "golangci-lint ran successfully" \
|
||||||
|
"linter exited $rc — the child is UNCHECKED, not clean: $(printf '%s' "$out" | tail -2)" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "$pass passed, $fail failed"
|
||||||
|
[ "$fail" -eq 0 ]
|
||||||
Reference in New Issue
Block a user