From 9278dadab0761455ddf0d703323b0d57d9c773d3 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 04:50:26 +0000 Subject: [PATCH] Print final run summary in scheduler cycle and prompts Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- scripts/agent/run-scheduler-cycle.mjs | 181 +++++++++++++++++++-- scripts/validate-scheduler-flow-parity.mjs | 21 +++ src/prompts/META_PROMPTS.md | 16 ++ src/prompts/scheduler-flow.md | 14 ++ 4 files changed, 216 insertions(+), 16 deletions(-) diff --git a/scripts/agent/run-scheduler-cycle.mjs b/scripts/agent/run-scheduler-cycle.mjs index 7a9b873..5dd2ce5 100644 --- a/scripts/agent/run-scheduler-cycle.mjs +++ b/scripts/agent/run-scheduler-cycle.mjs @@ -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, + }); } } diff --git a/scripts/validate-scheduler-flow-parity.mjs b/scripts/validate-scheduler-flow-parity.mjs index 330219a..6b05a1d 100644 --- a/scripts/validate-scheduler-flow-parity.mjs +++ b/scripts/validate-scheduler-flow-parity.mjs @@ -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:', + ], + }, ]; diff --git a/src/prompts/META_PROMPTS.md b/src/prompts/META_PROMPTS.md index 5e4f39b..f3a8c39 100644 --- a/src/prompts/META_PROMPTS.md +++ b/src/prompts/META_PROMPTS.md @@ -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/__content-audit-agent__completed.md` + - print final summary (Status: Success, Learnings: ) 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/__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: ) 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/__bug-reproducer-agent__completed.md` + - print final summary (Status: Success, Learnings: ) 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/__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: ) 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`. ``` diff --git a/src/prompts/scheduler-flow.md b/src/prompts/scheduler-flow.md index 3fc52cf..6aa4548 100644 --- a/src/prompts/scheduler-flow.md +++ b/src/prompts/scheduler-flow.md @@ -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/__.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 --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: ) 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: )