Merge pull request #363 from PR0M3TH3AN/print-scheduler-summary-6512839814829601974

Print Final Run Summary in Scheduler Cycle
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-02-20 07:47:07 -05:00
commit 45c0e5bbd7
4 files changed
+216 -16

No files matched your search

+165 -16
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env node
// Source of truth: numbered MUST steps 2 and 4-14 in src/prompts/scheduler-flow.md are
// Source of truth: numbered MUST steps 2 and 4-16 in src/prompts/scheduler-flow.md are
// implemented by this script; step 3 (policy-file read) is best-effort and non-fatal.
import fs from 'node:fs/promises';
import path from 'node:path';
@@ -370,6 +370,45 @@ async function writeLog({ cadence, agent, status, reason, detail, platform, meta
return file;
}
async function printRunSummary({ status, agent, promptPath, reason, detail, memoryFile, platform }) {
let learnings = 'No learnings recorded.';
if (memoryFile) {
try {
const content = await fs.readFile(memoryFile, 'utf8');
if (content.trim()) {
learnings = content.trim();
if (learnings.length > 2000) {
learnings = learnings.slice(0, 2000) + '\n... (truncated)';
}
}
} catch {
// ignore read errors
}
}
process.stdout.write('\n================================================================================\n');
process.stdout.write('Scheduler Run Summary\n');
process.stdout.write('================================================================================\n');
process.stdout.write(`Status: ${status}\n`);
process.stdout.write(`Agent: ${agent}\n`);
process.stdout.write(`Prompt: ${promptPath || '(none)'}\n`);
process.stdout.write(`Platform: ${platform}\n`);
process.stdout.write(`Reason: ${reason}\n`);
if (detail) {
process.stdout.write(`Detail: ${detail}\n`);
}
process.stdout.write('\nLearnings / Discoveries:\n');
process.stdout.write(`${learnings}\n`);
process.stdout.write('================================================================================\n\n');
}
async function exitWithSummary(code, summaryData) {
if (summaryData) {
await printRunSummary(summaryData);
}
process.exit(code);
}
async function main() {
const { cadence, platform, model } = parseArgs(process.argv.slice(2));
if (!VALID_CADENCES.has(cadence)) {
@@ -426,7 +465,13 @@ async function main() {
relay_health_history_path: preflight.payload?.historyPath || null,
},
});
process.exit(allRelaysUnhealthy ? 0 : (preflight.code || 1));
await exitWithSummary(allRelaysUnhealthy ? 0 : (preflight.code || 1), {
status: allRelaysUnhealthy ? 'deferred' : 'failed',
agent: 'scheduler',
promptPath: null,
reason: allRelaysUnhealthy ? 'All relays unhealthy preflight' : 'Lock backend unavailable preflight',
platform,
});
}
}
@@ -468,7 +513,13 @@ async function main() {
platform,
reason: ALL_EXCLUDED_REASON,
});
process.exit(1);
await exitWithSummary(1, {
status: 'failed',
agent: 'scheduler',
promptPath: null,
reason: ALL_EXCLUDED_REASON,
platform,
});
}
const deferralForAgent = schedulerRunState.lock_deferral?.selected_agent === selectedAgent
@@ -533,7 +584,14 @@ async function main() {
lock_idempotency_key: idempotencyKey,
},
});
process.exit(0);
await exitWithSummary(0, {
status: 'deferred',
agent: selectedAgent,
promptPath: null,
reason: 'Lock backend deferred',
platform,
detail: `Lock backend failure (${backendCategory})`,
});
}
schedulerRunState.lock_deferral = null;
@@ -562,7 +620,14 @@ async function main() {
lock_stdout_excerpt: stdoutExcerpt || '(empty)',
},
});
process.exit(2);
await exitWithSummary(2, {
status: 'failed',
agent: selectedAgent,
promptPath: null,
reason: 'Lock backend error',
platform,
detail: `Lock backend error (${backendCategory})`,
});
}
if (lockResult.code !== 0) {
@@ -575,7 +640,13 @@ async function main() {
platform,
reason: 'Failed to acquire lock',
});
process.exit(lockResult.code);
await exitWithSummary(lockResult.code, {
status: 'failed',
agent: selectedAgent,
promptPath: null,
reason: 'Failed to acquire lock',
platform,
});
}
const promptPath = path.resolve(process.cwd(), 'src/prompts', cadence, `${selectedAgent}.md`);
@@ -590,7 +661,14 @@ async function main() {
detail: promptValidation.detail,
metadata: categorizeFailureMetadata(promptValidation.category, { prompt_path: promptPath }),
});
process.exit(1);
await exitWithSummary(1, {
status: 'failed',
agent: selectedAgent,
promptPath,
reason: promptValidation.reason,
detail: promptValidation.detail,
platform,
});
}
schedulerRunState.lock_deferral = null;
@@ -626,7 +704,15 @@ async function main() {
detail: schedulerConfig.memoryPolicy.retrieveCommand,
metadata: categorizeFailureMetadata(FAILURE_CATEGORY.EXECUTION, { failure_class: 'prompt_validation_error' }),
});
process.exit(retrieveResult.code);
await exitWithSummary(retrieveResult.code, {
status: 'failed',
agent: selectedAgent,
promptPath,
reason: 'Memory retrieval command failed',
detail: schedulerConfig.memoryPolicy.retrieveCommand,
memoryFile,
platform,
});
}
}
@@ -645,7 +731,15 @@ async function main() {
detail: 'Handoff callback failed.',
metadata: categorizeFailureMetadata(FAILURE_CATEGORY.EXECUTION, { failure_class: 'prompt_validation_error' }),
});
process.exit(handoff.code);
await exitWithSummary(handoff.code, {
status: 'failed',
agent: selectedAgent,
promptPath,
reason: 'Prompt/handoff execution failed',
detail: 'Handoff callback failed',
memoryFile,
platform,
});
}
} else {
const detail = schedulerConfig.missingHandoffCommandForMode
@@ -660,7 +754,15 @@ async function main() {
detail,
metadata: categorizeFailureMetadata(FAILURE_CATEGORY.EXECUTION, { failure_class: 'prompt_validation_error' }),
});
process.exit(1);
await exitWithSummary(1, {
status: 'failed',
agent: selectedAgent,
promptPath,
reason: 'Prompt/handoff execution failed',
detail,
memoryFile,
platform,
});
}
if (schedulerConfig.memoryPolicy.storeCommand) {
@@ -678,7 +780,15 @@ async function main() {
detail: schedulerConfig.memoryPolicy.storeCommand,
metadata: categorizeFailureMetadata(FAILURE_CATEGORY.EXECUTION, { failure_class: 'prompt_validation_error' }),
});
process.exit(storeResult.code);
await exitWithSummary(storeResult.code, {
status: 'failed',
agent: selectedAgent,
promptPath,
reason: 'Memory storage command failed',
detail: schedulerConfig.memoryPolicy.storeCommand,
memoryFile,
platform,
});
}
}
@@ -709,7 +819,15 @@ async function main() {
detail: `Missing evidence for: ${missingSteps.join(', ')}`,
metadata: categorizeFailureMetadata(FAILURE_CATEGORY.PROMPT_SCHEMA, { failure_class: 'prompt_validation_error' }),
});
process.exit(1);
await exitWithSummary(1, {
status: 'failed',
agent: selectedAgent,
promptPath,
reason: 'Required memory steps not verified',
detail: `Missing evidence for: ${missingSteps.join(', ')}`,
memoryFile,
platform,
});
}
if (missingSteps.length) {
@@ -741,7 +859,15 @@ async function main() {
detail,
metadata: categorizeFailureMetadata(FAILURE_CATEGORY.PROMPT_SCHEMA, { failure_class: 'prompt_validation_error' }),
});
process.exit(artifactCheck.code);
await exitWithSummary(artifactCheck.code, {
status: 'failed',
agent: selectedAgent,
promptPath,
reason: 'Missing required run artifacts',
detail,
memoryFile,
platform,
});
}
for (const validation of schedulerConfig.validationCommands) {
@@ -758,7 +884,15 @@ async function main() {
detail: validation,
metadata: categorizeFailureMetadata(FAILURE_CATEGORY.EXECUTION, { failure_class: 'prompt_validation_error' }),
});
process.exit(result.code);
await exitWithSummary(result.code, {
status: 'failed',
agent: selectedAgent,
promptPath,
reason: 'Validation failed',
detail: validation,
memoryFile,
platform,
});
}
}
@@ -776,11 +910,26 @@ async function main() {
platform,
reason: `Completion publish failed. Retry npm run lock:complete -- --agent ${selectedAgent} --cadence ${cadence} after verifying relay connectivity`,
});
process.exit(completeResult.code);
await exitWithSummary(completeResult.code, {
status: 'failed',
agent: selectedAgent,
promptPath,
reason: 'Completion publish failed',
detail: 'Retry npm run lock:complete after verifying relay connectivity',
memoryFile,
platform,
});
}
await writeLog({ cadence, agent: selectedAgent, status: 'completed', platform, reason: 'Scheduler cycle completed successfully' });
process.exit(0);
await exitWithSummary(0, {
status: 'completed',
agent: selectedAgent,
promptPath,
reason: 'Scheduler cycle completed successfully',
memoryFile,
platform,
});
}
}
@@ -149,6 +149,27 @@ const invariants = [
"status: 'completed'",
],
},
{
name: 'final summary print',
promptSnippets: {
meta: [
'Print a final summary message to stdout',
'Status: [Success/Failure]',
'Learnings: [Content of the memory update file',
],
flow: [
'Print a final summary message to stdout',
'The message MUST include:',
'**Learnings**: [Content of the memory update file',
],
},
schedulerSnippets: [
'async function printRunSummary',
'async function exitWithSummary',
'await printRunSummary(summaryData)',
'Learnings / Discoveries:',
],
},
];
+16
View File
@@ -71,12 +71,19 @@ MUST 3: Run these commands in this order:
- Exit 0: continue.
- Exit non-zero: fail the run, write `_failed.md` with a clear completion-publish failure reason and retry guidance, then stop.
11) Only after step 10 succeeds, write final task log (`_completed.md` for success). For any failure in step 9 or step 10, write `_failed.md`, then stop.
12) Print a final summary message to stdout.
- Status: [Success/Failure]
- Agent: [Agent Name]
- Prompt: [Prompt Path]
- Reason: [Reason string]
- Learnings: [Content of the memory update file or "No learnings recorded"]
Worked example (required order):
- `AGENT_PLATFORM=codex npm run lock:lock -- --agent content-audit-agent --cadence daily`
- execute selected prompt work
- `AGENT_PLATFORM=codex npm run lock:complete -- --agent content-audit-agent --cadence daily` (complete, permanent)
- write `task-logs/daily/<timestamp>__content-audit-agent__completed.md`
- print final summary (Status: Success, Learnings: <content>)
Worked example (failed validation, no completion publish):
- `AGENT_PLATFORM=codex npm run lock:lock -- --agent content-audit-agent --cadence daily`
@@ -84,6 +91,7 @@ MUST 3: Run these commands in this order:
- `npm run lint` exits non-zero (or `npm test` exits non-zero)
- write `task-logs/daily/<timestamp>__content-audit-agent__failed.md` with failure reason
- stop without running `AGENT_PLATFORM=codex npm run lock:complete -- --agent content-audit-agent --cadence daily`
- print final summary (Status: Failed, Reason: <reason>)
MUST 4: If all daily agents are excluded, stop and write `_failed.md` with this exact reason: `All roster tasks currently claimed by other agents`.
```
@@ -155,12 +163,19 @@ MUST 3: Run these commands in this order:
- Exit 0: continue.
- Exit non-zero: fail the run, write `_failed.md` with a clear completion-publish failure reason and retry guidance, then stop.
11) Only after step 10 succeeds, write final task log (`_completed.md` for success). For any failure in step 9 or step 10, write `_failed.md`, then stop.
12) Print a final summary message to stdout.
- Status: [Success/Failure]
- Agent: [Agent Name]
- Prompt: [Prompt Path]
- Reason: [Reason string]
- Learnings: [Content of the memory update file or "No learnings recorded"]
Worked example (required order):
- `AGENT_PLATFORM=codex npm run lock:lock -- --agent bug-reproducer-agent --cadence weekly`
- execute selected prompt work
- `AGENT_PLATFORM=codex npm run lock:complete -- --agent bug-reproducer-agent --cadence weekly` (complete, permanent)
- write `task-logs/weekly/<timestamp>__bug-reproducer-agent__completed.md`
- print final summary (Status: Success, Learnings: <content>)
Worked example (failed validation, no completion publish):
- `AGENT_PLATFORM=codex npm run lock:lock -- --agent bug-reproducer-agent --cadence weekly`
@@ -168,6 +183,7 @@ MUST 3: Run these commands in this order:
- `npm run lint` exits non-zero (or `npm test` exits non-zero)
- write `task-logs/weekly/<timestamp>__bug-reproducer-agent__failed.md` with failure reason
- stop without running `AGENT_PLATFORM=codex npm run lock:complete -- --agent bug-reproducer-agent --cadence weekly`
- print final summary (Status: Failed, Reason: <reason>)
MUST 4: If all weekly agents are excluded, stop and write `_failed.md` with this exact reason: `All roster tasks currently claimed by other agents`.
```
+14
View File
@@ -232,6 +232,18 @@ Every agent prompt invoked by the schedulers (daily/weekly) MUST enforce this co
- `scripts/agent/run-scheduler-cycle.mjs` does **not** run `git commit` or `git push`.
- If commit/push is required for your workflow, perform it in the configured handoff agent command or a separate orchestration step.
16. Print a final summary message to stdout (standard output).
- The message MUST include:
- **Status**: [Success/Failure/Deferred]
- **Agent**: [Agent Name]
- **Prompt**: [Prompt Path]
- **Reason**: [Reason string]
- **Learnings**: [Content of the memory update file or "No learnings recorded"]
- If the run failed, include the failure reason.
- If the run succeeded, try to include the content of the memory update file (e.g., `memory-updates/<timestamp>__<agent>.md`) as the "Learnings".
Worked post-task example (MUST order):
1. `AGENT_PLATFORM=codex npm run lock:lock -- --agent content-audit-agent --cadence daily`
@@ -239,6 +251,7 @@ Worked post-task example (MUST order):
3. `node scripts/agent/verify-run-artifacts.mjs --since <run-start-iso> --check-failure-notes`
4. `AGENT_PLATFORM=codex npm run lock:complete -- --agent content-audit-agent --cadence daily` (complete, permanent)
5. Write `task-logs/daily/2026-02-14T10-00-00Z__content-audit-agent__completed.md`
6. Print final summary (Status: Success, Learnings: <content>)
Worked validation-failure example (MUST behavior):
@@ -248,3 +261,4 @@ Worked validation-failure example (MUST behavior):
4. `npm run lint` exits non-zero (or `npm test` exits non-zero)
5. Write `task-logs/daily/2026-02-14T10-00-00Z__content-audit-agent__failed.md` with the failing command and reason
6. Stop the run **without** calling `npm run lock:complete -- --agent content-audit-agent --cadence daily`
7. Print final summary (Status: Failed, Reason: <reason>)