Knowledge Harvest Loop — Overnight Implementation Manual¶
You are a fresh Claude Code session with no memory of the design conversation. This manual is self-contained. Read the Prime Directive and Environment sections fully before doing anything. The architecture you are implementing is specified in the companion file:
specs/brain/2026-06-20-knowledge-harvest-loop.md— read it first.
0. PRIME DIRECTIVE — read before acting¶
You are implementing the Knowledge Harvest Loop unattended, overnight. The owner (João) is asleep and cannot answer questions. Therefore:
You MAY do autonomously (safe, additive, reversible, no real cost)¶
- Read code, DB schema, configs.
- Create new files: gateway skills, migration SQL, admin templates, tests.
- Apply additive DB changes only (
CREATE TABLE IF NOT EXISTS,ADD COLUMN IF NOT EXISTS) with a written reversible down script. - Restart
jb-skills-gatewayto load new skills (verify healthy afterwards). - Run tests in
dry_runmode only (no real web research, no paid LLM batches; trivial single LLM calls for a unit test are acceptable). - Commit work to a new feature branch in each affected repo.
You MUST NOT do (HARD STOP — leave for João's morning approval)¶
- Enable any cron / scheduler.
- Set or raise any budget value.
- Run the first real (paid) harvest (real Deeper Research run that spends money).
- Send any Telegram / email / public message (test
notify.pushin dry-run only). - Deploy the admin UI tab to the live rabbithall hub (write the files only).
- Merge to
main/ push tomain/ promote to production. Push feature branches only. - Any destructive op:
DROP,DELETE,TRUNCATE, overwriting existing skills/files, force-recreate of shared containers.
Operating rules¶
- Plan fully, apply once. Never trial-and-error on production containers. Reason the whole change, then make it in one pass, then verify.
- Verify after every phase. If a verification fails, STOP that phase, record it in the morning report, and only continue with later phases that are independent and safe. Do not cascade on a failure.
- No silent caps. If you skip or truncate anything, write it in the report.
- Keep all generated UI light theme only (the owner is colorblind — dark backgrounds are unusable). Copy the CSS variables from
/opt/rabbithall/templates/admin_dashboard.html. - Work additive: never edit
tcn.ingest-articleor other live skills in place — add new code paths behind flags.
1. Environment (facts you need; you have no memory)¶
- VPS access:
ssh vps(root). Primary host76.13.44.83. - Gateway: container
jb-skills-gateway, uvicorn--workers 3. Skills live in/opt/jb-skills-gateway/skills/<namespace>/<skill>/main.py(+ empty__init__.py), bind-mounted (host edits are live). Internal base URLhttp://localhost:8095. To load new/changed skills:docker restart jb-skills-gateway(NOT/reload). A skill needsSKILL_META = {...}anddef run(payload: dict)or it fails silently with no log. - DB:
docker exec infra-postgres psql -U litellm -d down_rabbit_hole. Relevant tables:taxonomy_terms,search_topology(last_seen= last search, ordered ASC for stale-first),discovery_queue(content candidates, domain-agnostic),nodes(embedding_768,summary,main_takeaways[],metadata jsonb),brain_staging. Apache AGE graph:down_rabbit_hole_graph(Entities, MENTIONS, LINKS_TO). - AI: ALL via the LiteLLM proxy — never call providers directly. Chat tiers include
brain-summarise,brain-classify. Embeddings:gemini/gemini-embedding-001, 768-dim, via LiteLLM/embeddings. - Existing reusable skills:
tcn.six-eyes-scorer(POST/skills/tcn.six-eyes-scorer/run; thresholds<3discard /3–6quarantine /≥6auto-ingest),tcn.deep-research(POSTs todr_orchestrator:8200),brain.content-curator(ingest + cosineMAX(1 - (embedding_768 <=> v))),brain.ontology-enricher(extract entities → propose new terms),notify.push(category/severity; onlyaction+critical→ Telegram). - Admin UI: rabbithall, hub at
/admin/curation. CRITICAL deploy path: the real compose is/opt/rabbithall-compose/(NOT/opt/rabbithall/docker-compose.yml— that one is obsolete and using it takes the hub down). Templates+static are bind-mounted; app.py is baked (deploy viarh-deploy). For THIS overnight run you only write the template/route into the repo — you do not deploy. - Git/Forgejo: SSH remote
ssh://git@git.joaoluisbrazao.cloud:2222/joaobrazao/<repo>.git. Repos:jb-skills-gateway,rabbithall(confirm working-tree location on VPS, likely/opt/jb-skills-gatewayand/opt/rabbithall). Branch naming:feature/harvest-loop. - Tasks: Vikunja DEV
project_id=4. Insert viadocker exec infra-postgres psql -U litellm -d vikunja -c "INSERT INTO tasks (title,description,project_id,index,repeat_mode,created,updated,created_by_id) VALUES (...)",created_by_id=1,index= current max+1. - Shell gotcha: the remote command runner rejects backslashes/newlines in some cases — prefer single-line commands without
\escapes (e.g. unescaped dots insed/greppatterns).
2. Phases (execute in order; verify each)¶
Phase 0 — Preflight (read-only) + branch¶
- Read the spec
specs/brain/2026-06-20-knowledge-harvest-loop.md. ssh vpsand confirm:jb-skills-gatewayhealthy;infra-postgresreachable; tablestaxonomy_terms,search_topology,discovery_queue,nodesexist; LiteLLM reachable (/embeddingsreturns 200 for a trivial input).- In the
jb-skills-gatewayworking tree on the VPS:git checkout -b feature/harvest-loop(do NOT touchmain). Verify: branch created; all preflight checks green. If any fails → STOP, report.
Phase 1 — DB schema (additive, reversible)¶
Write migrations/harvest_001_up.sql and harvest_001_down.sql:
- CREATE TABLE IF NOT EXISTS harvest_targets ( id uuid PK default gen_random_uuid(), term text, topology text, domain varchar(100), destination jsonb, status varchar(20) default 'pending', depth int default 0, parent_target_id uuid, priority int default 100, enqueued_at timestamptz default now(), started_at timestamptz, finished_at timestamptz, tokens_spent int default 0, result_summary text, created_at timestamptz default now(), updated_at timestamptz default now(), deleted_at timestamptz ); partial unique index on (term, domain) where status in ('pending','researching').
- ALTER TABLE search_topology ADD COLUMN IF NOT EXISTS last_novelty_at timestamptz, ADD COLUMN IF NOT EXISTS check_interval int.
- ALTER TABLE nodes ADD COLUMN IF NOT EXISTS new_events jsonb, ADD COLUMN IF NOT EXISTS potential_consequences jsonb, ADD COLUMN IF NOT EXISTS connections jsonb (or store these in existing metadata jsonb — pick one and be consistent; columns preferred for queryability).
Apply the up to down_rabbit_hole (additive only).
Verify: \d harvest_targets shows the table; columns added. down script written and reviewed (do not run it).
Phase 2 — harvest.enqueue-term (public API)¶
Create /opt/jb-skills-gateway/skills/harvest/enqueue-term/{__init__.py,main.py}. SKILL_META name harvest.enqueue-term. Accept {term, topology, domain, destination, depth?, parent_target_id?}. Dedup against harvest_targets (pending/researching) and skip if the term already exists in taxonomy_terms for P1. Insert a pending row. docker restart jb-skills-gateway.
Verify: call the skill with a test payload (dry_run:true inserts nothing, just validates); confirm it is registered and a non-dry call inserts exactly one row and dedups on a second identical call. Clean up the test row.
Phase 3 — harvest.worker (paced executor, NIGHT = dry)¶
Create harvest/worker/. Logic: check budget (Phase 4) → pull up to K pending targets ordered by priority, depth ASC, enqueued_at → for each, call Deeper Research → normalize → distill (Phase 6) → novelty gate (Phase 5) → Six Eyes → route (<3 discard / 3–6 quarantine / ≥6 ingest) → update search_topology.last_seen/last_novelty_at + recompute cadence (Phase 7). Implement a dry_run mode that does NOT call paid Deeper Research — it must exercise the control flow with a mocked/sample document. Do NOT register any cron.
Verify: dry_run end-to-end on a sample target completes and routes correctly with zero real spend.
Phase 4 — harvest.budget-governor¶
Create harvest/budget-governor/. Track daily/monthly token/cost spend (lift the accounting pattern from discover.brain-discovery-orchestrator). On cap-reached, build the three-option decision payload and call notify.push in dry-run (do NOT actually send tonight). Budget values left unset (gated — João sets them).
Verify: unit test of the cap logic; confirms it would emit action+critical at the cap; confirms it returns "paused" instead of proceeding.
Phase 5 — Semantic novelty gate + delta extraction¶
Implement as a function used by the worker: embed candidate (gemini-768 via LiteLLM) → k-NN cosine against nodes tagged with the term → if max cosine ≥ redundancy_threshold (start 0.92, tunable) → discard; else brain-classify extracts only the net-new fact (delta) as Markdown.
Verify: unit test with two near-duplicate texts (one redundant, one with a new fact) → redundant discarded, novel delta extracted.
Phase 6 — Intelligence Object distillation (additive)¶
Add a distill() function producing the Markdown body + structured fields (executive_summary, new_events[], main_takeaways[], potential_consequences[], entities[], connections[], provenance {source_url, published_at, author}). Policy: do NOT persist raw body — store distilled Markdown + provenance only. Do not modify tcn.ingest-article in place; add the function and wire it for the harvest path only.
Verify: distill a sample article → object has all fields, no raw body, provenance present.
Phase 7 — Adaptive cadence (pure function)¶
Implement next_interval(prior_interval, found_novelty, category): bounded [floor, ceiling]; category prior seeds initial; found → max(floor, interval/2), none → min(ceiling, interval*1.5). Not ML.
Verify: unit tests for the Enzo-Ferrari (backs off to ceiling) and Ferrari-F1-team (stays at floor) cases.
Phase 8 — Connections → AGE + bounded enqueue¶
In the ingest path, for each connection: check existence in down_rabbit_hole_graph; exists → create LINKS_TO edge; not exists → harvest.enqueue-term with depth = parent.depth + 1, hard cap 3.
Verify: unit test: known entity → edge; unknown → enqueued at depth+1; depth 3 → not enqueued.
Phase 9 — Admin UI tab (WRITE ONLY, do not deploy)¶
Write /opt/rabbithall/templates/.../curation_harvest.html (light theme, CSS vars from admin_dashboard.html) + the route in app.py, listing (a) pending harvest_targets, (b) quarantine (Six Eyes 4–6) with source URLs. Commit to the rabbithall feature/harvest-loop branch. Do NOT rh-deploy, do NOT restart rabbithall.
Verify: template + route present in the branch; no deploy performed.
Phase 10 — Commit + report¶
Commit all work to feature/harvest-loop in each repo (gateway + rabbithall). Push the feature branch (not main). Write the morning report (below) to specs/brain/2026-06-20-harvest-OVERNIGHT-REPORT.md in jb-workspace and push.
3. GATED steps (morning, with João — do NOT do tonight)¶
- Apply any non-additive schema change.
- Set budget values in
harvest.budget-governor. - Register the crons (worker cadence).
- Run the first real (paid) harvest.
- Deploy the
/admin/curation/harvesttab to the live hub (rh-deploy). - Merge
feature/harvest-loop→mainafter review.
4. Morning report template (fill and push)¶
# Harvest Loop — Overnight Report (2026-06-21)
## Done (per phase): 0 …10 — status, what was created, verification result
## Failed / skipped: phase, reason, what is needed
## Branch: feature/harvest-loop @ <sha> in <repos>
## DB: additive migration applied? (table/columns) + down script path
## GATED — awaiting João: budget values, cron schedule, first harvest, admin deploy, merge to main
## Cost incurred tonight: <should be ~0; list any LLM calls>
## Recommended next action for João
5. If you get stuck¶
If a phase is ambiguous or a verification fails and you cannot resolve it safely, STOP that phase, record the exact blocker in the report, and move to the next independent phase. Never guess on a production-affecting action. Leaving safe, partial, well-documented progress is the correct outcome — finishing everything is not required.