Correct the governing documents where this session disproved them
Some checks failed
CI / test (push) Has been cancelled
CI / fixture (push) Has been cancelled

Both documents are the first thing a new reader trusts, and several of their
claims were wrong — some wrong from the start, some overtaken by work this
week. A governing document that misdescribes the system is worse than none,
because it is believed.

CLAUDE.md §10 specified Python 3.11, FastAPI, SQLAlchemy and Alembic. The code
is Go and has never been anything else. That is corrected rather than quietly
deleted, so the next person understands the document drifted rather than
wondering which half to trust.

Also in CLAUDE.md: the tool count was 17 with one write and is 19 with two;
delegation now exists and §11's Orchestration row says what it guarantees; the
embedder deviation described a Voyage-or-stand-in choice that has since become
EMBED_PROVIDER with three options, of which production sets none.

The handover claimed three things that this session disproved by running them:

  - "definition_versions is empty ... nothing has gone through it". It was not
    empty in production; activity-agent had a v1 that the shipped file
    contradicted, which is how a real drift was found. It now holds every
    agent and skill.
  - "Skills are still stored in user_preferences". They are rows in
    skill_definitions, and are now versioned.
  - "make eval-live ... has never been run". It has, it passes 3/3, and what
    it established is recorded — including that the handbook corpus carries a
    planted prompt injection which the agent refused and reported. That is I7
    holding against a real model, which is worth more than the pass count.

The endpoint counts were one high throughout (55/57, not 56/58) — the delta of
two was always right, so the signal worked and the absolute numbers did not.

Added, because they cost time this week and would cost it again:

  - the app reaches its database through pgbouncer, not PostgreSQL directly.
    Enabling TLS on PostgreSQL does nothing for the application hop; pgbouncer
    terminates 5432 and needs its own client_tls_sslmode.
  - the seeded UserActivity is NOT anchored to today the way ShiftRecord is,
    so it ages out of every window the activity tools offer. Twenty-three days
    old as of writing: zero events in the last 7 days, 6 of 15 in the last 30.
    The agent answers truthfully and the demo looks dead.
  - the whole stack runs on Docker alone. Dockerfile.api builds every command
    plus the migrate CLI, so a new machine needs neither Go nor psql — which
    is how this one was set up, having no Homebrew.
  - Ollama runs on the HOST, so a container reaches it at
    host.docker.internal, not localhost. The old .env said localhost and would
    have failed with nothing obviously wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJvibeSc1JYXjatankqM1g
This commit is contained in:
2026-08-29 12:10:41 +05:30
parent 6849363a37
commit 4c29185c3b
2 changed files with 86 additions and 20 deletions

View File

@@ -188,12 +188,16 @@ Each eval case:
## 10. Conventions
- Python 3.11+, FastAPI, async throughout. SQLAlchemy 2.0 style.
- Type hints on every public function. `mypy --strict` on `src/registry/` and `src/runtime/`.
- **Go** (see `go-api/go.mod`), standard library HTTP with `net/http` routing
patterns, `pgx` for PostgreSQL. NOT Python: this document specified
Python 3.11 / FastAPI / SQLAlchemy / Alembic and the code has never been any
of those. Corrected here rather than left to mislead the next reader, which
it did.
- Exported functions carry doc comments. `go vet ./...` clean; `gofmt -w`.
- Errors: structured exception types with a `code`, never bare strings. User-facing text is derived at the surface layer, not raised from the core.
- Logging: structured JSON, always include `run_id`, `tenant_id`, `agent_key`, `agent_version`. Never log message content or retrieved chunks at INFO — that is a data leak into your log store. DEBUG only, behind a per-tenant flag.
- Config via environment, validated once at startup into a frozen settings object. No `os.getenv` at call sites.
- Migrations: Alembic, one per PR, reversible.
- Migrations: golang-migrate, one per PR, reversible (`.up.sql` and `.down.sql`).
---
@@ -216,9 +220,9 @@ depends on the curated-versus-self-serve decision and is not settled.
| Layer | State |
|---|---|
| Surfaces | `POST /api/v1/agents/{id}/runs` (streams over SSE on `Accept: text/event-stream`), `GET /api/v1/runs/{id}`; the chat panel is the only answering path — the browser simulator is deleted |
| Orchestration | spec-driven loop, four bounds claimed before dispatch, six terminations, trajectories in `agent_runs` |
| Orchestration | spec-driven loop, four bounds claimed before dispatch, six terminations, trajectories in `agent_runs`; delegation per §6 — a subagent is a tool call, runs as the caller, shares the parent budget, capped at depth 2, and writes its own trajectory linked by `parent_run_id` |
| Registry | 9 agents + 23 skills as rows; published versions immutable (append-only, trigger-enforced); runs pin the version they started with |
| Tools | 17, one of which writes, behind a bound single-use confirmation |
| Tools | 19, two of which write (`move_application`, `assign_worker`), behind a bound single-use confirmation |
| Knowledge | ACL-tagged ingest, hybrid dense + BM25 fused with RRF, pre-filtered |
| Gateway | tier → model + effort, token accounting, refusal as an outcome |
@@ -233,8 +237,11 @@ depends on the curated-versus-self-serve decision and is not settled.
- Vectors are `real[]` with a dot-product function rather than pgvector, which
is not installed. Exact search, no ANN index, bounded by the ACL pre-filter.
The upgrade is a column type change and no logic change.
- Dense retrieval runs on a deterministic stand-in embedder until a Voyage key
exists. It is **not semantic** and refuses to run in production.
- Dense retrieval takes its embedder from `EMBED_PROVIDER`: `ollama` (local,
real semantics, no credential), `voyage` (hosted), or `lexical` — a
deterministic stand-in that is **not semantic** and that config validation
refuses in production. Unset means keyword-only, which is what production
runs today.
---