Skip to content

Commit c3e4de6

Browse files
svelderrainruizGitHub Copilot
andauthored
Runtime daemon: compile bounded worker task packets from durable lane state (#1002) (#1005)
* #1002 Emit runtime daemon task packets from durable lane state * Runtime daemon: harden observer task packet tracking (#1002) --------- Co-authored-by: GitHub Copilot <copilot@users.noreply.github.com>
1 parent 7300307 commit c3e4de6

8 files changed

Lines changed: 977 additions & 85 deletions

File tree

docs/knowledgebase/External-Agent-Runtime.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,10 @@ Initial extraction note:
351351
deterministic issue branch and persists `worker-branch.json` plus
352352
`workers-branch/*.json` metadata so later turns can resume from a real lane
353353
workspace instead of a detached checkout
354+
- the worker/observer seam now also compiles a bounded per-cycle task packet
355+
from durable runtime state, persists `task-packet.json` plus
356+
`task-packets/*.json`, and leaves an adapter hook for repo-specific
357+
objective/helper context before the execution layer is added
354358
- the compare-vi repository wrapper remains at
355359
`tools/priority/runtime-supervisor.mjs`
356360
- that wrapper now includes the first compare-vi scheduler cut: when no manual

packages/runtime-harness/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ It is intentionally not the `compare-vi-cli-action` adapter. The core owns:
1111
- worker step/status/stop/resume execution
1212
- observer/daemon loop orchestration
1313
- scheduler decision artifacts and planner handoff
14+
- bounded worker task-packet artifacts and adapter handoff
1415
- lease-aware turn execution
1516
- deterministic event, lane, blocker, and turn artifacts
1617

@@ -57,6 +58,8 @@ Adapters may also provide:
5758
lane state before later worker cycles reuse it
5859
- `activateWorker(context)` to attach that ready checkout onto its deterministic
5960
lane branch before real repo-native work runs
61+
- `buildTaskPacket(context)` to compile the one-turn worker packet that the next
62+
execution seam should consume from durable runtime state
6063

6164
The observer persists scheduler evidence under the runtime directory:
6265

@@ -69,6 +72,8 @@ The observer persists scheduler evidence under the runtime directory:
6972
- `workers-ready/*.json` for per-lane worker readiness history
7073
- `worker-branch.json` for the latest worker branch-activation state
7174
- `workers-branch/*.json` for per-lane worker branch attachment history
75+
- `task-packet.json` for the latest bounded worker packet
76+
- `task-packets/*.json` for per-cycle task-packet history
7277

7378
The compare-vi repository is the first adapter implementation.
7479

packages/runtime-harness/index.mjs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const SCHEDULER_DECISION_SCHEMA = 'priority/runtime-scheduler-decision@v1
1616
export const WORKER_CHECKOUT_SCHEMA = 'priority/runtime-worker-checkout@v1';
1717
export const WORKER_READY_SCHEMA = 'priority/runtime-worker-ready@v1';
1818
export const WORKER_BRANCH_SCHEMA = 'priority/runtime-worker-branch@v1';
19+
export const TASK_PACKET_SCHEMA = 'priority/runtime-worker-task-packet@v1';
1920
export const DEFAULT_RUNTIME_DIR = path.join('tests', 'results', '_agent', 'runtime');
2021
export const DEFAULT_LEASE_SCOPE = 'workspace';
2122
export const ACTIONS = new Set(['status', 'step', 'stop', 'resume']);
@@ -284,6 +285,7 @@ export function createRuntimeAdapter(adapter = {}) {
284285
prepareWorker: typeof adapter.prepareWorker === 'function' ? adapter.prepareWorker : null,
285286
bootstrapWorker: typeof adapter.bootstrapWorker === 'function' ? adapter.bootstrapWorker : null,
286287
activateWorker: typeof adapter.activateWorker === 'function' ? adapter.activateWorker : null,
288+
buildTaskPacket: typeof adapter.buildTaskPacket === 'function' ? adapter.buildTaskPacket : null,
287289
resolveRepository:
288290
typeof adapter.resolveRepository === 'function'
289291
? adapter.resolveRepository
@@ -339,6 +341,41 @@ function summarizeWorkerBranch(workerBranchRecord) {
339341
};
340342
}
341343

344+
function summarizeTaskPacket(taskPacketRecord) {
345+
if (!taskPacketRecord) return null;
346+
return {
347+
laneId: taskPacketRecord.laneId,
348+
cycle: taskPacketRecord.cycle,
349+
status: taskPacketRecord.status,
350+
objective: {
351+
summary: taskPacketRecord.objective?.summary ?? null,
352+
source: taskPacketRecord.objective?.source ?? null
353+
},
354+
branch: {
355+
name: taskPacketRecord.branch?.name ?? null,
356+
status: taskPacketRecord.branch?.status ?? null
357+
},
358+
pullRequest: {
359+
url: taskPacketRecord.pullRequest?.url ?? null,
360+
status: taskPacketRecord.pullRequest?.status ?? null
361+
},
362+
checks: {
363+
status: taskPacketRecord.checks?.status ?? null,
364+
blockerClass: taskPacketRecord.checks?.blockerClass ?? null
365+
},
366+
helperSurface: {
367+
preferredCount: Array.isArray(taskPacketRecord.helperSurface?.preferred)
368+
? taskPacketRecord.helperSurface.preferred.length
369+
: 0,
370+
fallbackCount: Array.isArray(taskPacketRecord.helperSurface?.fallbacks)
371+
? taskPacketRecord.helperSurface.fallbacks.length
372+
: 0
373+
},
374+
generatedAt: taskPacketRecord.generatedAt ?? null,
375+
artifacts: taskPacketRecord.artifacts ?? {}
376+
};
377+
}
378+
342379
function normalizeWorkerRecord(worker, now) {
343380
if (!worker || typeof worker !== 'object') return null;
344381
const checkoutPath = normalizeText(worker.checkoutPath) || null;
@@ -433,6 +470,56 @@ function normalizeWorkerBranchRecord(workerBranch, now) {
433470
};
434471
}
435472

473+
function normalizeTaskPacketRecord(taskPacket, now) {
474+
if (!taskPacket || typeof taskPacket !== 'object') return null;
475+
const objective = taskPacket.objective && typeof taskPacket.objective === 'object' ? taskPacket.objective : {};
476+
const branch = taskPacket.branch && typeof taskPacket.branch === 'object' ? taskPacket.branch : {};
477+
const pullRequest = taskPacket.pullRequest && typeof taskPacket.pullRequest === 'object' ? taskPacket.pullRequest : {};
478+
const checks = taskPacket.checks && typeof taskPacket.checks === 'object' ? taskPacket.checks : {};
479+
const helperSurface = taskPacket.helperSurface && typeof taskPacket.helperSurface === 'object' ? taskPacket.helperSurface : {};
480+
const recentEvents = Array.isArray(taskPacket.recentEvents) ? taskPacket.recentEvents : [];
481+
const evidence = taskPacket.evidence && typeof taskPacket.evidence === 'object' ? taskPacket.evidence : {};
482+
const artifacts = taskPacket.artifacts && typeof taskPacket.artifacts === 'object' ? taskPacket.artifacts : {};
483+
return {
484+
schema: TASK_PACKET_SCHEMA,
485+
generatedAt: normalizeText(taskPacket.generatedAt) || toIso(now),
486+
cycle: Number.isInteger(taskPacket.cycle) ? taskPacket.cycle : null,
487+
laneId: normalizeText(taskPacket.laneId) || null,
488+
status: normalizeText(taskPacket.status).toLowerCase() || 'ready',
489+
source: normalizeText(taskPacket.source) || null,
490+
objective: {
491+
summary: normalizeText(objective.summary) || null,
492+
source: normalizeText(objective.source) || null
493+
},
494+
branch: {
495+
name: normalizeText(branch.name) || null,
496+
forkRemote: normalizeText(branch.forkRemote) || null,
497+
status: normalizeText(branch.status) || null,
498+
trackingRef: normalizeText(branch.trackingRef) || null,
499+
checkoutPath: normalizeText(branch.checkoutPath) || null
500+
},
501+
pullRequest: {
502+
url: normalizeText(pullRequest.url) || null,
503+
status: normalizeText(pullRequest.status) || null
504+
},
505+
checks: {
506+
status: normalizeText(checks.status) || null,
507+
blockerClass: normalizeText(checks.blockerClass) || null
508+
},
509+
helperSurface: {
510+
preferred: Array.isArray(helperSurface.preferred)
511+
? helperSurface.preferred.map((entry) => String(entry))
512+
: [],
513+
fallbacks: Array.isArray(helperSurface.fallbacks)
514+
? helperSurface.fallbacks.map((entry) => String(entry))
515+
: []
516+
},
517+
recentEvents,
518+
evidence,
519+
artifacts
520+
};
521+
}
522+
436523
function buildActiveLaneSummary(laneRecord) {
437524
if (!laneRecord) return null;
438525
return {
@@ -446,6 +533,7 @@ function buildActiveLaneSummary(laneRecord) {
446533
worker: summarizeWorker(laneRecord.worker),
447534
workerReady: summarizeWorkerReady(laneRecord.workerReady),
448535
workerBranch: summarizeWorkerBranch(laneRecord.workerBranch),
536+
taskPacket: summarizeTaskPacket(laneRecord.taskPacket),
449537
updatedAt: laneRecord.updatedAt
450538
};
451539
}
@@ -534,6 +622,7 @@ function buildLaneRecord(options, now) {
534622
const worker = normalizeWorkerRecord(options.worker, now);
535623
const workerReady = normalizeWorkerReadyRecord(options.workerReady, now);
536624
const workerBranch = normalizeWorkerBranchRecord(options.workerBranch, now);
625+
const taskPacket = normalizeTaskPacketRecord(options.taskPacket, now);
537626
return {
538627
schema: LANE_SCHEMA,
539628
laneId,
@@ -554,6 +643,7 @@ function buildLaneRecord(options, now) {
554643
worker,
555644
workerReady,
556645
workerBranch,
646+
taskPacket,
557647
createdAt: toIso(now),
558648
updatedAt: toIso(now)
559649
};
@@ -788,6 +878,8 @@ async function runStepAction(context) {
788878
workerReadyArtifactPath: laneRecord?.workerReady?.artifacts?.lanePath ?? null,
789879
workerBranchPath: laneRecord?.workerBranch?.artifacts?.latestPath ?? null,
790880
workerBranchArtifactPath: laneRecord?.workerBranch?.artifacts?.lanePath ?? null,
881+
taskPacketPath: laneRecord?.taskPacket?.artifacts?.latestPath ?? null,
882+
taskPacketHistoryPath: laneRecord?.taskPacket?.artifacts?.historyPath ?? null,
791883
statePath: runtimePaths.statePath,
792884
eventsPath: runtimePaths.eventsPath
793885
}
@@ -813,6 +905,7 @@ async function runStepAction(context) {
813905
report.worker = summarizeWorker(laneRecord?.worker);
814906
report.workerReady = summarizeWorkerReady(laneRecord?.workerReady);
815907
report.workerBranch = summarizeWorkerBranch(laneRecord?.workerBranch);
908+
report.taskPacket = summarizeTaskPacket(laneRecord?.taskPacket);
816909
report.state = state;
817910
} finally {
818911
const leaseId = report.lease?.acquire?.leaseId ?? null;

0 commit comments

Comments
 (0)