Problem
find_active_run (src/dmx/loop_state.py) decides which loop is active by globbing every *.json file under .dmx/jobs/{job_id}/ and keeping any file whose status is not complete or failed:
for path in sorted(job_dir.glob("*.json")):
data = json.loads(path.read_text(encoding="utf-8"))
if data.get("status") in _TERMINAL_STATUSES:
continue
candidates.append((data.get("loop_name", ""), data.get("task_id", "")))
Missing status is treated as non-terminal.
The bundled validate skill is required to write a sidecar artifact at exactly that path: .dmx/jobs/{job_id}/validation-report.json. spec_adherence (src/dmx/validators/spec_adherence.py) reads that file. The expected shape is commit SHA, scope items, regressions, and edge cases — not loop runtime state. There is no status, loop_name, or task_id.
So a correct validate run leaves two JSON files in the job directory:
validate-{task_id}.json — the real run (running / paused)
validation-report.json — an artifact with no status
loop_advance / loop_continue then see two non-terminal runs and raise AmbiguousActiveRun (or stall until a human edits the artifact). The workaround in this trial was to stamp "status": "complete" onto validation-report.json, which is a lie: that file is not a loop.
This showed up on the bundled spec → plan → dev → validate → release path with no custom loops or extra files. Job gh-1 in tasks-api-scrap.
Related: the GH-9 change that removed .dmx/loop-state.json and made the job directory the source of truth. That scan never excluded skill artifacts that the runtime itself tells the agent to write there (loop_tools._skill_instruction even names validation-report.json as the example).
Proposal
Not mutually exclusive:
- Only treat files that look like loop state as runs. Require
loop_name, task_id, and status before a JSON file is a candidate. Anything else is ignored. Cheap, matches the {loop_name}-{task_id}.json naming already used by write_state.
- Do not write artifacts into the run directory. Put
validation-report.json under .dmx/jobs/{job_id}/artifacts/ (or beside .dmx/ outside jobs/). Update spec_adherence and dmx-validate.md Step 9.
- If the glob stays, exclude known artifact names (
validation-report.json). Weaker than (1); the next sidecar will hit the same bug.
(1) is the real fix. (2) is the cleaner layout. Tests should start a validate loop, write a report without status, and assert find_active_run still returns the validate run — not AmbiguousActiveRun.
Out of scope
- Changing the report's scope/edge-case schema
- Warning-vs-success chaining on
dev / coverage_threshold
Acceptance criteria
Context
src/dmx/loop_state.py — find_active_run
src/dmx/loop_tools.py — _skill_instruction (artifact path)
src/dmx/validators/spec_adherence.py
src/dmx/skills/workflow/4-validate/dmx-validate.md Step 9
Problem
find_active_run(src/dmx/loop_state.py) decides which loop is active by globbing every*.jsonfile under.dmx/jobs/{job_id}/and keeping any file whosestatusis notcompleteorfailed:Missing
statusis treated as non-terminal.The bundled
validateskill is required to write a sidecar artifact at exactly that path:.dmx/jobs/{job_id}/validation-report.json.spec_adherence(src/dmx/validators/spec_adherence.py) reads that file. The expected shape is commit SHA, scope items, regressions, and edge cases — not loop runtime state. There is nostatus,loop_name, ortask_id.So a correct validate run leaves two JSON files in the job directory:
validate-{task_id}.json— the real run (running/paused)validation-report.json— an artifact with nostatusloop_advance/loop_continuethen see two non-terminal runs and raiseAmbiguousActiveRun(or stall until a human edits the artifact). The workaround in this trial was to stamp"status": "complete"ontovalidation-report.json, which is a lie: that file is not a loop.This showed up on the bundled
spec → plan → dev → validate → releasepath with no custom loops or extra files. Jobgh-1intasks-api-scrap.Related: the GH-9 change that removed
.dmx/loop-state.jsonand made the job directory the source of truth. That scan never excluded skill artifacts that the runtime itself tells the agent to write there (loop_tools._skill_instructioneven namesvalidation-report.jsonas the example).Proposal
Not mutually exclusive:
loop_name,task_id, andstatusbefore a JSON file is a candidate. Anything else is ignored. Cheap, matches the{loop_name}-{task_id}.jsonnaming already used bywrite_state.validation-report.jsonunder.dmx/jobs/{job_id}/artifacts/(or beside.dmx/outsidejobs/). Updatespec_adherenceanddmx-validate.mdStep 9.validation-report.json). Weaker than (1); the next sidecar will hit the same bug.(1) is the real fix. (2) is the cleaner layout. Tests should start a validate loop, write a report without
status, and assertfind_active_runstill returns the validate run — notAmbiguousActiveRun.Out of scope
dev/coverage_thresholdAcceptance criteria
find_active_runignores.dmx/jobs/{job_id}/validation-report.jsoneven when that file has nostatusloop_advanceafter the skill writes the report, without editing the reportAmbiguousActiveRunspec_adherencestill finds the reportContext
src/dmx/loop_state.py—find_active_runsrc/dmx/loop_tools.py—_skill_instruction(artifact path)src/dmx/validators/spec_adherence.pysrc/dmx/skills/workflow/4-validate/dmx-validate.mdStep 9