@afokapu/atdd-flow 0.3.53 → 0.3.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/handover.ts +24 -5
package/package.json
CHANGED
package/src/handover.ts
CHANGED
|
@@ -286,7 +286,26 @@ function assertSinglePiWriter(raw: string) {
|
|
|
286
286
|
if (pi.length !== 1) throw new Error("Legacy source bootstrap requires exactly one live Pi writer on the authorized source pane.");
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
|
|
289
|
+
type PiSource = Pick<HandoverReplacementRequest["source"], "pi_session" | "pi_session_path">;
|
|
290
|
+
|
|
291
|
+
function assertCanonicalSourceNotLive(raw: string, canonical: PiSource) {
|
|
292
|
+
const value = JSON.parse(raw), agents = result(value)?.agents;
|
|
293
|
+
if (!Array.isArray(agents)) throw new Error("Consumed Path-A replacement requires complete global Herdr agent-list evidence.");
|
|
294
|
+
for (const entry of agents) {
|
|
295
|
+
const agent = object(entry);
|
|
296
|
+
if (!agent) throw new Error("Consumed Path-A replacement requires complete global Herdr agent-list evidence.");
|
|
297
|
+
if (text(agent.agent) !== "pi") continue;
|
|
298
|
+
const session = object(agent.agent_session);
|
|
299
|
+
const sessionPath = session?.kind === "path" ? text(session.value) : undefined;
|
|
300
|
+
if (!sessionPath) throw new Error("Consumed Path-A replacement requires complete global Herdr agent-list evidence.");
|
|
301
|
+
const sessionId = basename(sessionPath).match(/_([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i)?.[1];
|
|
302
|
+
if (resolve(sessionPath) === resolve(canonical.pi_session_path) || sessionId === canonical.pi_session) {
|
|
303
|
+
throw new Error("Consumed Path-A replacement prior canonical source is still a live Pi agent.");
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async function verifyLegacySource(source: HandoverReplacementRequest["source"], target: HandoverReplacementRequest["target"], authorizationHash: string, dependencies: HandoverReplacementDependencies, canonical?: PiSource) {
|
|
290
309
|
const command = dependencies.command ?? runOutput;
|
|
291
310
|
const pane = legacyPaneObservation(await command(herdr(source.herdr_session, "pane", "get", source.pane)));
|
|
292
311
|
assertLegacyPane(pane, source, target.cwd);
|
|
@@ -296,6 +315,9 @@ async function verifyLegacySource(source: HandoverReplacementRequest["source"],
|
|
|
296
315
|
]);
|
|
297
316
|
const agent = legacyAgentName(agentRaw, source, target.cwd);
|
|
298
317
|
assertSinglePiWriter(processRaw);
|
|
318
|
+
if (canonical && (canonical.pi_session !== source.pi_session || resolve(canonical.pi_session_path) !== resolve(source.pi_session_path))) {
|
|
319
|
+
assertCanonicalSourceNotLive(await command(herdr(source.herdr_session, "agent", "list")), canonical);
|
|
320
|
+
}
|
|
299
321
|
return { authorization_hash: authorizationHash, ...source, cwd: target.cwd, agent, verified_at: dependencies.at?.() ?? now() };
|
|
300
322
|
}
|
|
301
323
|
|
|
@@ -421,9 +443,6 @@ function assertCanonicalPlacement(intent: Awaited<ReturnType<typeof prepareRepla
|
|
|
421
443
|
|| resolve(request.target.cwd) !== resolve(intent.record.worktree)) {
|
|
422
444
|
throw new Error("Consumed Path-A replacement does not match the unchanged canonical main runtime and placement.");
|
|
423
445
|
}
|
|
424
|
-
if (intent.handover.supersedes !== request.handover.id && !canonicalSourceMatchesRuntime(intent.record.runtime, intent.source)) {
|
|
425
|
-
throw new Error("Consumed Path-A replacement does not match the current handover lineage.");
|
|
426
|
-
}
|
|
427
446
|
}
|
|
428
447
|
|
|
429
448
|
function canonicalRuntimeForSupersession(intent: Awaited<ReturnType<typeof prepareReplacement>>, request: HandoverReplacementRequest) {
|
|
@@ -668,7 +687,7 @@ async function supersedeConsumedReplacement(root: string, resolved: string, file
|
|
|
668
687
|
seat: resolved, pi_session: intent.source.pi_session, pi_session_path: intent.source.pi_session_path,
|
|
669
688
|
source: { herdr_session: intent.source.herdr_session, pane: intent.source.pane }, target: intent.target,
|
|
670
689
|
});
|
|
671
|
-
const liveSource = await verifyLegacySource(intent.source, intent.target, authorization.hash, dependencies);
|
|
690
|
+
const liveSource = await verifyLegacySource(intent.source, intent.target, authorization.hash, dependencies, consumed.canonical);
|
|
672
691
|
const request = supersedingRequest(resolved, file, intent, authorization, consumed, liveSource, dependencies);
|
|
673
692
|
const manifest = supersessionManifest(resolved, file, consumed);
|
|
674
693
|
const staging = await stageSupersession(file, request, consumed, manifest, dependencies);
|