Files
mathiasandClaude Opus 4.8 b79fb892c8
CI / Lint / Test / Vet (pull_request) Successful in 13s
CI / Build & Import (pull_request) Has been skipped
docs(claude): point to LANGUAGE.md + caveman rubric
Wires the Phase 1 vocabulary pilot into the canonical agent instructions
(tapir#18). CLAUDE.md is canonical here (no .context/ source, no generation
header).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 22:54:57 +02:00

133 lines
8.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md — Agent operating instructions for Tapir
Read this first if you are an agent (or human) starting a work session in this repo.
It tells you how to work here. For *what* and *why*, read `README.md` and the guardrail
docs it indexes.
## Orientation order
1. `README.md` — what Tapir is, links to all guardrails.
2. `VISION.md` — the staged Definition of Success. **Stage 0 ("useful to me") is the gate.**
Do not build Stage 1+ machinery before Stage 0 holds.
3. `DECISIONS.md` — the ADRs. Decisions are settled here; do not re-litigate without a new ADR.
4. `docs/architecture/architecture.md`, `docs/data-model.md`, `docs/use-cases/*.feature`.
5. `docs/homelab-integration.md` — the concrete endpoints/conventions you'll need.
6. `LANGUAGE.md` — the project vocabulary. Apply the caveman rubric before destructive operations.
## How to work in this repo
- **Trunk-Based Development (ADR-009).** Commit directly to `main`. One logical change per
commit. Every commit deployable. No feature branches or PRs for solo/agent work — the only
exception is a short-lived `agent/<desc>` branch when another agent is *simultaneously*
active on this repo, merged within the same session.
- **Run the quality gate before every push.** `task check`. CI is the gate, not branch
protection — do not enable branch protection on this repo.
- **Conventional commits.** `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`. Subject line says
what; body says why.
- **Language is Go (ADR-001).** Do not introduce Python or a second language. If you think you
need one, that's a new ADR with a real justification, not a default.
## Things that look reusable but are NOT — read before "reusing"
These caused real mistakes that were caught and corrected; the corrections are load-bearing.
- **The `llm` package is COPIED from `hyperguild/ingestion`, not imported (ADR-004).** Tapir
owes that repo nothing at the dependency level. Do not add `hyperguild/ingestion` as a Go
module dependency to "share" code. If the copied `llm` needs changes, change Tapir's copy.
- **The brain sink is HTTP to brain-mcp, NOT the filesystem `brain` package (ADR-005).**
`hyperguild/ingestion`'s `internal/brain` writes files into a brain git checkout on disk.
That is the wrong model for Tapir. The brain sink calls brain-mcp's `brain_ingest` tool over
HTTP. Do not copy or replicate the filesystem brain package.
- **YouTube/Vimeo OAuth is written fresh (ADR-006).** `hyperguild/ingestion`'s `internal/oauth`
is the MCP *server's inbound* auth (client_credentials). It has nothing to do with *outbound*
OAuth to video providers despite the shared name. Use `golang.org/x/oauth2`.
## Settled decisions you should not "helpfully" reopen
(See `DECISIONS.md` for full rationale. Listed here so you don't propose them.)
- **No Supabase** — reuse Dex / ESO+1Password / Postgres (ADR-002).
- **No global cross-tenant *video* table** — videos stay per-user (data-model). Transcripts ARE
shared since ADR-021 (public caption content, keyed by `(provider, provider_video_id)`, non-RLS)
so re-analysis never re-fetches; the *videos* half of cross-tenant dedup stays a Future C concern.
- **No audio-download + speech-to-text in the core path** — captions-first (ADR-007). STT is a
deferred, bounded optional component.
- **No public SaaS / sign-up / billing / Google OAuth verification at scale** — Future C,
deferred behind the Stage 0 gate (ADR-008).
## Architecture stance for new code
- **Clean Architecture, dependencies point inward.** The engine (use cases) depends only on the
ports (`VideoSource`, `Summarizer`, `Sink`, `SecretStore`). Concrete providers, the AI router,
stores, and sinks are adapters. Adding a video provider or a sink = a new adapter implementing
the interface, nothing in the engine changes. This is what keeps "standalone vs homelab" a
wiring choice (ADR-003).
- **BDD.** The `docs/use-cases/*.feature` files are the behavior spec (design records — there is
no godog runner). New behavior gets a scenario; the use-case core is tested through fake
adapters, not live YouTube/brain. A name-coverage gate (`test/acceptance/scenario_coverage_test.go`,
`TestScenarioCoverage`) keeps the two from drifting: every non-`@pending` scenario must be
mapped to an existing Go test in `scenarioCoverage`. When you add a scenario, either map it to
its covering test or tag it `@pending` in the `.feature` with a one-line reason. It checks the
*link*, not that the test exercises the scenario — that's the deliberate trade for not running
godog (see issue #5 / the BDD-runner decision).
## Skills (engineering discipline)
Skills live in the canonical library `mathias/skills` and are wired into this repo as
**gitignored symlinks** — do not commit them, do not add a skills manifest to this repo.
- **Install/refresh:** `task skills` (or `curl -fsSL https://gitea.d-ma.be/mathias/skills/raw/branch/main/install.sh | bash`). Idempotent.
- **Most relevant skills for Tapir work:**
- `tdd`, `atdd`, `test-design` — the build is test-first; the `.feature` files are the ATDD spec.
- `clean-code`, `solid` — the ports-and-adapters structure depends on these.
- `gitea-ci` — for the CI workflow (and its act_runner gotchas) and the TBD section.
- `debug` — when `task check` goes red for a non-obvious reason.
- Reviewer-side: `code-review`, `refactoring`, `cognitive-load`.
- Session close: `session-retrospective` (surface learnings into brain before context is lost).
## Current build state (start here for the first task)
The repo is **green and shipping** — last tag `v0.15.0`. `task check` passes (fmt, vet, lint,
`go test -p 1 ./...`). Go is `1.26.1` (see `go.mod`).
- Clean Architecture core is implemented: `internal/domain` (entities), `internal/ports`
(interfaces), `internal/usecase.Engine.ProcessNewVideo` (resolve transcript → summarize →
deliver to sinks | skip on no-transcript). The acceptance tests in `test/acceptance/` are
green against it.
- Adapters present under `internal/adapters/`: `youtube` (captions-first `VideoSource`,
timedtext/InnerTube acquisition per ADR-010), `summarizer` + `llm` (the copied AI router,
now a resilient endpoint chain — local primary → local fallback → external worst-case,
parse-failure-aware, ADR-004 + ADR-022), `store` (Postgres, golang-migrate migrations 001015),
`secrets` (file-backed `SecretStore`). The brain HTTP sink (ADR-005) is the remaining
optional sink.
- Stage 1 is open (ADR-012): multi-user with **DB-enforced** isolation — Postgres RLS `FORCE`d
on all user-owned tables (migration 003), two-user isolation test in
`internal/adapters/store/rls_test.go`. Registration gate, per-user YouTube web connect, and
account management (disconnect / delete, ADR-013) all shipped.
- Transcript persistence (ADR-021, migration 015): transcripts are a **shared, non-RLS** store
keyed by `(provider, provider_video_id)` — the single exception to the isolation boundary
(`TestTranscriptsTableIsSharedNotRLS`). The engine reads stored transcripts before any caption
fetch (`usecase.resolveTranscript`), so re-analysis — re-summarize, paste-a-URL, onboarding
burst — never re-touches YouTube. Per-user summaries/videos stay RLS-scoped.
- `cmd/tapir` subcommands: `list`, `show`, `auth` (interactive host-side OAuth), `run` (batch
watch→summarize), `serve` (the HTMX+Templ web reader/writer under `internal/web`, a new
transport over the unchanged engine/ports — ADR-003). `tapir env` prints config.
- **Build/run:** `task check` is the gate; `task build` produces the binary. Local dev uses
`StubAuth` (allow-all) and a `TAPIR_DB_DSN` Postgres; the deployed service uses Dex OIDC.
**Setup facts** (resolved — see `docs/homelab-integration.md` for the live values): LiteLLM is
off-cluster at `koala:30401/v1/` with `LITELLM_MASTER_KEY` from 1Password; the summarization
model is config (`TAPIR_SUMMARIZER_MODEL`, default `koala/phi4-mini`), never hardcoded. The
brain-mcp base URL and ESO ref scheme are pinned in that doc; check it before wiring rather than
re-deriving.
## Provenance (where this design came from)
- The reuse decisions came from **Spike S5**, recorded at
`infra/docs/superpowers/handoffs/2026-06-02-video-adapter-placement.md` (read it for the
per-package lift-vs-copy analysis).
- The `llm` package source is `hyperguild/ingestion/internal/llm` (`Client` + `Router`).
- The standalone-first framing, the Supabase/Python rejections, and the staged success
definition came from a planning + grill session on 2026-06-02 (claude.ai). The conclusions are
in `VISION.md` and `DECISIONS.md`; this file is the operational distillation.