Skip to content

Commit 38d36b8

Browse files
committed
Retain metadata ownership when deleted paths are reused
1 parent a6f2717 commit 38d36b8

3 files changed

Lines changed: 17 additions & 2 deletions

File tree

‎core/linking.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,12 @@ export function linkHistory(plan: Plan, history: History, ledger: ReadonlyMap<st
146146
for (const id of line.origins) if (!removed.has(id)) removed.set(id, combine([line.evidence, current]));
147147
}
148148
}
149-
const metadataEvidence = metadataChange(delta) ? combine([previous.metadata, current]) : previous.metadata;
149+
const metadataEvidence = metadataChange(delta)
150+
? combine([previous.metadata, ...touched.map(path => metadata.get(path) ?? empty()), current])
151+
: previous.metadata;
150152
const metadataPaths = unique([...previous.metadataPaths, ...touched]);
151153
charge(metadataPaths.length);
152-
for (const path of metadataPaths) metadata.set(path, metadataEvidence);
154+
for (const path of metadataPaths) metadata.set(path, combine([metadata.get(path) ?? empty(), metadataEvidence]));
153155
if (oldPath) files.delete(oldPath);
154156
if (delta.newPath) files.set(delta.newPath, { lines: next, metadata: metadataEvidence, metadataPaths });
155157
}

‎docs/implementation/build-step-1.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,7 @@ Updated README import/linking examples to supply the new required context. Decli
100100
Reproduced seven failing fixtures covering six valid findings. Replay now rejects before cumulative line/segment/reference/text growth exceeds its budget; origin unions and hunk-sharing avoid unchecked flattening, and grouping no longer repeatedly re-splits accumulated text. Read and linking operations each use a monotonic overall deadline, with Git subprocess/diff timeouts clamped to the remaining budget. Filesystem checks are cooperative between calls; process-level containment remains the runner's job.
101101

102102
File metadata retains the whole rename path lineage, so a later deletion includes its owner's evidence on the final original-path file card. Multiple metadata owners remain conservatively Ambiguous; foreign owners remain Unplanned. Link target validation rejects intermediate regular-file/gitlink entries, overlap in both ancestry directions, and multi-file unsafe-link repair. Repair of an unsafe existing link remains possible as its own item. Tests cover text, empty, and binary rename/deletion cards; exact line/segment boundaries; reference fanout; and deterministic deadline exhaustion.
103+
104+
## Merged-contract review round 4
105+
106+
The review had no inline findings, but its summary identified lost metadata after deleting and recreating a path. A real binary-file regression reproduced only the recreating owner being retained. Metadata updates now combine existing path evidence before storing/propagating it, so prior deletion owners survive reuse and subsequent renames. Owned deletion plus recreation is Ambiguous; foreign deletion plus recreation stays Unplanned. Both regressions pass.

‎test/history.test.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,3 +306,12 @@ it('bounds linking duration and replacement-origin fanout', () => {
306306
try { expect(() => linkHistory(f.plan, history, f.ledger, p => p, { maxDurationMs: 3 })).toThrow(/deadline/i); }
307307
finally { clock.mockRestore(); }
308308
});
309+
310+
it.each(['P1', undefined])('retains deletion and recreation owners on reused-path file cards (%s)', owner => {
311+
const f = fixture({ 'a.txt': '\0old binary' });
312+
rmSync(join(f.dir, 'a.txt')); f.commit(owner);
313+
f.write('a.txt', '\0new binary'); f.commit('P2');
314+
const card = f.segments().find(s => s.kind === 'file')!;
315+
expect(card.owners).toEqual([owner ?? null, 'P2']);
316+
expect(card.row).toBe(owner ? 'Ambiguous' : 'Unplanned');
317+
});

0 commit comments

Comments
 (0)