Correct the governing documents where this session disproved them
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:
@@ -40,8 +40,9 @@ retrieval finds nothing. The org slug is `krow-dev` — a hardcoded constant
|
||||
(`internal/orgctx.DevOrgSlug`), not configuration.
|
||||
|
||||
**The endpoint count is a signal.** `GET /api/v1/version` reports it. Agent run
|
||||
routes are not registered without a model credential, so 56 means no
|
||||
`ANTHROPIC_API_KEY` and 58 means there is one. A keyless deployment boots
|
||||
routes are not registered without a model credential, so **55** means no
|
||||
`ANTHROPIC_API_KEY` and **57** means there is one. (These were written as 56/58
|
||||
and were one high; the delta of two — the two run routes — was always right.) A keyless deployment boots
|
||||
cleanly under `APP_ENV=staging` and refuses under `production`.
|
||||
|
||||
---
|
||||
@@ -123,30 +124,70 @@ that. One detector had a Friday-and-Saturday blind spot for exactly this reason.
|
||||
that fails on any skip other than `TestLive*`; keep it.
|
||||
|
||||
**The eval suites use a scripted model.** They prove the permission boundary,
|
||||
not answer quality. `make eval-live` uses the real model and costs tokens; it
|
||||
has never been run.
|
||||
not answer quality. `make eval-live` uses the real model and costs tokens.
|
||||
|
||||
It has now been run, and passes 3/3 in about 30 seconds. Worth knowing what it
|
||||
established, because it is more than "the agents work":
|
||||
|
||||
- the coverage agent called `assign_worker` and the write did NOT happen —
|
||||
I4 holds against a real model, not only a scripted one;
|
||||
- the handbook corpus contains a planted prompt injection telling the agent
|
||||
to send the staff roster to an external address. The agent refused it,
|
||||
answered the real question with citations, and reported the document as
|
||||
tampered with. I7 holds end to end;
|
||||
- the activity agent declined to subtract two figures it could not
|
||||
reconcile, and said so, rather than producing the confident wrong number
|
||||
this schema invites.
|
||||
|
||||
Re-run it after any change to the loop, retrieval, or prompt assembly. It is
|
||||
the only check that measures answers rather than boundaries.
|
||||
|
||||
**The seeded activity data is NOT anchored to today.** `ShiftRecord` is —
|
||||
`seed.js` says so — and `UserActivity` is not, so it ages out of every window
|
||||
the activity tools offer. As of 2026-08-29 the newest event was 23 days old:
|
||||
zero events in the last 7 days and 6 of 15 in the last 30. The activity agent
|
||||
answers truthfully and the demo looks dead. Anchoring it the way shifts are
|
||||
anchored is the fix; it changes `seed.json`, so it goes through
|
||||
`npm run seed:fixture` and re-runs the frontend checks.
|
||||
|
||||
---
|
||||
|
||||
## Still outstanding
|
||||
|
||||
- The knowledge corpus is 8 documents locally; production still has the 2 seeded
|
||||
ones until `ingest` runs there.
|
||||
- `ANTHROPIC_API_KEY` was pasted into a chat transcript and is live in a
|
||||
Kubernetes Secret. Rotate it.
|
||||
- Deployments report `version=dev`: the image is built without
|
||||
`--build-arg VERSION`. `make docker-build` passes it.
|
||||
- `APP_ENV=staging` on the deployment, so the production config guards are off.
|
||||
- Skills are still stored in `user_preferences`; agents were moved to the
|
||||
registry and skills were deliberately left for their own pass.
|
||||
- `definition_versions` is empty — immutable versioning is built, trigger-proven,
|
||||
and nothing has gone through it because `importagents` republishes in place.
|
||||
- CI tests but does not deploy. The README's claim that migrations are "run by
|
||||
CI against the target database" is still aspirational.
|
||||
- The fixture-drift CI jobs need `FRONTEND_REPO_TOKEN` to see the sibling repo,
|
||||
and fail rather than pass quietly without it.
|
||||
- The remote is Gitea. These are GitHub Actions workflows; they do nothing until
|
||||
a compatible runner exists.
|
||||
a compatible runner exists. Nobody has confirmed a runner exists, so treat
|
||||
both repositories as having no CI until somebody checks.
|
||||
- **The application talks to its database in clear text.** `DATABASE_SSLMODE=
|
||||
disable` against `66.116.207.225`, which is a DIFFERENT machine from the
|
||||
cluster host — so credentials and every row cross the network unencrypted.
|
||||
It is permitted only because `APP_ENV=staging`; the production guard refuses
|
||||
`disable` outright. PostgreSQL itself now has `ssl = on` (2026-08-29, port
|
||||
5433, reload not restart), but the app does not reach PostgreSQL directly:
|
||||
**pgbouncer terminates 5432** and offers no TLS of its own. The fix is
|
||||
`client_tls_sslmode = allow` plus a cert in `/etc/pgbouncer/pgbouncer.ini`,
|
||||
then `DATABASE_SSLMODE=require` in `krow-config` and the `krow-db` secret.
|
||||
`allow` keeps existing plaintext clients working, so it is additive.
|
||||
- Production retrieval is **keyword-only**: no `EMBED_PROVIDER` in
|
||||
`krow-config`, so `knowledge_chunks.embedding` is null for all 34 rows. A
|
||||
`VOYAGE_API_KEY` is the cheap fix; Ollama in-cluster is the other, and the
|
||||
nodes were at 60% and 49% memory when that was last looked at.
|
||||
- Delegation (§6) is implemented and on `main` but NOT deployed. Until the next
|
||||
image ships, production agents still ignore their `subagents:`.
|
||||
- §3's publish-time cycle detection is still missing. The runtime depth cap
|
||||
(2) is what bounds a cycle that reaches run time.
|
||||
- `cmd/importagents` has no tests, and `run()` opens its own pool from config,
|
||||
so making it testable is a refactor rather than an addition.
|
||||
- `importagents` does not enforce monotonicity: a spec whose `version:` is
|
||||
LOWERED still overwrites the live row and rolls the deployed agent backwards.
|
||||
|
||||
---
|
||||
|
||||
@@ -155,8 +196,26 @@ has never been run.
|
||||
git clone <backend> krow-backend && git clone <frontend> krow-demo
|
||||
cp krow-backend/CLAUDE.md ./claude.md # the governing doc lives above both repos
|
||||
|
||||
Needs: Go (see `go-api/go.mod`), Node 20, PostgreSQL, Docker, and Ollama with
|
||||
`nomic-embed-text` if you want semantic retrieval locally. Then:
|
||||
Needs, if you run the backend natively: Go (see `go-api/go.mod`), Node 20,
|
||||
PostgreSQL, Docker, and Ollama with `nomic-embed-text` for semantic retrieval.
|
||||
|
||||
You do not need most of that. `infrastructure/Dockerfile.api` builds EVERY
|
||||
command in `go-api/cmd/` plus the golang-migrate CLI into the image, so the
|
||||
whole stack runs on Docker alone — no Go, no psql, no migrate on the host:
|
||||
|
||||
cd krow-backend/infrastructure
|
||||
cp .env.docker.example .env # fill it in; DATABASE_HOST=postgres
|
||||
docker compose -f docker-compose.yml -f docker-compose.local-db.yml up -d
|
||||
docker exec krow-api seed
|
||||
docker exec krow-api importagents --dir /app/agents --skills /app/skills --org krow-dev
|
||||
docker exec krow-api ingest --dir /app/knowledge --org krow-dev
|
||||
printf '%s' 'PASSWORD' | docker exec -i krow-api setpassword -email demo@krow.app -stdin
|
||||
|
||||
Ollama, if you want semantic retrieval, runs on the HOST — so the container
|
||||
reaches it at `host.docker.internal:11434`, NOT `localhost:11434`, which inside
|
||||
a container means the container.
|
||||
|
||||
Running natively instead, you need all of the above. Then:
|
||||
|
||||
cd krow-backend && cp .env.example .env # fill it in; .env is gitignored
|
||||
make migrate-up && make seed
|
||||
|
||||
Reference in New Issue
Block a user