Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f509e5f34 | |||
| e7acab2c56 |
@@ -0,0 +1,167 @@
|
||||
# Claude Code Handoff — Team-Memory **Dynamic Graph** (redesign + deploy reconciliation)
|
||||
|
||||
> **Fresh-session handoff.** Read this top-to-bottom before touching the Team-memory graph.
|
||||
> It captures the dynamic-graph redesign, the honest-metrics fix, the click-to-explain "Flow"
|
||||
> pane, and — most importantly — how it was **reconciled against `main`'s parallel
|
||||
> implementation** so it can actually deploy. Author: Claude Code (Opus 4.8) session, 2026-06-28.
|
||||
|
||||
---
|
||||
|
||||
## 0. TL;DR — what to do next
|
||||
|
||||
1. **Merge [PR #38](https://github.com/karti-ai/podman/pull/38) → `main`.** It is `MERGEABLE` (no conflicts) and is the deploy path. Deploy = push to `main` (`deploy_on_push`).
|
||||
2. **After deploy, hard-refresh** (Cmd-Shift-R) or use a private window — the PWA service worker caches aggressively, so you'll think nothing changed.
|
||||
3. **Close [PR #22](https://github.com/karti-ai/podman/pull/22)** (the older one into `feat/live-graph-glue`) with a note pointing at #38; it is superseded for the deploy path.
|
||||
4. Optionally delete the stale branches `feat/live-graph-glue` and `feat/team-memory-dynamic-graph` once #38 lands.
|
||||
|
||||
**State right now:** the live site (`165-22-129-249.sslip.io` / `podman.live`) runs `main`, which has a **static** graph with **inflated metrics** and **does not render the learning-loop / activity rails** (even though its backend computes them). PR #38 fixes all three.
|
||||
|
||||
---
|
||||
|
||||
## 1. Branches, PRs, deploy
|
||||
|
||||
| Branch | What's on it | Status |
|
||||
| --- | --- | --- |
|
||||
| `main` | Trunk. Has a **parallel** Team-memory impl: computes `loop`/`activity` in the API (rich types) but **static** graph, **inflated** metrics, rails **never rendered**. Deployed. | live |
|
||||
| `feat/team-memory-deploy` | **The reconciliation.** Dynamic graph + honest metrics + Flow pane, on top of `main`, rails adapted to `main`'s types. | **PR #38 → `main`, MERGEABLE** |
|
||||
| `feat/live-graph-glue` | Where the original redesign (v1) was merged (PR #22). Branched ~100 commits before `main`'s later work; a direct merge to `main` was unsafe/tangled. | superseded by #38 |
|
||||
| `feat/team-memory-dynamic-graph` | v1 PR branch (merged into glue via #22). | superseded |
|
||||
|
||||
- **Deploy = merge/push to `main`.** Do **not** push to `main` directly; merge the PR. `main` is shared by ~4 engineers and moves fast.
|
||||
- Live API to sanity-check: `curl https://165-22-129-249.sslip.io/api/pods/demo-pod/graph`.
|
||||
|
||||
---
|
||||
|
||||
## 2. The divergence (read this — it's the crux)
|
||||
|
||||
While the dynamic-graph redesign was being built on `feat/live-graph-glue`, **another engineer shipped a *parallel* version of the same feature on `main`.** They are not the same:
|
||||
|
||||
| Concern | `main` (deployed) | This redesign (PR #38) |
|
||||
| --- | --- | --- |
|
||||
| Graph layout | **Static** — renders server `x`/`y` columns, no motion | **Dynamic** force-directed (`forceSim.ts`), draggable, animated |
|
||||
| Learning-loop rail | **Computed in API, never rendered** | Rendered (`LearningLoop.tsx`) |
|
||||
| Activity stream | **Computed in API, never rendered** | Rendered (`ActivityStream.tsx`) |
|
||||
| Metric cards | **Inflated** (raw collision-signature / accepted-outcome counts → e.g. 50 risk paths for 4 files) | **De-noised** (distinct collision files / owner engineers) |
|
||||
| Selected-node pane | kind/status/relationships | + **Flow** narrative ("Karti and Yahya are both editing auth.ts…") |
|
||||
| `loop` type | `PodLearningLoop` = `{ activeStep, steps: PodLearningLoopStep[] }` (richer; step `status`) | **kept `main`'s** |
|
||||
| `activity` type | `PodGraphActivity` = `{ id, at, kind, title, detail, nodeId?, edgeId? }` (richer) | **kept `main`'s** |
|
||||
|
||||
**Reconciliation strategy (what PR #38 does):** keep `main` as the trunk; keep `main`'s **backend** materializer/`buildLoop`/activity and its **richer shared types**; swap in **this redesign's frontend graph layer**; adapt the rail components to consume `main`'s types; port only the **honest-metrics** fix into `main`'s `live.ts`.
|
||||
|
||||
> A literal `feat/live-graph-glue → main` merge was attempted first and produced **invalid auto-merge states** (duplicate `loop`/`activity` keys in `demo.ts`, duplicate imports in `live.ts`) because glue was ~100 commits stale. It was aborted; the same best-of-both was re-applied cleanly on a branch off `main`. The PR diff is just the graph layer (11 files).
|
||||
|
||||
---
|
||||
|
||||
## 3. What PR #38 changes (11 files)
|
||||
|
||||
**Frontend — new `frontend/src/components/graph/*`, composed into `GraphView.tsx`:**
|
||||
- `forceSim.ts` — **dependency-free** force layout (charge repulsion, link springs, centroid recenter + gentle pull, 2-pass collision, bounds clamp, alpha annealing). Driven by a `requestAnimationFrame` loop. **No new dependency / no `pnpm-lock.yaml` change.**
|
||||
- `GraphCanvas.tsx` — SVG render from the sim: **draggable + pinnable** nodes (double-click to release), curved edges that fan parallel pairs, weight-sized geometric shapes (square / outlined-square / circle / triangle / diamond), fade-in on new nodes/edges, animated `learned_from` dash, **risk-path lit / rest dimmed**, label collision-avoidance.
|
||||
- `encoding.ts` — node/edge kind colors, `highlightFor` (risk/learn/all modes), `flowNarrative(graph, nodeId)` (the plain-English path walk), `modeBlurb`, legends, `ACTIVITY_TAG` (keyed by `main`'s `PodGraphActivityKind`).
|
||||
- `MetricsRail.tsx`, `LearningLoop.tsx`, `ActivityStream.tsx`, `SelectedNodePanel.tsx` — the rails + stream + detail pane in light shadcn (`@/components/ui/*`). `LearningLoop` consumes `PodLearningLoop` (steps + `activeStep`); `ActivityStream` consumes `PodGraphActivity` (title + detail); `SelectedNodePanel` renders the **Flow** section + mode-aware default copy.
|
||||
- `GraphView.tsx` — composes everything; polls `/api/pods/:id/graph` every 5s and **diffs** (positions/pins preserved across refreshes — no hard replace), + best-effort `ws /api/events` nudge; drops a **stale selection** (selected node gone across a poll) so the canvas can't dim entirely.
|
||||
- `lib/graph.ts` — adds `backendEventsUrl()` (http→ws) for the nudge.
|
||||
|
||||
**Backend — surgical (keeps `main`'s materializer + `buildLoop` + activity builder):**
|
||||
- `backend/src/graph/live.ts` — the **headline metric cards** are now derived from the **final de-noised graph**: *Open risk paths* = distinct collision **files** (`touches` edges to file nodes); *Learned owners* = distinct **owner engineers** (`owns`/`learned_from` edges). On live data: **50 → 4** risk files, **16 → 1** owner. `riskPaths` (raw signatures) is still computed and fed to `buildLoop` (the loop is a throughput view, intentionally separate). **`buildLoop` and the activity builder are untouched.**
|
||||
- `backend/src/graph/demo.ts` — fallback metrics realigned to the demo graph (3 owners / 1 risk path / 100%) so the numbers never contradict the picture.
|
||||
|
||||
---
|
||||
|
||||
## 4. How it works (architecture)
|
||||
|
||||
```
|
||||
materializePodGraph(podId) // backend/src/graph/live.ts (main's, + metric fix)
|
||||
→ GET /api/pods/:id/graph // backend/src/server.ts (PodGraph incl. loop/activity)
|
||||
→ fetchPodGraph() poll every 5s // frontend/src/lib/graph.ts (+ ws /api/events nudge)
|
||||
→ GraphView // diffs snapshots, computes highlight + flow
|
||||
→ GraphCanvas (forceSim tick → SVG, draggable)
|
||||
→ MetricsRail / LearningLoop / ActivityStream / SelectedNodePanel
|
||||
```
|
||||
|
||||
- **`PodGraph` data contract** (`shared/src/graph.ts`, `main`'s): `nodes`, `edges`, `metrics`, optional `loop?: PodLearningLoop`, `activity?: PodGraphActivity[]`. Node kinds: engineer/feature/file/collision/intervention. Edge kinds: owns/editing/touches/collides/warns/learned_from.
|
||||
- **Force sim** ignores the server's `x`/`y` except as **seed** positions (mapped into the canvas). Key tuning constants in `forceSim.ts`: `REPEL=4400`, `CENTER_STRENGTH=0.014`, `RECENTER=0.5`, `COLLIDE_PAD=12`, `COLLIDE_ITERS=2`, `BOUND_PAD=30`. Repulsion must dominate centering or the graph collapses to a point.
|
||||
- **Default mode is "Risk path"** — lights the collision→intervention→`learned_from` chain, dims the rest to opacity `0.14`.
|
||||
- **Flow narrative** (`flowNarrative`) walks a node's incident edges to produce sentences, e.g. collision → "Karti and Yahya are both editing auth.ts before pushing — the overlap git can't see. PodMan stepped in and suggested a sync PR."
|
||||
|
||||
---
|
||||
|
||||
## 5. Is the data real / dynamic? (FAQ — was asked)
|
||||
|
||||
- **Real:** yes. The graph is materialized **live from the real Atlas collections** (`observations`, `collisions`, `interventions`, `outcomes`, `engineer_states`, `pods`, `team_model`) on every request — not the hardcoded demo. The demo only shows as a **fallback** when there's zero activity. `generatedAt` advances on each request (re-materialized, not cached).
|
||||
- **Dynamic:** the backend re-reads Mongo per request and the frontend polls ~5s + WS nudge, so the UI reflects current DB state within seconds. **But it only *changes* when the PodMan agent writes new data** (vision → observations → collisions → interventions → outcomes). When the agent is idle, the graph is static at last-known state. (At handoff time the newest activity was ~52 min old — no live ingestion.)
|
||||
- **Numbers look inflated** because a lot of the real data is **test churn** (repeated `infra/README.md` collisions). The honest-metrics fix (§3) counts distinct entities so the cards match the graph; `MAX_COLLISIONS=8` in the materializer caps the visible collisions.
|
||||
|
||||
---
|
||||
|
||||
## 6. Build / verify (toolchain quirks — important)
|
||||
|
||||
- **`pnpm` is not on PATH** in the dev sandbox; use **`CI=true npx pnpm@10.32.1 …`** (pin 10.32.1 to match CI and keep the lockfile v10-compatible; `CI=true` avoids the no-TTY abort).
|
||||
- `CI=true npx pnpm@10.32.1 lint`
|
||||
- `CI=true npx pnpm@10.32.1 -r typecheck`
|
||||
- `CI=true npx pnpm@10.32.1 -r build`
|
||||
- The `npm error config prefix cannot be changed from project config: .npmrc` line is a **non-fatal warning** (tsc/eslint still run; exit code 0).
|
||||
- **Adding a dependency is high-friction**: `pnpm install` wants to wipe + recreate `node_modules` (modules-dir version mismatch) and churns the shared `pnpm-lock.yaml` that CI's `--frozen-lockfile` depends on. That's why the force sim is **in-house**, not `d3-force`. Prefer zero-dep solutions.
|
||||
- **CI** (`.github/workflows/hermes-verify.yml`) runs `pnpm install --frozen-lockfile && pnpm lint && pnpm -r typecheck && pnpm -r build` (pnpm 10.32.1, node 22).
|
||||
|
||||
**Running it locally to eyeball:** the **backend can't run locally** (needs LiveKit/Gemini/GitHub secrets and hard-exits without Mongo). Verify the **frontend** by pointing a vite dev server at either the live backend (CORS is `*`) or a tiny mock that serves `createDemoPodGraph()` from `backend/dist/graph/demo.js`:
|
||||
|
||||
```
|
||||
# mock backend (node http) serving GET /api/pods/:id/graph from backend/dist/graph/demo.js
|
||||
# then: VITE_BACKEND_URL=http://localhost:8799 in frontend/.env.local
|
||||
CI=true npx pnpm@10.32.1 --filter @podman/frontend dev
|
||||
```
|
||||
|
||||
A throwaway entry (`frontend/graph-preview.html` + `frontend/src/graph-preview.tsx` mounting `<GraphView podId="demo-pod" .../>`) renders the view directly without the pods list. Drive it with Playwright (already a devDependency; chromium is cached) — sample node positions over time to confirm the sim ticks, `getComputedStyle` opacity to confirm dimming, click nodes to read the Flow text. **Delete all of these temp files before committing.**
|
||||
|
||||
---
|
||||
|
||||
## 7. Gotchas (these already bit; don't re-learn them)
|
||||
|
||||
- **PWA cache** — hard-refresh after every deploy or you'll think nothing changed.
|
||||
- **StrictMode RAF freeze (fixed):** the dev double-mount cancels the animation frame between effect passes; the loop must re-arm **unconditionally** after `setData` (`ensureRaf()` is idempotent via the `rafRef==null` guard), not gated on a topology change — else the sim is frozen at seed positions in dev until first interaction. Seed positions are a plausible layout, so this can hide.
|
||||
- **Dimming vs animation (fixed):** `.pm-dim` opacity is defeated if the fade-in uses `animation-fill-mode: both/forwards` (held final keyframe overrides the class). The enter animation must use **no fill-mode**.
|
||||
- **Stale selection (fixed):** after a poll, a selected node can vanish from the payload; `highlightFor(selected)` would then light only a dead id and dim the whole graph. `GraphView` derives `liveSelected = selected ∈ nodeById ? selected : null` and clears it.
|
||||
- **Force tuning:** repulsion must dominate centering (`REPEL ≫ CENTER_STRENGTH·r`) or the graph collapses; `COLLIDE_ITERS≥2` keeps linked nodes from stacking.
|
||||
- **Pathological data:** with the *uncapped* live materializer (pre-`MAX_COLLISIONS`), engineer labels can crowd the center because each engineer fans many `collides` edges. The materializer cap is the real fix; the frontend still spreads + draggable.
|
||||
- **`learned_from` "money" edge on `demo-pod`** won't draw on real data unless there's one intact accept flow (its one accepted outcome is orphaned). The demo fallback shows it.
|
||||
- **Parallel impl on `main`:** keep `main`'s `PodLearningLoop`/`PodGraphActivity` types and `buildLoop`/activity builder. Do **not** reintroduce the v1 `LearningStage`/`ActivityEvent` types — they were dropped in the reconciliation.
|
||||
|
||||
---
|
||||
|
||||
## 8. Open items / nice-to-haves
|
||||
|
||||
- **Merge PR #38, redeploy, hard-refresh** (the headline).
|
||||
- Close PR #22; delete `feat/live-graph-glue` + `feat/team-memory-dynamic-graph`.
|
||||
- Optional polish: more canvas spread on small graphs; label de-clutter for pathological/uncapped data; seed a clean collision→intervention→accept chain on `demo-pod` so the violet money edge draws on real data.
|
||||
- Optional: reconcile the loop "Predict" value (distinct signatures) vs the "Open risk paths" card (distinct files) — they intentionally differ today; could unify wording if it confuses.
|
||||
|
||||
---
|
||||
|
||||
## 9. Key files
|
||||
|
||||
| File | Role |
|
||||
| --- | --- |
|
||||
| `backend/src/graph/live.ts` | `materializePodGraph` — real-data graph + `buildLoop`/activity (main's) + **honest metric cards** |
|
||||
| `backend/src/graph/demo.ts` | demo fallback (graph + loop + activity + consistent metrics) |
|
||||
| `backend/src/graph/store.ts` | `loadPodGraph` (live → seeded `team_model.graph` → demo), `reachFrom` (`$graphLookup`) |
|
||||
| `shared/src/graph.ts` | `PodGraph` contract incl. `PodLearningLoop`, `PodGraphActivity` (main's types) |
|
||||
| `frontend/src/components/GraphView.tsx` | page: header, toggles, 3-panel grid, poll/WS, compose |
|
||||
| `frontend/src/components/graph/forceSim.ts` | the in-house force simulation |
|
||||
| `frontend/src/components/graph/GraphCanvas.tsx` | dynamic SVG graph (drag/animate) |
|
||||
| `frontend/src/components/graph/encoding.ts` | colors, `highlightFor`, `flowNarrative`, legends, activity tags |
|
||||
| `frontend/src/components/graph/{MetricsRail,LearningLoop,ActivityStream,SelectedNodePanel}.tsx` | rails/stream/detail |
|
||||
| `frontend/src/lib/graph.ts` | `fetchPodGraph`, `backendEventsUrl` |
|
||||
|
||||
---
|
||||
|
||||
## 10. Verification done (PR #38)
|
||||
|
||||
`pnpm lint` + `-r typecheck` + `-r build` pass. Playwright (dev/StrictMode) confirmed, against the demo payload served from a mock:
|
||||
- dynamic graph **ticks on load** (positions move with no interaction) and is **draggable**;
|
||||
- **learning-loop rail** renders main's 5 steps (ADAPT active) and the **activity stream** renders main's title+detail;
|
||||
- metric cards read **3 / 1 / 100%** (consistent with the graph);
|
||||
- **Flow** narrative is correct per node kind (collision / intervention / engineer / file);
|
||||
- **no overlapping nodes** (≥48px min separation), **zero page errors** (only an expected WS 404 against the mock, handled).
|
||||
|
||||
The honest-metric formula was also re-checked against the **real live graph**: **4** distinct risk files / **1** learned owner (vs the deployed 50 / 16).
|
||||
@@ -0,0 +1,666 @@
|
||||
# Codex Handoff: MongoDB Cleanup, Team Memory Graph, and RSI Learning Docs
|
||||
|
||||
Date: 2026-06-28
|
||||
Repo state checked: `main` at `1d097b1`
|
||||
Database checked: MongoDB Atlas database named `podman`
|
||||
Scope: docs/spec handoff, live Team memory graph verification, and safe DB cleanup path
|
||||
|
||||
## Current Repo State
|
||||
|
||||
The local checkout was moved to `main` and fast-forwarded to `origin/main`.
|
||||
Working tree was clean after inspection.
|
||||
|
||||
The learning and graph specification docs are present on `main`:
|
||||
|
||||
- `docs/agent-learning/README.md`
|
||||
- `docs/agent-learning/spec.md`
|
||||
- `docs/agent-learning/policy.md`
|
||||
- `docs/agent-learning/plan.md`
|
||||
- `docs/agent-learning/prompt.md`
|
||||
- `docs/continual-learning/README.md`
|
||||
- `docs/continual-learning/spec.md`
|
||||
- `docs/continual-learning/policy.md`
|
||||
- `docs/continual-learning/plan.md`
|
||||
- `docs/continual-learning/prompt.md`
|
||||
- `docs/graph-discovery/README.md`
|
||||
- `docs/graph-discovery/spec.md`
|
||||
- `docs/graph-discovery/policy.md`
|
||||
- `docs/graph-discovery/plan.md`
|
||||
- `docs/graph-discovery/prompt.md`
|
||||
|
||||
These docs describe the intended architecture, but only some pieces are backed
|
||||
by live Atlas collections today.
|
||||
|
||||
## Verification Summary
|
||||
|
||||
Atlas connection is valid through local `.env` `MONGODB_URI`. No secrets were
|
||||
printed during verification.
|
||||
|
||||
Public API checks:
|
||||
|
||||
- `https://165-22-129-249.sslip.io/health` returned `200` with `{ "ok": true }`.
|
||||
- `GET /api/pods` returned one real pod: `demo-pod`.
|
||||
- `GET /api/pods/demo-pod/graph` returned a live materialized Team memory graph.
|
||||
|
||||
Live graph response for `demo-pod`:
|
||||
|
||||
- Nodes: `31`
|
||||
- Edges: `59`
|
||||
- Learned owners metric: `2`
|
||||
- Open risk paths metric: `2`
|
||||
- Accept rate metric: `39%`
|
||||
- Learning loop active step: `adapt`
|
||||
- Learned edges: `2` `learned_from` edges
|
||||
- Activity stream populated from real records
|
||||
|
||||
Important conclusion:
|
||||
|
||||
The main Team memory graph endpoint is real and backed by Atlas live
|
||||
materialization. It is not merely returning the demo fallback.
|
||||
|
||||
## Atlas Collection Snapshot
|
||||
|
||||
Approximate counts observed:
|
||||
|
||||
| Collection | Count / status |
|
||||
| --- | ---: |
|
||||
| `pods` | 1 |
|
||||
| `engineer_states` | 9 |
|
||||
| `observations` | 2318 |
|
||||
| `collisions` | 451 |
|
||||
| `interventions` | 362 |
|
||||
| `outcomes` | 107 |
|
||||
| `team_model` | 65 |
|
||||
| `graph_nodes` | 715 |
|
||||
| `graph_edges` | 845 |
|
||||
| `hermes_jobs` | 29 |
|
||||
| `hermes_job_events` | 337 |
|
||||
| `memory_vectors` | missing |
|
||||
| `agent_runs` | missing |
|
||||
| `agent_trace_events` | missing |
|
||||
| `strategy_versions` | missing |
|
||||
| `learning_proposals` | missing |
|
||||
|
||||
Real pod:
|
||||
|
||||
```text
|
||||
demo-pod
|
||||
name: demo pod
|
||||
members: ram, Karti, yahya, shakthi
|
||||
```
|
||||
|
||||
Noise observed:
|
||||
|
||||
- Many `verify-pod-*` records.
|
||||
- Many `verify-graph-*` records.
|
||||
- `Verify ...` observations inside `demo-pod`.
|
||||
- Orphaned verify outcomes in `demo-pod`.
|
||||
- Some stale or duplicate engineer state casing, e.g. `Shakthi` and `shakthi`.
|
||||
- `frontend-pod` outcomes with no corresponding active pod.
|
||||
|
||||
## What Is Real Today
|
||||
|
||||
The following are real and active:
|
||||
|
||||
- Atlas connectivity.
|
||||
- `demo-pod` pod record.
|
||||
- Real `engineer_states` for demo members.
|
||||
- Real observation/collision/intervention/outcome collections.
|
||||
- Live graph materialization from source collections.
|
||||
- Graph response `loop` and `activity` fields.
|
||||
- Real `learned_from` edges produced by accepted real outcomes that still join
|
||||
back to surviving intervention/collision records.
|
||||
|
||||
The following are not yet real:
|
||||
|
||||
- `memory_vectors` collection.
|
||||
- `agent_runs` collection.
|
||||
- `agent_trace_events` collection.
|
||||
- `strategy_versions` collection.
|
||||
- `learning_proposals` collection.
|
||||
|
||||
That means the continual-learning story is currently supported by exact MongoDB
|
||||
records and graph edges. The richer agent-learning spec is documented but not
|
||||
implemented in Atlas yet.
|
||||
|
||||
## Claude Code Review Addendum
|
||||
|
||||
Source: pasted Claude Code review text approved for Codex to read. The review
|
||||
was treated as input, then reconciled against current `main` and the Atlas check
|
||||
above. Do not copy the review blindly; a few findings were from an older repo
|
||||
state or have since been superseded.
|
||||
|
||||
### Still Material Findings
|
||||
|
||||
The review correctly identifies the main mismatch:
|
||||
|
||||
```text
|
||||
The docs describe a broader self-improving platform, while the shipped product
|
||||
currently has a narrower but real recall-and-policy loop.
|
||||
```
|
||||
|
||||
The shipped loop lives in code, not in the aspirational agent-learning docs:
|
||||
|
||||
- `backend/src/agent/podman.ts`
|
||||
- Calls `recallSimilar(collision)`.
|
||||
- If prior memory exists, bumps severity to `critical`.
|
||||
- Calls `shouldIntervene(collision, prior)`.
|
||||
- Calls `preferredAction(collision, prior)`.
|
||||
- Adds the visible message suffix `Seen before.` when prior memory exists.
|
||||
- `backend/src/memory/policy.ts`
|
||||
- Suppresses known false-positive prior outcomes.
|
||||
- Enforces pod cooldown.
|
||||
- Reuses a prior accepted intervention action when available.
|
||||
- `backend/src/memory/vectors.ts`
|
||||
- Stores `memorySignature`, `memoryText`, and optional `embedding` on
|
||||
`collisions`.
|
||||
- `recallSimilar` tries vector recall first, then signature/file fallback.
|
||||
|
||||
This is the real recursive/self-improving asset today:
|
||||
|
||||
```text
|
||||
new collision -> recall prior collision -> adjust severity/action/message ->
|
||||
record outcome -> future collision changes behavior
|
||||
```
|
||||
|
||||
The current docs should eventually be reconciled around this loop instead of
|
||||
implying the full agent-learning platform already exists.
|
||||
|
||||
### Confirmed Aspirational Areas
|
||||
|
||||
The following are documented but not live in Atlas/code yet:
|
||||
|
||||
- `agent_runs`
|
||||
- `agent_trace_events`
|
||||
- `strategy_versions`
|
||||
- `learning_proposals`
|
||||
- `memory_vectors`
|
||||
|
||||
The `agent-learning` docs should be treated as future architecture unless a
|
||||
small, explicit slice is implemented. For demo purposes, do not build a broad
|
||||
strategy-versioning platform. If time allows, the smallest credible slice is one
|
||||
stored policy/strategy row that explains a concrete behavior change.
|
||||
|
||||
### Outcome Write Caveat
|
||||
|
||||
`backend/src/memory/store.ts` `recordOutcome` currently:
|
||||
|
||||
- inserts the outcome into `outcomes`;
|
||||
- updates the intervention status to `accepted` or `dismissed`.
|
||||
|
||||
It does not currently persist `team_model.ownership`. The live graph can still
|
||||
derive `owns` and `learned_from` from accepted real outcomes at read time, but a
|
||||
literal "before/after MongoDB ownership write" does not exist yet.
|
||||
|
||||
If the demo needs a concrete durable ownership diff, add a small explicit write
|
||||
on accepted real outcomes:
|
||||
|
||||
```text
|
||||
accepted && wasRealCollision -> team_model.ownership[normalizedFile] = learnedOwner
|
||||
```
|
||||
|
||||
That should be a separate code task, not part of the DB cleanup unless the user
|
||||
explicitly asks.
|
||||
|
||||
### Vector Recall Caveat
|
||||
|
||||
The docs often say "exact recall first." Current code does the reverse:
|
||||
|
||||
```text
|
||||
recallSimilar = vector recall first, then signature/file fallback
|
||||
```
|
||||
|
||||
Also:
|
||||
|
||||
- Embeddings live on `collisions`, not `memory_vectors`.
|
||||
- Atlas vector index name in code is `collision_embedding`.
|
||||
- Gemini embedding calls request `outputDimensionality: 768`.
|
||||
- Voyage embeddings may have a different dimensionality depending on model.
|
||||
|
||||
For the hackathon demo, exact/signature/file fallback is the reliable story.
|
||||
Vector recall should remain nice-to-have unless Atlas index configuration is
|
||||
verified.
|
||||
|
||||
### Demo Script Caveat
|
||||
|
||||
`docs/demo-setup.md` is stale relative to the Team memory observatory demo. It
|
||||
still describes an older Hermes/voice/blocker flow and does not script:
|
||||
|
||||
- graph observatory;
|
||||
- collision -> intervention -> outcome;
|
||||
- `learned_from`;
|
||||
- run 1 vs run 2 changed behavior.
|
||||
|
||||
Before stage rehearsal, rewrite `docs/demo-setup.md` around the actual
|
||||
observatory path.
|
||||
|
||||
### Superseded Review Findings
|
||||
|
||||
The pasted review included two findings that must be treated carefully:
|
||||
|
||||
- It claimed the current `shared/src/graph.ts` had an older `LearningStage` /
|
||||
`ActivityEvent.text` contract. Current `main` uses `PodLearningLoop` with
|
||||
`activeStep`, step `status`, and `PodGraphActivity` with `title` / `detail`.
|
||||
Always check `shared/src/graph.ts` before editing specs.
|
||||
- It claimed the hero `learned_from` edge did not render on `demo-pod`. The
|
||||
current Atlas/public API check returned two live `learned_from` edges. The
|
||||
risk is still real if cleanup deletes accepted real outcomes or their joined
|
||||
collision/intervention records. Preserve the intact accepted chains.
|
||||
|
||||
### Priority Reconciliation Tasks
|
||||
|
||||
After the DB cleanup script, the next documentation/code priorities should be:
|
||||
|
||||
1. Rewrite `docs/demo-setup.md` as the canonical graph observatory demo script.
|
||||
2. Add a short `docs/recursive-loop.md` or equivalent section that names the
|
||||
real shipped loop in `podman.ts`, `policy.ts`, and `vectors.ts`.
|
||||
3. Mark agent-learning collections and strategy versioning as not-yet-built
|
||||
unless implemented.
|
||||
4. Reconcile vector-recall language in docs with current `vectors.ts`.
|
||||
5. Optionally add the `recordOutcome` ownership write if a durable ownership
|
||||
diff is needed for judging.
|
||||
|
||||
## Main Data Issue
|
||||
|
||||
The live graph and the normalized graph mirror are out of sync.
|
||||
|
||||
Live materializer for `demo-pod`:
|
||||
|
||||
```text
|
||||
31 nodes
|
||||
59 edges
|
||||
2 learned_from edges
|
||||
```
|
||||
|
||||
Normalized mirror in `graph_nodes` / `graph_edges` for `demo-pod`:
|
||||
|
||||
```text
|
||||
11 seeded/demo-style nodes
|
||||
13 seeded/demo-style edges
|
||||
```
|
||||
|
||||
Impact:
|
||||
|
||||
- `GET /api/pods/demo-pod/graph` is good and real.
|
||||
- `GET /api/pods/demo-pod/graph/reach/:nodeId` uses `graph_edges`, so it can
|
||||
return stale seeded paths.
|
||||
- Example observed:
|
||||
- `/graph/reach/engineer:karti` returned a seeded path.
|
||||
- `/graph/reach/engineer:ram` returned `0`, even though Ram is present in the
|
||||
live graph.
|
||||
|
||||
The cleanup should therefore include a mirror rebuild after deleting test data.
|
||||
|
||||
## Relevant Code Paths
|
||||
|
||||
Graph and MongoDB:
|
||||
|
||||
- `backend/src/graph/live.ts`
|
||||
- Live materializer.
|
||||
- Reads `pods`, `engineer_states`, `observations`, `collisions`,
|
||||
`interventions`, `outcomes`, and `team_model`.
|
||||
- Produces `nodes`, `edges`, `metrics`, `loop`, and `activity`.
|
||||
- `backend/src/graph/store.ts`
|
||||
- `loadPodGraph`: live materializer first, then seeded `team_model.graph`,
|
||||
then demo fallback.
|
||||
- `seedGraph`: writes seeded graph into `team_model`, `graph_nodes`,
|
||||
`graph_edges`.
|
||||
- `reachFrom`: uses `$graphLookup` over `graph_edges`.
|
||||
- `backend/src/memory/db.ts`
|
||||
- MongoDB connection and core collection helpers.
|
||||
- `shared/src/graph.ts`
|
||||
- Public graph contract including optional `loop` and `activity`.
|
||||
|
||||
Docs:
|
||||
|
||||
- `docs/mongodb.md`
|
||||
- `docs/graph.md`
|
||||
- `docs/graph-discovery/`
|
||||
- `docs/continual-learning/`
|
||||
- `docs/agent-learning/`
|
||||
|
||||
## DB Cleanup Goal
|
||||
|
||||
Get Atlas into a demo-stable state:
|
||||
|
||||
1. Preserve real `demo-pod` learning history.
|
||||
2. Remove verification/orphan/test records.
|
||||
3. Rebuild `graph_nodes` and `graph_edges` from the live materialized graph.
|
||||
4. Keep cleanup repeatable and reversible.
|
||||
5. Avoid ad hoc shell deletes.
|
||||
|
||||
## Required Safety Rule
|
||||
|
||||
Take a backup before deleting anything.
|
||||
|
||||
```bash
|
||||
mongodump "$MONGODB_URI" --archive=podman-before-cleanup.archive --gzip
|
||||
```
|
||||
|
||||
Do not commit the archive.
|
||||
|
||||
## Cleanup Keep Set
|
||||
|
||||
Start with this conservative keep set:
|
||||
|
||||
```js
|
||||
const keepPods = ["demo-pod"];
|
||||
```
|
||||
|
||||
Records with `podId` outside this set are cleanup candidates unless there is a
|
||||
specific reason to preserve them.
|
||||
|
||||
## Phase 1: Dry-Run Counts
|
||||
|
||||
Write a script that defaults to dry-run. It should print counts only.
|
||||
|
||||
Candidate file:
|
||||
|
||||
```text
|
||||
scripts/db-cleanup.mjs
|
||||
```
|
||||
|
||||
Default behavior:
|
||||
|
||||
```bash
|
||||
node scripts/db-cleanup.mjs --dry-run
|
||||
```
|
||||
|
||||
Apply behavior:
|
||||
|
||||
```bash
|
||||
node scripts/db-cleanup.mjs --apply
|
||||
```
|
||||
|
||||
The script must not delete anything unless `--apply` is present.
|
||||
|
||||
## Phase 2: Remove Orphan/Test Pod Data
|
||||
|
||||
Delete records whose `podId` is not in `keepPods`.
|
||||
|
||||
Collections:
|
||||
|
||||
- `engineer_states`
|
||||
- `observations`
|
||||
- `collisions`
|
||||
- `interventions`
|
||||
- `outcomes`
|
||||
- `team_model`
|
||||
- `graph_nodes`
|
||||
- `graph_edges`
|
||||
- `hermes_jobs`
|
||||
- `hermes_job_events`
|
||||
|
||||
Filter:
|
||||
|
||||
```js
|
||||
{ podId: { $nin: ["demo-pod"] } }
|
||||
```
|
||||
|
||||
Note:
|
||||
|
||||
Some `pods` documents may use `id` instead of `podId`. For `pods`, do not use
|
||||
the filter above. Keep the document with `id: "demo-pod"` and delete obvious
|
||||
test pods only if they exist.
|
||||
|
||||
## Phase 3: Clean Demo-Pod Verification Artifacts
|
||||
|
||||
Within `demo-pod`, delete only obvious verification records.
|
||||
|
||||
### Observations
|
||||
|
||||
```js
|
||||
{
|
||||
podId: "demo-pod",
|
||||
$or: [
|
||||
{ engineerId: /^Verify\b/ },
|
||||
{ currentFile: /^PodMan verification screen$/ },
|
||||
{ currentFile: /^frame \d+$/ }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Outcomes
|
||||
|
||||
```js
|
||||
{
|
||||
podId: "demo-pod",
|
||||
$or: [
|
||||
{ interventionId: /^int-verify-/ },
|
||||
{ collisionId: /^col-verify-/ }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Collisions and Interventions
|
||||
|
||||
Be more conservative. Delete only records that clearly have verify IDs or no
|
||||
matching counterpart.
|
||||
|
||||
Safe candidate filters:
|
||||
|
||||
```js
|
||||
// collisions
|
||||
{
|
||||
podId: "demo-pod",
|
||||
id: /^col-verify-/
|
||||
}
|
||||
|
||||
// interventions
|
||||
{
|
||||
podId: "demo-pod",
|
||||
id: /^int-verify-/
|
||||
}
|
||||
```
|
||||
|
||||
Optional orphan cleanup:
|
||||
|
||||
- Delete interventions whose `collisionId` does not exist in `collisions`.
|
||||
- Delete outcomes whose `interventionId` does not exist in `interventions` and
|
||||
whose `collisionId` does not exist in `collisions`.
|
||||
|
||||
Run orphan cleanup only after dry-run prints exact IDs and counts.
|
||||
|
||||
## Phase 4: Normalize Demo-Pod Engineer State
|
||||
|
||||
Keep canonical active engineers:
|
||||
|
||||
```text
|
||||
ram
|
||||
Karti
|
||||
yahya
|
||||
shakthi
|
||||
```
|
||||
|
||||
Cleanup candidates:
|
||||
|
||||
```js
|
||||
{
|
||||
podId: "demo-pod",
|
||||
$or: [
|
||||
{ name: /^Verify\b/ },
|
||||
{ name: /^codex-check$/i },
|
||||
{ name: /^testrepo/i },
|
||||
{ name: "Shakthi" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Only delete `Shakthi` if `shakthi` is confirmed as the canonical current record.
|
||||
|
||||
## Phase 5: Rebuild Graph Mirror
|
||||
|
||||
This is the most important post-cleanup step.
|
||||
|
||||
The live graph endpoint is real, but reachability uses stale mirrored records.
|
||||
After cleanup:
|
||||
|
||||
1. Materialize the live graph for `demo-pod`.
|
||||
2. Delete mirrored rows for `demo-pod`.
|
||||
3. Insert live graph nodes into `graph_nodes`.
|
||||
4. Insert live graph edges into `graph_edges`.
|
||||
5. Update `team_model.graph` and `team_model.updatedAt`.
|
||||
|
||||
Pseudocode:
|
||||
|
||||
```js
|
||||
const graph = await materializePodGraph("demo-pod");
|
||||
|
||||
await db.collection("graph_nodes").deleteMany({ podId: "demo-pod" });
|
||||
await db.collection("graph_edges").deleteMany({ podId: "demo-pod" });
|
||||
|
||||
await db.collection("graph_nodes").insertMany(
|
||||
graph.nodes.map((node) => ({ ...node, podId: "demo-pod" }))
|
||||
);
|
||||
|
||||
await db.collection("graph_edges").insertMany(
|
||||
graph.edges.map((edge) => ({ ...edge, podId: "demo-pod" }))
|
||||
);
|
||||
|
||||
await db.collection("team_model").updateOne(
|
||||
{ podId: "demo-pod" },
|
||||
{ $set: { podId: "demo-pod", graph, updatedAt: new Date().toISOString() } },
|
||||
{ upsert: true }
|
||||
);
|
||||
```
|
||||
|
||||
Important:
|
||||
|
||||
Use `materializePodGraph`, not `createDemoPodGraph`, for this rebuild.
|
||||
`seedGraph` currently writes a demo graph and would recreate the stale mismatch.
|
||||
|
||||
## Phase 6: Add Helpful Indexes
|
||||
|
||||
Current `initMemory` creates the core indexes, but cleanup/reachability benefits
|
||||
from these as well:
|
||||
|
||||
```js
|
||||
db.graph_nodes.createIndex({ podId: 1, id: 1 }, { unique: true });
|
||||
db.graph_edges.createIndex({ podId: 1, id: 1 }, { unique: true });
|
||||
db.graph_edges.createIndex({ podId: 1, source: 1 });
|
||||
db.graph_edges.createIndex({ podId: 1, target: 1 });
|
||||
db.graph_edges.createIndex({ podId: 1, kind: 1 });
|
||||
db.team_model.createIndex({ podId: 1 }, { unique: true });
|
||||
db.collisions.createIndex({ podId: 1, memorySignature: 1 });
|
||||
db.outcomes.createIndex({ podId: 1, interventionId: 1 });
|
||||
db.interventions.createIndex({ podId: 1, collisionId: 1 });
|
||||
```
|
||||
|
||||
Make index creation idempotent.
|
||||
|
||||
## Validation After Cleanup
|
||||
|
||||
Run these checks after `--apply`.
|
||||
|
||||
### Atlas counts
|
||||
|
||||
Confirm:
|
||||
|
||||
- No `verify-pod-*` pod data remains.
|
||||
- No `verify-graph-*` team models or graph rows remain.
|
||||
- `demo-pod` still has meaningful observations, collisions, interventions, and
|
||||
outcomes.
|
||||
|
||||
### Public API
|
||||
|
||||
```bash
|
||||
curl https://165-22-129-249.sslip.io/health
|
||||
curl https://165-22-129-249.sslip.io/api/pods
|
||||
curl https://165-22-129-249.sslip.io/api/pods/demo-pod/graph
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- Health is `ok`.
|
||||
- `demo-pod` still exists.
|
||||
- Graph has nodes, edges, metrics, loop, activity.
|
||||
- Graph includes `learned_from` if accepted real outcomes remain.
|
||||
|
||||
### Reachability
|
||||
|
||||
After mirror rebuild, these should reflect the live graph, not old seed data:
|
||||
|
||||
```bash
|
||||
curl "https://165-22-129-249.sslip.io/api/pods/demo-pod/graph/reach/engineer%3Aram"
|
||||
curl "https://165-22-129-249.sslip.io/api/pods/demo-pod/graph/reach/engineer%3Ayahya"
|
||||
curl "https://165-22-129-249.sslip.io/api/pods/demo-pod/graph/reach/engineer%3Akarti"
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- At least engineers present in the live graph should have reachable edges when
|
||||
they have outbound graph edges.
|
||||
|
||||
## Known Tooling Caveat
|
||||
|
||||
One `pnpm exec` verification attempt was blocked by supply-chain policy:
|
||||
|
||||
```text
|
||||
prettier@3.9.0 was within the minimumReleaseAge cutoff
|
||||
```
|
||||
|
||||
This did not indicate a MongoDB or graph failure. Direct MongoDB reads and the
|
||||
existing local `backend/node_modules/.bin/tsx` binary were used instead.
|
||||
|
||||
Do not run broad dependency cleanup during DB cleanup unless explicitly asked.
|
||||
|
||||
## Recommended Cleanup Script Shape
|
||||
|
||||
The script should:
|
||||
|
||||
- Load `.env` with `dotenv`.
|
||||
- Require `MONGODB_URI`.
|
||||
- Print the database name.
|
||||
- Refuse to run against a DB whose name is not `podman` unless `--force-db` is
|
||||
passed.
|
||||
- Default to `--dry-run`.
|
||||
- Require `--apply` for deletes.
|
||||
- Print every collection and matched count before deleting.
|
||||
- Never print `MONGODB_URI`.
|
||||
- Rebuild graph mirror only after delete phase succeeds.
|
||||
- Print before/after counts.
|
||||
|
||||
Suggested flags:
|
||||
|
||||
```text
|
||||
--dry-run
|
||||
--apply
|
||||
--skip-mirror-rebuild
|
||||
--keep-pod demo-pod
|
||||
--force-db
|
||||
```
|
||||
|
||||
## Do Not Do
|
||||
|
||||
- Do not run `seedGraph("demo-pod")` as the fix; that writes the demo graph.
|
||||
- Do not delete all `outcomes`; accepted and dismissed outcomes are learning
|
||||
signals.
|
||||
- Do not delete all `team_model`; preserve or rebuild the `demo-pod` document.
|
||||
- Do not print secrets or raw terminal/screenshot content.
|
||||
- Do not rely on vector collections for the current demo; they are absent.
|
||||
- Do not treat seeded graph mirror data as proof of current live learning.
|
||||
|
||||
## Suggested Next Codex Prompt
|
||||
|
||||
Use this prompt for the implementation pass:
|
||||
|
||||
```text
|
||||
Create a safe MongoDB cleanup script for PodMan.
|
||||
|
||||
Read docs/handoff/README.md, docs/mongodb.md, docs/graph.md,
|
||||
docs/continual-learning/spec.md, backend/src/graph/live.ts,
|
||||
backend/src/graph/store.ts, backend/src/memory/store.ts,
|
||||
backend/src/memory/policy.ts, backend/src/memory/vectors.ts, and
|
||||
backend/src/agent/podman.ts before coding.
|
||||
|
||||
Implement scripts/db-cleanup.mjs with --dry-run default and --apply required for
|
||||
deletes. Keep demo-pod, remove verify/orphan pod data, remove obvious demo-pod
|
||||
verification artifacts, and rebuild graph_nodes/graph_edges/team_model.graph for
|
||||
demo-pod from materializePodGraph, not createDemoPodGraph. Do not print secrets.
|
||||
Run dry-run first and show counts before applying.
|
||||
|
||||
Do not implement broad agent-learning infrastructure in this task. The real
|
||||
shipped RSI loop today is recallSimilar -> shouldIntervene/preferredAction ->
|
||||
outcome -> future recall. Preserve accepted real outcome chains so learned_from
|
||||
continues to render.
|
||||
```
|
||||
Reference in New Issue
Block a user