* fix(memories): isolate Phase 2 consolidation per project working directory
The global memory consolidation job used a single 'global' job key,
causing all projects to share one Phase 2 slot. Whichever project
claimed it first got every project's stage1 outputs written into its
memory directory — cross-project contamination.
Root cause:
- GLOBAL_KEY = 'global' was a single key shared by all projects
- listStage1OutputsForGlobal() had no cwd filter — returned ALL
stage1 outputs across all projects
- markGlobalPhase2Succeeded/Failed/Unowned all operated on the same
single global job row
Fix:
- Replace GLOBAL_KEY constant with globalJobKey(cwd) function that
namespaces the job key per project: 'global:/path/to/project'
- Add cwd parameter to all Phase 2 storage functions so each project
maintains its own job slot in the jobs table
- Filter listStage1OutputsForGlobal() by t.cwd = ? so each project
only consolidates its own thread outputs
- Thread cwd from session.sessionManager.getCwd() through runPhase2()
to all storage calls
- Add cwd to markStage1SucceededWithOutput/NoOutput so enqueueGlobal
Watermark targets the correct per-project job key
- Add cwd parameter to enqueueMemoryConsolidation() public API and
update its only external caller (command-controller)
Closes#369
* test(memories): add isolation tests for per-project Phase 2 consolidation
Three tests covering the regression fixed in the previous commit:
- listStage1OutputsForGlobal filters outputs by cwd (no cross-project leak)
- enqueueGlobalWatermark creates separate job rows keyed per-project
- tryClaimGlobalPhase2Job claims only the requested project's slot and
leaves the other project's job independently claimable
* docs: add inline comments for memory isolation fix and tests
---------
Co-authored-by: Rens Tillmann <rens@super-forms.com>