Skip to content

Commit c87821a

Browse files
committed
fix(coding-agent): prefer persisted header depth for legacy rehydration entries
The legacy-entry rehydration fallback (entry.rlmDepth ?? 1) is passed as config, which outranks the opened transcript header in AgentSession — so a nested legacy child whose header carried a deeper persisted depth woke at depth 1, weakening the host recursion gate. Registry depth stays authoritative when present; otherwise the validated header depth wins and 1 remains the last-resort default for fully legacy children. origin: PR #584 fallback restored in round-1 triage; surfaced by post-rebase bot review of PR #589.
1 parent 7f57ccf commit c87821a

2 files changed

Lines changed: 46 additions & 3 deletions

File tree

‎packages/coding-agent/src/modes/daemon/daemon-mode.ts‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2619,9 +2619,14 @@ export class AgentDaemon {
26192619
},
26202620
},
26212621
rlmSessionDir: entry.sessionDir,
2622-
rlmDepth: existsSync(entry.sessionFile)
2623-
? resolveSessionRlmDepth(sessionManager.getHeader() ?? {}, entry.sessionFile)
2624-
: (entry.rlmDepth ?? 1),
2622+
// Registry depth is authoritative (written at spawn); for legacy entries
2623+
// without it, the shared accessor resolves persisted header depth or the
2624+
// session file's sub- path before the depth-1 default.
2625+
rlmDepth:
2626+
entry.rlmDepth ??
2627+
(existsSync(entry.sessionFile)
2628+
? resolveSessionRlmDepth(sessionManager.getHeader() ?? {}, entry.sessionFile)
2629+
: 1),
26252630
rlmMaxDepth: entry.rlmMaxDepth,
26262631
rlmParentNodeId: entry.rlmParentNodeId ?? entry.childId,
26272632
},

‎packages/coding-agent/test/daemon-mode.test.ts‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4664,6 +4664,44 @@ describe("daemon mode helpers", () => {
46644664
}
46654665
});
46664666

4667+
it("prefers the persisted header depth when a legacy registry entry lacks one", async () => {
4668+
const tempDir = mkdtempSync(join(tmpdir(), "prime-agent-daemon-legacy-header-depth-"));
4669+
try {
4670+
const fixture = makePersistedRlmDaemonFixture(tempDir);
4671+
const childLines = readFileSync(fixture.childSessionFile, "utf8").split("\n");
4672+
const childHeader = JSON.parse(childLines[0] ?? "{}") as Record<string, unknown>;
4673+
childHeader.rlmDepth = 2;
4674+
childLines[0] = JSON.stringify(childHeader);
4675+
writeFileSync(fixture.childSessionFile, childLines.join("\n"));
4676+
4677+
const registryPath = join(fixture.parentArtifactDir, "rlm-subagents.jsonl");
4678+
const registryEntry = JSON.parse(readFileSync(registryPath, "utf8").trim()) as Record<string, unknown>;
4679+
delete registryEntry.rlmDepth;
4680+
writeFileSync(registryPath, `${JSON.stringify(registryEntry)}\n`);
4681+
4682+
const internals = fixture.daemon as unknown as {
4683+
createRuntime(command: Extract<DaemonCommand, { type: "create" }>): Promise<ActiveSessionState>;
4684+
createAgentMessageController(
4685+
getCurrentState: () => ActiveSessionState | undefined,
4686+
): AgentSessionMessageController;
4687+
};
4688+
const parentState = await internals.createRuntime({
4689+
type: "create",
4690+
sessionPath: fixture.parentSessionFile,
4691+
});
4692+
4693+
await internals
4694+
.createAgentMessageController(() => parentState)
4695+
.sendAgentMessage({ target: "renamed-worker", message: "report progress" });
4696+
4697+
// The nested header depth must win over the legacy depth-1 default so the
4698+
// woken child does not come up shallower than persisted.
4699+
expect(fixture.createRuntime.mock.calls[1]?.[0].sessionOptions?.rlmDepth).toBe(2);
4700+
} finally {
4701+
rmSync(tempDir, { recursive: true, force: true });
4702+
}
4703+
});
4704+
46674705
it("hydrates only the ancestor chain when a nested passive child is messaged", async () => {
46684706
const tempDir = mkdtempSync(join(tmpdir(), "prime-agent-daemon-lazy-nested-message-"));
46694707
try {

0 commit comments

Comments
 (0)