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

8.6 KiB
Raw Permalink Blame History

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 FORCEd 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.