feat(FN-4822): complete Step 8 — document central claim authority
Fusion-Task-Id: FN-4822 Fusion-Task-Lineage: 08cc29e8-114a-48dc-80de-8d7fd2ce0e69
This commit is contained in:
committed by
gsxdsm
parent
199f317813
commit
a11bd0719e
@@ -485,6 +485,8 @@ Task ownership supports explicit checkout leases. Agents should be aware of:
|
||||
- If `task.checkedOutBy` is set to another agent, the run exits with `reason: "checkout_conflict"`
|
||||
- Heartbeat execution does not auto-checkout — callers are responsible for obtaining checkout before starting work
|
||||
|
||||
When a `CentralClaimStore` is wired, the authoritative lease owner is the central `taskClaims` row in `~/.fusion/fusion-central.db`; per-project task lease fields are treated as a synchronization mirror of that central result. Without a claim store configured, checkout behavior remains the existing single-node per-project lease flow.
|
||||
|
||||
## Per-Agent Heartbeat Configuration
|
||||
|
||||
Each agent can override heartbeat behavior via `runtimeConfig`. Key settings:
|
||||
|
||||
@@ -27,10 +27,13 @@ Core tables:
|
||||
- `nodes`
|
||||
- `peerNodes`
|
||||
- `settingsSyncState`
|
||||
- `taskClaims` (authoritative cross-node task checkout claims keyed by `(projectId, taskId)`)
|
||||
- `__meta`
|
||||
|
||||
Per-project task data remains in each repo’s `.fusion/fusion.db`.
|
||||
|
||||
`taskClaims` is the central cross-node lease mutex introduced by FN-4819 §2: claim acquisition/renewal/release happen in `~/.fusion/fusion-central.db`, while per-project lease fields mirror the central winner for local scheduler/runtime consumption.
|
||||
|
||||
Peer/mesh coordination spans core + engine, with startup ownership in CLI process entrypoints:
|
||||
|
||||
- Topology visibility is now cluster-wide from any connected node: dashboard mesh reads aggregate remote local snapshots and dedupe by `nodeId`, with fallback to last-known local mesh state when a peer is temporarily unreachable.
|
||||
|
||||
@@ -1432,7 +1432,7 @@ export class AgentStore extends EventEmitter {
|
||||
const centralResult = await Promise.resolve(this.claimStore.tryClaimTask({
|
||||
projectId: this.claimProjectId,
|
||||
taskId,
|
||||
nodeId: requestNodeId,
|
||||
nodeId: requestNodeId!,
|
||||
agentId,
|
||||
runId: leaseContext?.runId ?? task.checkoutRunId ?? null,
|
||||
renewedAt: nextRenewedAt,
|
||||
|
||||
@@ -55,19 +55,19 @@ describe("cross-node claim mutex integration", () => {
|
||||
releaseBarrier = resolve;
|
||||
});
|
||||
|
||||
centralDb.tryClaimTask = ((input) => {
|
||||
centralDb.tryClaimTask = (((input: Parameters<CentralDatabase["tryClaimTask"]>[0]) => {
|
||||
waiters += 1;
|
||||
if (waiters === 2) {
|
||||
releaseBarrier?.();
|
||||
}
|
||||
return barrier.then(() => originalTryClaim(input));
|
||||
}) as CentralDatabase["tryClaimTask"];
|
||||
}) as unknown) as CentralDatabase["tryClaimTask"];
|
||||
};
|
||||
|
||||
installBarrier();
|
||||
const [first, second] = await Promise.allSettled([
|
||||
storeA.checkoutTask(agentA, taskId, { runId: "run-a" }),
|
||||
storeB.checkoutTask(agentB, taskId, { runId: "run-b" }),
|
||||
storeA.checkoutTask(agentA, taskId, { nodeId: "node-a", runId: "run-a" }),
|
||||
storeB.checkoutTask(agentB, taskId, { nodeId: "node-b", runId: "run-b" }),
|
||||
]);
|
||||
|
||||
const fulfilled = [first, second].filter((entry): entry is PromiseFulfilledResult<Awaited<ReturnType<AgentStore["checkoutTask"]>>> => entry.status === "fulfilled");
|
||||
@@ -87,8 +87,8 @@ describe("cross-node claim mutex integration", () => {
|
||||
|
||||
installBarrier();
|
||||
const [third, fourth] = await Promise.allSettled([
|
||||
storeA.checkoutTask(agentA, taskId, { runId: "run-c" }),
|
||||
storeB.checkoutTask(agentB, taskId, { runId: "run-d" }),
|
||||
storeA.checkoutTask(agentA, taskId, { nodeId: "node-a", runId: "run-c" }),
|
||||
storeB.checkoutTask(agentB, taskId, { nodeId: "node-b", runId: "run-d" }),
|
||||
]);
|
||||
|
||||
const fulfilled2 = [third, fourth].filter((entry): entry is PromiseFulfilledResult<Awaited<ReturnType<AgentStore["checkoutTask"]>>> => entry.status === "fulfilled");
|
||||
|
||||
Reference in New Issue
Block a user