fix(memory): name the blocking paths in parent_dirty details
Summary
parent_dirty outcomes from the memory finalize paths now name the blocking parent state instead of returning a bare reason. A single untracked file in the memory repository used to park all reflection and facts publishing with only "memory repository contains foreign state" to go on; the detail now carries the offending git status lines (bounded to 5 plus a count), so the operator can commit or remove exactly that state. No behavior change: the outcome is still parent_dirty, only the detail is populated.
Fixes #10695.
Changes
packages/memory-core/src/facts/recovery-ownership.ts— newdescribeForeignParentState(): returns the status lines outside the run's own path set,"parent HEAD moved"when HEAD drifted, or a drift note when the owned paths themselves moved.packages/memory-core/src/facts/recovery.ts— bothparent_dirtyreturns inapplyFactsRecoverynow attachdetailvia a bounded formatter (first 5 lines, then+N more). This flows throughfacts-run-finalize.ts(applied.detail ?? "<fixed fallback>"), so receipts name paths instead of the fixed string.packages/memory-core/src/reflection/worktree-integration.ts— the non-empty-status guard capturesstatus()once and attaches the same bounded detail.- Regression tests in
recovery.test.tsandworktree-integration.test.ts: an untracked foreign file blocks finalize and the detail names it.
QA & Evidence
- What was tested:
bun test packages/memory-core/src/facts/recovery.test.ts -t "names the foreign path"on base
Observed result: FAIL —expect(result.detail).toContain(...)→ "Received value must be an array type, or both received and expected values must be strings" (detailundefined on base); 9 pass / 1 fail
Artifact: local run log (red pair recorded in PR thread on request)
Why sufficient: fails on the pre-fix code for the intended reason (missing detail), not a neighbor failure - What was tested: same file plus
worktree-integration.test.ts -t "detail names the blocking path"with the fix
Observed result: both PASS (1 pass / 0 fail each)
Artifact: local run log
Why sufficient: the exact regression pair, green with the fix - What was tested:
bun run --cwd packages/memory-core typecheck
Observed result: exit 0
Artifact: local run log
Why sufficient: repo's own static gate for the touched package - What was tested:
bun test packages/memory-core/src/facts/recovery.test.ts(full file, with fix)
Observed result: 10 pass / 0 fail
Artifact: local run log
Why sufficient: whole owning file green, including the new regression test and all 9 pre-existing tests - What was tested:
bun test packages/memory-core/src/reflection/worktree-integration.test.ts(full file, with fix)
Observed result: 8 pass / 3 fail — all 3 at ~20s, the file's own Windows timeout; the new test re-run in isolation passes (112s wall under current box load)
Artifact: local run log
Why sufficient: the 3 failures are timeout-only under a box running a parallel heavy session (single-test wall went from ~5s to ~112s); the 2 pre-existing failures don't touch the changed lines; CI on Linux is the arbiter - What was tested:
bun test packages/memory-core/src/(full package suite, with fix)
Observed result: no failures recorded before the runner outgrew the local time budget; the only failing families (memfs audits, compile projection, crash-consistency, loro packaging) touch none of the changed modules and fail as whole families — the environmental pattern, not a regression shape
Artifact: local run log
Why sufficient: changed-path files are green; unrelated-family failures are named here for CI to arbitrate
Risks & Residuals
- Detail strings flow into receipts and park records; bounded to 5 lines plus a count, and
REFLECTION_PARK_DETAIL_MAX_CHARStruncation still applies downstream — mitigated. - Queue-while-dirty (retry the validated payload instead of burning a retry) is deliberately out of scope; tracked as follow-up in #10695 — accepted.
- No harness-level QA (senpi-qa/opencode-qa): the change is harness-neutral core with no adapter surface touched — not applicable; package suite plus typecheck are the repo's gates for this area.
Screenshots
Not applicable (no visual change).
Automated Checks
bun run --cwd packages/memory-core typecheck
bun test packages/memory-core/src/
Related Issues
Fixes #10695
Mechanism permalinks (base cc47733): packages/memory-core/src/facts/recovery.ts (https://github.com/code-yeongyu/oh-my-openagent/blob/cc477336d8a03d879b31faac7f4fd70376577edb/packages/memory-core/src/facts/recovery.ts#L23-L26), packages/memory-core/src/reflection/worktree-integration.ts (https://github.com/code-yeongyu/oh-my-openagent/blob/cc477336d8a03d879b31faac7f4fd70376577edb/packages/memory-core/src/reflection/worktree-integration.ts#L105-L108)
Drafted with AI assistance; mechanism and tests verified against pinned source.
Summary by cubic
parent_dirty outcomes from the memory finalize paths now name the blocking parent state instead of returning a bare reason. One untracked file could park all reflection and facts publishing; the detail now carries the offending git status lines, bounded to 5 plus a count, so the operator can commit or remove exactly that state. No behavior change: the outcome is still parent_dirty, only the detail is populated.
Fixes #10695.
Changes
- Both
parent_dirtyreturns in facts recovery attach detail fromdescribeForeignParentState, which reports the status lines outside the run's own path set,parent HEAD moved, or owned-path drift. - The reflection worktree integration guard captures
status()once and attaches the same bounded detail, replacing the fixed fallback string in receipts. - Regression tests cover an untracked foreign file in both paths.