fix(workspace): tell the model which memory store is the team's, and flush mirrors before `run` exits

Merged
SA
2mo ago
+378-36

Issue for this PR

Closes #1332

Type of change

  • Bug fix

What does this PR do?

Two stores answer "remember this for the team" in a linked project, and nothing told the model which one teammates read: the engine's always-on workflow text says to call add_memories, while altimate_memory_write's description never said its blocks are the workspace-mirrored, cross-checkout store. So a plain prompt went to the engine hub and the decision never reached repo B โ€” the issue's tapes show the mirror and overlay themselves work when the model is told to use the block store.

  1. Prompt. The ## Altimate Workspace section (from #1330) gains a Team memory line for a bound project whose workspace has not said memory is off: save decisions and conventions with altimate_memory_write (project for this project, global for everything); they sync to the workspace and every linked checkout; the datamate_* memory tools are the engine's separate store and are not what teammates' sessions read; read altimate_memory_read first and update an existing block rather than add a duplicate. The pure render() takes { teamMemory } as an option (tests stay deterministic); systemSection derives it from memoryEnabledCached. The section cap is raised to 2,000 so the longest shape (pinned + stale + team memory) still fits a maximum-length name.
  2. Tool description. altimate_memory_write says the same, for models that weight tool descriptions over prompt text.
  3. Second defect from the issue โ€” lost uploads on exit. MemoryStore.write mirrors fire-and-forget (void mirrorBlock(...)), which is right for the TUI and wrong for a one-shot run that exits when the turn ends. memory-sync now tracks mirrors in flight and exposes flushPendingMirrors(timeoutMs); run awaits it beside the existing flushPendingSyncs before exiting. The store's write path is unchanged (a cloud failure still must not fail a local write).

Not in this PR (design decisions beyond a routing fix, from the issue's later comment): hiding or de-prioritising the engine hub when workspace memory is on, and training_save as a third write path with no dedup. The prompt line's "read before write" instruction is the cheap dedup; provenance in altimate_memory_read output is a separate change.

How did you verify your code works?

  • identity.test.ts: the line's content, present only with teamMemory: true, absent for unbound; every shape ร— team-memory fits the cap with the name intact. identity-section.test.ts: through systemSection, a bound workspace with memory enabled in the memo gets the line and one with a remembered "off" does not (new noteMemoryDisabledForTests seam).
  • memory-sync.test.ts: flushPendingMirrors holds for a mirror parked on a slow server and returns when it lands; returns immediately with nothing in flight; gives up at its bound on a hung server. Deleting the tracking fails both.
  • run-accounting.test.ts: the exit path flushes syncs and mirrors under the pilot flag (source-level, alongside the existing lifecycle contracts).
  • test/altimate/workspace, test/memory, run-accounting, the v0.12.1 adversarial file: 1068 pass. Typecheck clean.

Not re-run live: the issue's two-repo tape needs two linked checkouts and the extractor; the mirror/overlay path is unchanged and was verified live before v0.12.0.

Screenshots / recordings

N/A โ€” system-prompt and tool-description text.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

๐Ÿค– Generated with Claude Code

https://claude.ai/code/session_012Q51zFUmPg1WwtS5CrGJE6


Summary by cubic

Fixes workspace memory routing so a plain "save this for the team" reaches the store teammates read, and stops run from exiting before a last-turn memory upload lands (closes #1332).

  • The identity section now tells a bound workspace with memory confirmed on to save with altimate_memory_write, that it syncs to the workspace and linked checkouts, that datamate_* tools are a separate engine store, and to check altimate_memory_read first. The line renders only on confirmed enablement (not unknown or stale), and the section cap rises to 2,000.
  • altimate_memory_write's description repeats the same guidance for models that read tool descriptions.
  • run now awaits the skill-sync and mirror flushes together before exiting so mirrors and block archives aren't abandoned. SIGINT/SIGTERM aborts the run first, then holds up to 2s for pending mirrors.
  • Mirror state is anchored on globalThis so the flush reads the same set the writer filled despite two module specifiers.

Note: the first turn of a cold run can miss the line while the enablement request is in flight; the tool description covers that turn.

Summary by CodeRabbit

  • New Features

    • Workspace identity information now shows team memory only when enabled for a linked workspace.
    • Pending workspace memory mirrors and archives are completed before a run exits, with a timeout to prevent indefinite waiting.
    • Workspace identity sections can now display up to 2,000 characters.
  • Bug Fixes

    • Memory guidance is omitted when workspace memory is disabled, stale, or its status is unknown.
    • Memory synchronization now requires both workspace synchronization and workspace memory to be enabled.
    • Interrupt handling now allows pending memory updates to finish before exiting.