Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Arena Synthesis

## Rubric

| Criterion | Candidate A | Candidate B | Candidate C |
| --- | --- | --- | --- |
| Proves current bug from repo evidence | Strong | Strong | Medium |
| Fixes engine-backed persistence without broad API/schema drift | Medium | Medium | Low |
| Adds meaningful current-failing regression | Strong | Strong | Medium |
| Practical verification under pnpm hardening | Medium | Medium | Medium |
| Maintains future project-scoped memory behavior | Strong | Strong | Low |

## Pick

Base: candidate B.

Candidate B gives the clearest implementation boundary: maintain an internal `mem:memory-projects` sidecar at `StateKV`, hydrate `Memory.project` centrally on `KV.memories` reads/lists, and use a fake SDK that intentionally strips `project` from memory rows to produce a focused red test. Candidate A converges on the same architecture and adds useful details around list hydration and legacy unscoped behavior. Candidate C is useful as a simpler alternative but is not a safe base: pinned `iii-sdk@0.11.2` serializes trigger messages with `JSON.stringify`, so a `project: undefined` own property is not a reliable wire/storage-shape stabilizer.

The cross-judge agreed with this pick. Judge scores were A 21.0/25, B 24.0/25, and C 13.0/25. The judge recommended using B as the base, grafting A's lifecycle/schema guardrails, and grafting C's unscoped-first integration scenario only.

## Grafts

- From candidate A: keep direct `memory.project` authoritative when present, and hydrate from the sidecar only when the row lacks a usable project.
- From candidate A: include delete/stale-sidecar cleanup and legacy unscoped-memory tests.
- From candidate B: prefer a primitive JSON string sidecar value if the implementation proceeds, because the reported engine behavior specifically drops unknown object fields in the memory record.
- From candidate B: add a `mem::remember` + diagnostics regression using a fake `StateKV`/SDK that simulates the engine dropping `Memory.project`.

## Rejections

- Reject candidate C's primary fix for now. Adding `project` as an own property with value `undefined` in `remember.ts` would fail to survive the SDK's JSON wire encoding and therefore may only improve mock-object tests.
- Reject patching `memory-project-coverage` to ignore missing project values. That would hide the isolation failure instead of fixing readback.
- Reject per-endpoint hydration only in diagnostics or `/memories`. That leaves search, smart search, migration, mesh/export, lineage, and enrichment inconsistent.
- Reject dependency or iii-engine upgrades as the first move; those are broader dependency/runtime boundary changes.

## Cross-Judge

Report: `/tmp/arena-303-memory-project/judge/report.md`.

The judge confirmed the candidate convergence/divergence pattern:

- A/B converge on central `StateKV` sidecar hydration.
- C diverges with a `project: undefined` row-shape fix, which the judge rejected as unreliable because undefined values do not survive normal JSON wire encoding.
- All candidates agree MCP/REST/function contracts should stay unchanged and existing mock-KV tests are insufficient.

## Boundary Result

The winning design changes internal persistence/schema behavior by adding a KV sidecar scope and central hydration. That falls under the delegated Human Checkpoint list for persistence/schema changes. Implementation should stop until current-turn approval is given for this narrow internal persistence-sidecar change.

## Verification If Approved

- Red/green focused test for `StateKV` hydration under a fake engine that strips `project` from `KV.memories`.
- Focused function/API tests covering `mem::remember`, diagnostics `memory-project-coverage`, `/agentmemory/memories` project filtering, and `inferMemoryProjects`.
- `corepack pnpm test` when dependencies are materialized; if blocked by pnpm hardening, run `corepack pnpm install --frozen-lockfile --ignore-scripts` first per repo instructions.
- `git diff --check`.
- Because persistence/schema code is touched, run Semgrep default scan and staged Gitleaks before commit.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Implementation Plan

## Source

- User request: implement the arena-selected solution for GitHub issue #303 using the GitHub feature loop.
- Arena result: use candidate B as the base, grafting candidate A's lifecycle guardrails and candidate C's unscoped-first integration scenario.
- Approved boundary: narrow internal persistence/schema sidecar for `KV.memories`; no public MCP/REST API changes, no dependency changes, no upstream iii-engine changes.

## Goal

Make `Memory.project` survive the iii-engine state boundary for memories saved through `memory_save`/`mem::remember`, so project-scoped memories read back as scoped and `memory-project-coverage` can pass for newly scoped data.

## Files

- `src/state/schema.ts`: add an internal `KV.memoryProjects` sidecar scope.
- `src/types.ts`: add the corresponding `MemoryProjectRecord` state type.
- `src/state/kv.ts`: hydrate `KV.memories` reads/lists from the sidecar and maintain sidecar lifecycle on set/delete.
- `test/state-kv-memory-projects.test.ts`: add the focused fake-engine regression that strips `project` from memory rows.
- Existing tests to run around `remember`, diagnostics, and migration/project inference.

## Implementation Steps

1. Add a red regression test for `StateKV` using a fake SDK whose `state::set` drops `project` only for `KV.memories`.
2. Cover:
- scoped memory set/get/list rehydrates `project`,
- unscoped overwrite clears stale sidecar,
- memory delete clears sidecar,
- malformed sidecar data is ignored,
- direct `project` on the memory row remains authoritative.
3. Implement the internal sidecar:
- `KV.memoryProjects = "mem:memory-projects"`,
- store sidecar values as primitive JSON strings keyed by memory id,
- sidecar payload: `{ memoryId, project, updatedAt }`,
- parse sidecars defensively and ignore malformed values,
- only hydrate existing memory rows; orphan sidecars never create memories.
4. Keep public contracts unchanged: `Memory.project?: string` stays the public field, MCP/REST payloads remain as-is.
5. Run focused tests, then broader repo-native checks available in this worktree.
6. Run required post-change review and security gates before any commit or remote action.

## Verification Plan

- Red: `corepack pnpm exec vitest run test/state-kv-memory-projects.test.ts` fails before `src/state/kv.ts` is fixed.
- Green targeted:
- `corepack pnpm exec vitest run test/state-kv-memory-projects.test.ts`
- `corepack pnpm exec vitest run test/remember-project-scope.test.ts test/diagnostics.test.ts test/infer-memory-projects.test.ts`
- Repo-native:
- `corepack pnpm test`
- `corepack pnpm run lint`
- `corepack pnpm run build`
- Hygiene/security:
- `git diff --check`
- `gitleaks protect --staged --redact` after staging intended files
- `semgrep scan --config p/default --error --metrics=off .` unless a repo-native equivalent is identified

## Stop Conditions

- The fake-engine regression cannot be made red before implementation.
- The sidecar requires changing public API behavior, auth/security behavior, dependencies, upstream iii-engine, or an automatic data migration.
- Verification is blocked or red after one focused debug pass without a clear next fix.
- Security gates fail or required tools are unavailable without explicit user acceptance.
Loading
Loading