Releases: deepmodel-ai/dmx
Releases · deepmodel-ai/dmx
Release list
v0.4.2
Fixed
- (GH-40) The folder-shaped
{name}/SKILL.mdfallback (GH-27 phase 4) now applies to every skill-resolution tier, not just shared sources: a project's own.dmx/skills/and dmx's bundledskills/directory both get the same flat-then-folder-shaped lookup. - (GH-40 review)
get_skill_definition's skill name is now validated as a plain slug before any filesystem lookup, closing a path-traversal gap that let a../-laden or absolute name resolve to arbitrary files outside the intended skills directories.
See CHANGELOG.md for full details.
v0.4.1
Fixed
- GH-36:
find_active_runno longer mistakes a skill's own JSON artifact (e.g.validatewritingvalidation-report.json) for a second, permanently-non-terminal loop run. Previously, a correctvalidaterun could raiseAmbiguousActiveRunon everyloop_advance/loop_continuecall. - GH-37:
check_pr_ready'smemory_updatedcheck no longer fails on the loop runtime's own uncommitted.dmx/jobs/writes. Previously, a goodreleaseloop (PR opened, memory bank committed) could finish with a misleadingwarningoutcome purely because its own paused job-state JSON was dirty.
Full Changelog: v0.4.0...v0.4.1
v0.4.0
[0.4.0] — 2026-09-09
Added
- (Phase 1 of GH-27, org-wide shared sources) A new
.dmx/shared-sources.yamlfile lets a repo declare one or more org-wide shared sources (git::<url>[//<subdir>]?ref=<tag|branch|sha>, Terraform's module-source convention) that resolve as a new tier — after the app repo, before the bundled fallback — for loops (_resolve_loop), skills (_resolve_skill), and validators (resolve_validator_path). Declared-list order is the precedence order among multiple shared sources. This phase wires the resolver tier only; it reads directly from.dmx/vendor/{name}/if already populated, but nothing yet clones/fetches a source into that location — that's the/dmx/syncskill, landing in a follow-up phase. A repo with no.dmx/shared-sources.yamlbehaves exactly as it did before this change — fully backward compatible, zero risk to existing resolution behavior. - (Phase 2 of GH-27, org-wide shared sources) New
/dmx/syncskill: clones each source declared in.dmx/shared-sources.yamlat its pinnedref, copies its tree (not a nested git repo/submodule — a plain copy tracked by the app repo's own git history) into.dmx/vendor/{name}/, and writes a per-source.lock.jsonrecording the resolved commit SHA, before committing and pushing the result. Blocked from running directly on the configuredbranch_base, matching every other write path in dmx that requires a reviewed PR. Clone/checkout/shape failures are reported per source with one of three distinguishable causes (auth/URL, missing ref, wrong-shaped source) rather than a generic git error. Backed by a new deterministic MCP tool,sync_shared_sources(dmx.sync_runner), for the same reason validators and loop orchestration stay out of the agent's hands — no LLM judgment is needed for cloning and copying files. - (Phase 3 of GH-27, org-wide shared sources)
/dmx/syncnow detects and warns about same-name collisions across the app repo's own.dmx/loops//.dmx/skills//validators/and every declared shared source, checked per category and inshared_sourcesdeclared-order (the same order the resolvers use to pick a winner) — e.g. two shared sources both definingspec.yaml, or a local skill accidentally shadowing an org-wide one. A collision is a warning, not a failure: sync and commit still proceed, surfaced in/dmx/sync's output (and therefore the PR diff) so an unintended shadow doesn't go unnoticed. (detect_collisionsindmx.sync_runner) - (Phase 4 of GH-27, org-wide shared sources)
_resolve_skillnow recognizes the{name}/SKILL.mdfolder shape (the agentskills.io / Claude Code ecosystem convention —SKILL.mdplus optionalscripts//references//assets/) as a fallback within a shared source, after dmx's own flat{name}.md— so an org can pointshared_sourcesdirectly at an already-standards-shaped skills repo with zero dmx-specific restructuring. When a folder-shaped skill resolves,get_skill_definitionprefixes the returned instructions with the skill's on-disk root path (e.g..dmx/vendor/{source}/skills/{name}/) so the agent can resolvescripts//references//assets/paths mentioned in the body directly, and surfaces anydependencies:declared in frontmatter as an explicit note rather than silently dropping it. Vendored content is never copied into.dmx/skills/or emitted into an IDE's native skills directory — see GH-27 for why both were rejected. - (Phase 5 of GH-27, org-wide shared sources) Docs: new README "Shared sources" section covering the resolution tier,
.dmx/shared-sources.yamlconfig,/dmx/sync, dual-format skill support, and the enterprise/private-repo auth story; Roadmap checklist entry marked done. This closes out GH-27 — all five phases (resolver plumbing,/dmx/syncvendoring, collision detection, dual-format skills, docs) are now implemented.
Fixed
- (GH-27 hardening, found in post-implementation review)
detect_collisionsnow correctly compares skills by their logical name instead of a raw filename glob, so it catches two cases the phase-3 implementation missed once phase 4 added dual-format skill support: two sources both declaring the same folder-shaped{name}/SKILL.mdskill (a directory never matched theskills/*.mdglob at all), and a flat{name}.mdin one source colliding with a folder-shaped ordmx--prefixed version of the same logical skill in another — both of which do collide at actual_resolve_skilltime and were previously shadowed with no warning. - (GH-27 hardening)
.dmx/shared-sources.yamlentries now validatename(must be a plain slug — letters, digits,_,-, not leading with-) andsubdir(must be relative, no..segment) at parse time, instead of trusting them as literal path segments. Previously, a typo'd or maliciousname/subdircould makesync_source'sshutil.rmtree/vendoring, or a resolver'ssource_root, operate on a path outside.dmx/vendor/entirely — most notably asubdirstarting with/, whichPath.__truediv__silently resolves as absolute, discarding the intended base path completely. - (GH-27 hardening)
/dmx/syncnow fails fast, per-source, if any entry under a shared source'sskills/directory is neither a flat{name}.mdfile nor a{name}/SKILL.mdfolder (e.g. a stray non-Markdown file, or a folder with noSKILL.mdinside) — previously such an entry vendored silently and then simply never resolved, with no diagnostic pointing at which entry was wrong. Hidden entries (.gitkeep,.gitignore,.DS_Store, and similar repo-hygiene artifacts extremely common at the top of any real directory) are skipped rather than flagged — found immediately in a follow-up review of this same check, since it would otherwise fail an entirely well-formed source over something unrelated to whether its skills resolve. Vendoring also now strips a.gitdirectory at any depth in the copied tree (shutil.copytree(..., ignore=shutil.ignore_patterns(".git"))), not just the top-level one — defensive, since git itself already refuses to track a path literally named.gitat any depth, so this can't currently be hit through normal git usage, but it costs nothing to guard directly at the copy step rather than relying on that as the only line of defense.
v0.3.4
Fixed
- Loop run state (
.dmx/jobs/{job_id}/*.json) and the session-note breadcrumb_finish_loopwrites toactiveContext.mdare now committed once a loop run genuinely finishes (notpaused/iterating), instead of being left as an uncommitted, local-only change — matching the README's documented "committed with the PR" contract forjobs/. This mattered most for the bundledreleaseloop, which has noon_completechain target: nothing downstream ran to commit its final state, andclose-ticketexplicitly makes no.dmx/changes before force-deleting the branch, so the loop's own "complete" outcome was silently and permanently lost every time. Silent no-op outside a git repo or when there's nothing to commit; if a commit is attempted but fails (e.g. rejected by a pre-commit hook), the loop's own response now surfaces a warning instead of only logging it server-side, and every git call is timeout-bounded so a hunggit commit(e.g. waiting on a GPG passphrase) can't hang the whole MCP tool call. (GH-23) close-ticketno longer transitions the ticket to Done/Complete (or adds the "PR merged" comment) unless Step 4 actually found a merged PR for the branch — previously the transition step ran unconditionally, so running/dmx/close-ticketwhile a PR was still open could mark the ticket Done before the code had actually merged. Also hardened the transition itself to treat an already-terminal ticket/issue as a no-op success rather than an error (idempotent forgithub-issues' auto-close-via-Closes #N, and for Jira setups where a GitHub↔Jira integration already auto-transitioned the ticket). (GH-21)draft-release-notenow commits and pushes.dmx/releases/{version}.mdafter writing it, instead of leaving it as an uncommitted local file.release-mergeopens its PR straight from{branch_base}'s pushed state, so the release notes file was previously silently absent from that PR's diff whenever it hadn't been committed by hand first. Re-running it against unchanged content (nothing new merged) is a no-op rather than failing on "nothing to commit". (GH-22)- Starting the
specloop, or running/dmx/create-ticketmanually, on a repository with zero commits now surfaces a clear, actionable error instead of a confusing one. Previously,current_branch()(which shells out togit rev-parse --abbrev-ref HEAD) silently returnedNoneon a freshlygit init'd repo's unbornHEAD, causing thespecloop's branch guard to fail with a generic "could not determine the current git branch" message — even though the branch name itself was perfectly resolvable._branch_guard_errornow distinguishes this specific case and tells the user to commit and push before retrying.dmx-create-ticket.mdgained the same check as its new Step 2, so manual/foreground usage fails fast before creating a ticket or attempting to branch, rather than failing later when GitHub'screate_branchAPI rejects branching from a ref-less remote. (GH-19)
v0.3.3
Fixed
- The bundled
releaseloop no longer runsupdate-memoryaftercreate-prhas already opened the PR —create-pralready performs its own memory-bank sync and commit (Steps 4-5), so chainingupdate-memoryimmediately after left its edits (further inbox promotions,activeContext.mdrewrites) as dangling uncommitted changes never included in the PR, and silently discarded later byclose-ticket's branch deletion.release.yamlnow runscreate-pronly. update-memorynow commits its own changes at the end of its instructions, so it can never leave dangling uncommitted.dmx/state regardless of when or how it's invoked in the future.check_pr_ready'smemory_updatedcheck now fails if.dmx/has any uncommitted changes (staged or not), instead of only checking the latest commit or the mere existence ofactiveContext.md— a loop-config ordering mistake now surfaces loudly instead of silently passing. (GH-15)
Full Changelog: v0.3.2...v0.3.3
v0.3.2
Fixed
- Loop state is now isolated per branch/ticket instead of tracked through a single global
.dmx/loop-state.jsonpointer, which was silently overwritten by anyrun_loopcall regardless of branch — pausing work on one branch and running a loop on another could lose or corrupt the paused run's state. The active run is now derived by scanning.dmx/jobs/{job_id}/for the one non-terminal state file, keyed off the current branch/ticket;.dmx/loop-state.jsonno longer exists. - The
specloop (which creates a brand new ticket and branch) can now only be started from the configured integration branch (branch_base) — declared via the newrequire_branch: baseloop-config field — and always starts under a temporary job id rather than resolving one from a stalespec.md/branch left over from the previous ticket. This prevented a new ticket's spec state from being written into the previous ticket's job folder. - Starting a
require_branchloop (e.g.spec) while a previous run of it is still pending under an unresolved job id is now rejected up front with a clear message, instead of silently creating a second pending job folder that would only surface later as an opaque "ambiguous active run" error. on_completechaining into a loop the branch guard blocks (e.g. a custom config chaining intospecfrom off its base branch) no longer reports "chaining automatically" and then immediately contradicts it with a rejection — the finished loop's own outcome is now reported plainly alongside the blocked-chain reason.
Full Changelog: v0.3.1...v0.3.2
v0.3.1
Fixed
workspace_rootresolution no longer silently falls back to the filesystem root,$HOME, or an unrelated directory when an MCP client doesn't report roots (or the server process has an unrelated cwd). Auto-detected roots must now contain a.gitor.dmxmarker, or resolution fails loudly instead of guessing — previously this could cause loop state files to be written outside the intended project, with job folder names sometimes wrong or incomplete (e.g.maininstead of the actual ticket/branch).setup_ide_rules(the/dmx-initbootstrap call) still succeeds on brand-new, marker-less projects — the marker check above is skipped for this pre-.git/.dmxcall specifically.- Explicit
workspace_rootarguments must now be an absolute path (~is expanded); relative values are rejected instead of being silently resolved against the server process's cwd.
Full Changelog: v0.3.0...v0.3.1
v0.3.0 — Loop runtime
Added
- Loop runtime — a declarative execution engine that runs an ordered sequence of skills autonomously with automated validators and policy-driven proceed/pause decisions, replacing manual skill-by-skill orchestration for trusted workflows.
- Loop config schema — declarative YAML with
skills,trigger,goal_state,repeat_until,validators,failure_handling,on_optional_failure,human_gate, andon_completeauto-chaining. Bundled loops ship insrc/dmx/loops/(spec,plan,dev,validate,release); teams override via.dmx/loops/. - Validator runner — validators are plain Python functions in
validators/{name}.py, resolved deterministically and invoked via subprocess with a{skill_outputs, goal_state, loop_context}input contract and a{pass, message, checks}output contract. - Bundled validators —
check_spec_complete,check_plan_complete,check_pr_ready,run_tests, andspec_adherence.spec_adherencegrades a structuredvalidation-report.jsonartifact produced by thevalidateskill's diff-based analysis, rather than the agent's free-text self-report. - State machine —
running → paused → running → ... → complete/failed/iterating, persisted to.dmx/loop-state.jsonand per-run job state under.dmx/jobs/{job_id}/. get_skill_definitionMCP tool — fetches a skill's full instructions on demand at each loop skill boundary.run_loop,loop_advance,loop_continueMCP tools and thedmx-run-loop/dmx-loop-continueskills exposing the loop runtime as first-class workflow entry points.- Loop-level memory hooks — loops read
activeContext.mdbefore running and write session-note breadcrumbs after completion.
Fixed
check_spec_complete'sqa_answeredcheck now recognizes questions structurally (numbered-list orQ:markers) instead of requiring one exact answer label, so it doesn't silently fail against every spec.md the bundleddmx-create-ticketskill actually generates.
Full changelog: https://github.com/deepmodel-ai/dmx/blob/main/CHANGELOG.md#030--2026-08-25