diff --git a/docs/agent-handoffs/incidents/INCIDENT_PROMPT-CONTRACT-FAILURES_2026-02-15T01:03:31Z.md b/docs/agent-handoffs/incidents/INCIDENT_PROMPT-CONTRACT-FAILURES_2026-02-15T01-03-31Z.md similarity index 100% rename from docs/agent-handoffs/incidents/INCIDENT_PROMPT-CONTRACT-FAILURES_2026-02-15T01:03:31Z.md rename to docs/agent-handoffs/incidents/INCIDENT_PROMPT-CONTRACT-FAILURES_2026-02-15T01-03-31Z.md diff --git a/memory-updates/2026-02-22T16:59:21Z__docs-alignment-agent.md b/memory-updates/2026-02-22T16-59-21Z__docs-alignment-agent.md similarity index 100% rename from memory-updates/2026-02-22T16:59:21Z__docs-alignment-agent.md rename to memory-updates/2026-02-22T16-59-21Z__docs-alignment-agent.md diff --git a/scripts/agent/run-scheduler-cycle.mjs b/scripts/agent/run-scheduler-cycle.mjs index 1df3b64..ae80c4a 100644 --- a/scripts/agent/run-scheduler-cycle.mjs +++ b/scripts/agent/run-scheduler-cycle.mjs @@ -889,6 +889,7 @@ async function main() { schedulerRunState.lock_deferral = null; await writeRunState(runStatePath, schedulerRunState); const runArtifactSince = new Date().toISOString(); + const runArtifactTimestamp = ts(); const runStartMs = Date.parse(runArtifactSince); const outputChunks = []; @@ -904,6 +905,7 @@ async function main() { SCHEDULER_CADENCE: cadence, SCHEDULER_PROMPT_PATH: promptPath, SCHEDULER_MEMORY_FILE: memoryFile, + SCHEDULER_RUN_TIMESTAMP: runArtifactTimestamp, }; if (schedulerConfig.memoryPolicy.retrieveCommand) { diff --git a/scripts/agent/verify-run-artifacts.mjs b/scripts/agent/verify-run-artifacts.mjs index a70cf6d..bb45341 100644 --- a/scripts/agent/verify-run-artifacts.mjs +++ b/scripts/agent/verify-run-artifacts.mjs @@ -133,6 +133,14 @@ function getMatchingArtifacts(files, { expectedPrefix, session }) { return files.filter((file) => file.name.startsWith(expectedPrefix)); } +function findInvalidFilenameCharacters(files) { + // Windows-invalid filename characters: <>:"/\|?* + const invalidCharRe = /[<>:"/\\|?*]/; + return files + .filter((file) => invalidCharRe.test(file.name)) + .map((file) => file.name); +} + async function hasValidArtifactStructure(files, artifact) { for (const file of files) { const content = await fs.readFile(file.filePath, 'utf8').catch(() => ''); @@ -336,6 +344,11 @@ async function main() { continue; } + const invalidFilenames = findInvalidFilenameCharacters(matchingArtifacts); + if (invalidFilenames.length) { + missing.push(`${artifact.dir}: invalid filename characters for cross-platform sync in ${invalidFilenames.join(', ')}`); + } + const hasValidStructure = await hasValidArtifactStructure(matchingArtifacts, artifact); if (!hasValidStructure) { missing.push(`${artifact.dir}: ${artifact.structureHint}`); diff --git a/src/context/CONTEXT_2026-02-15T01:03:31Z.md b/src/context/CONTEXT_2026-02-15T01-03-31Z.md similarity index 100% rename from src/context/CONTEXT_2026-02-15T01:03:31Z.md rename to src/context/CONTEXT_2026-02-15T01-03-31Z.md diff --git a/src/context/CONTEXT_2026-02-22T16:59:21Z.md b/src/context/CONTEXT_2026-02-22T16-59-21Z.md similarity index 100% rename from src/context/CONTEXT_2026-02-22T16:59:21Z.md rename to src/context/CONTEXT_2026-02-22T16-59-21Z.md diff --git a/src/decisions/DECISIONS_2026-02-15T01:03:31Z.md b/src/decisions/DECISIONS_2026-02-15T01-03-31Z.md similarity index 100% rename from src/decisions/DECISIONS_2026-02-15T01:03:31Z.md rename to src/decisions/DECISIONS_2026-02-15T01-03-31Z.md diff --git a/src/decisions/DECISIONS_2026-02-22T16:59:21Z.md b/src/decisions/DECISIONS_2026-02-22T16-59-21Z.md similarity index 100% rename from src/decisions/DECISIONS_2026-02-22T16:59:21Z.md rename to src/decisions/DECISIONS_2026-02-22T16-59-21Z.md diff --git a/src/prompts/scheduler-flow.md b/src/prompts/scheduler-flow.md index 70c4bb0..e79d804 100644 --- a/src/prompts/scheduler-flow.md +++ b/src/prompts/scheduler-flow.md @@ -26,6 +26,8 @@ Every agent prompt invoked by the schedulers (daily/weekly) MUST enforce this co - `docs/agent-handoffs/README.md` - Recent notes in `docs/agent-handoffs/learnings/` and `docs/agent-handoffs/incidents/` 2. **Update run artifacts** in `src/context/`, `src/todo/`, `src/decisions/`, and `src/test_logs/` during the run, or explicitly document why each artifact update is not needed for that run. + - When creating filenames with ``, use the scheduler-provided `SCHEDULER_RUN_TIMESTAMP` value when available. + - Required filename-safe format: `YYYY-MM-DDTHH-MM-SSZ` (for example `2026-02-15T01-03-31Z`); never use `:` in filenames. 3. **Capture reusable failures and unresolved issues**: - Record reusable failures in `docs/agent-handoffs/incidents/` - Record active unresolved reproducible items in `KNOWN_ISSUES.md` diff --git a/src/test_logs/TEST_LOG_2026-02-15T01:03:31Z.md b/src/test_logs/TEST_LOG_2026-02-15T01-03-31Z.md similarity index 100% rename from src/test_logs/TEST_LOG_2026-02-15T01:03:31Z.md rename to src/test_logs/TEST_LOG_2026-02-15T01-03-31Z.md diff --git a/src/test_logs/TEST_LOG_2026-02-22T16:59:21Z.md b/src/test_logs/TEST_LOG_2026-02-22T16-59-21Z.md similarity index 100% rename from src/test_logs/TEST_LOG_2026-02-22T16:59:21Z.md rename to src/test_logs/TEST_LOG_2026-02-22T16-59-21Z.md diff --git a/src/todo/TODO_2026-02-15T01:03:31Z.md b/src/todo/TODO_2026-02-15T01-03-31Z.md similarity index 100% rename from src/todo/TODO_2026-02-15T01:03:31Z.md rename to src/todo/TODO_2026-02-15T01-03-31Z.md diff --git a/src/todo/TODO_2026-02-22T16:59:21Z.md b/src/todo/TODO_2026-02-22T16-59-21Z.md similarity index 100% rename from src/todo/TODO_2026-02-22T16:59:21Z.md rename to src/todo/TODO_2026-02-22T16-59-21Z.md diff --git a/task-logs/daily/2026-02-15T01:03:31Z__onboarding-audit-agent__completed.md b/task-logs/daily/2026-02-15T01-03-31Z__onboarding-audit-agent__completed.md similarity index 100% rename from task-logs/daily/2026-02-15T01:03:31Z__onboarding-audit-agent__completed.md rename to task-logs/daily/2026-02-15T01-03-31Z__onboarding-audit-agent__completed.md diff --git a/task-logs/daily/2026-02-15T01:22:33Z__perf-agent__completed.md b/task-logs/daily/2026-02-15T01-22-33Z__perf-agent__completed.md similarity index 100% rename from task-logs/daily/2026-02-15T01:22:33Z__perf-agent__completed.md rename to task-logs/daily/2026-02-15T01-22-33Z__perf-agent__completed.md diff --git a/task-logs/daily/2026-02-22T16:59:21Z__docs-alignment-agent__completed.md b/task-logs/daily/2026-02-22T16-59-21Z__docs-alignment-agent__completed.md similarity index 100% rename from task-logs/daily/2026-02-22T16:59:21Z__docs-alignment-agent__completed.md rename to task-logs/daily/2026-02-22T16-59-21Z__docs-alignment-agent__completed.md diff --git a/test/verify-run-artifacts.test.mjs b/test/verify-run-artifacts.test.mjs index c280cb0..69a4ea2 100644 --- a/test/verify-run-artifacts.test.mjs +++ b/test/verify-run-artifacts.test.mjs @@ -102,3 +102,25 @@ test('fails when failure identifiers are not cross-linked to known issues or inc assert.equal(result.code, 1); assert.match(result.stderr, /not cross-linked/i); }); + +test('fails when artifact filenames contain Windows-invalid characters', async () => { + const root = await setupFixture(); + await writeArtifacts(root, { includeMetadata: true }); + await fs.writeFile(path.join(root, 'KNOWN_ISSUES.md'), '_No active issues currently documented._\n', 'utf8'); + + const validPath = path.join(root, 'src', 'test_logs', 'TEST_LOG_1.md'); + const invalidPath = path.join(root, 'src', 'test_logs', 'TEST_LOG_2026-02-15T01:03:31Z.md'); + const content = await fs.readFile(validPath, 'utf8'); + await fs.writeFile(invalidPath, content, 'utf8'); + await fs.unlink(validPath); + + const result = await runVerify(root, [ + '--agent', 'docs-agent', + '--cadence', 'daily', + '--prompt-path', 'src/prompts/daily/docs-agent.md', + ]); + + assert.equal(result.code, 1); + assert.match(result.stderr, /invalid filename characters/i); + assert.match(result.stderr, /TEST_LOG_2026-02-15T01:03:31Z\.md/); +});