Skip to content

Releases: stuarthu/dsh-crew

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 21 Oct 12:39
  • A job's documents no longer land in your project folder. The opening document, the design,
    the task table, every ADR and CRD, the boundary contracts and the release plans now go in a
    temp folder: <TMP>/dsh-crew/<repo>/<job-slug>/. <TMP> is your system temp folder — /tmp
    when nothing sets it — and <repo> is your project's full path with the slashes written as
    -. Your project folder keeps its code, qa/, and the four files that ship: README.md,
    CHANGELOG.md, CLAUDE.md and principles.md. A job's documents are read while the job runs
    and are kept after it by nothing.
  • The Chinese README is gone. README-zh.md was deleted, and with it the rule that the two
    READMEs say the same thing and go in the same commit. README.md is the one README, in English.
    A job whose language is not English still writes a translated README of its own, and nothing
    keeps that page in step with the English one.
  • The task table is one per job, not one per repository. The temp folder carries the job name,
    so each job starts its own table and never reads an earlier job's.
  • Nothing checks the Verdicts line any more. The PM still writes it, and still prints one line
    per task to you at the end of a job. What is gone is the red: a task with a missing value, or a
    not run with no reason after the dash, reddens nothing. qa/gaps.md says so.

v0.12.0

Choose a tag to compare

@github-actions github-actions released this 15 Oct 14:20

Fixed

  • The Crew preset now appears in the preset picker on dsh 0.2.x, where until
    now it was silently switched off.
    The declaration row was carried by
    @deepseek-ai/dsh-agent-preset and switched itself off with a disabled: line.
    A row's disabled is evaluated against the loader context, which cannot see
    the registry service — only a !!js expression inside a row's config sees that
    plugin's own context. So the expression was true on every dsh, the row was
    disabled on every dsh, and dsh kept a declaration whose activation fails "on the
    roster with its diagnostic" rather than failing the profile. The symptom was a
    missing preset and no error, on the one generation that has no other home for it:
    0.2.x never reads $DSH_HOME/.agent-presets.
    • The row is now carried by host/crew-preset.js, the package's own plugin,
      and it carries no disabled: line at all. The check runs inside that plugin,
      where ctx.get('agentPresets') resolves against the row's declared injections.
    • Nothing changes on dsh 0.1.x. That generation never reads a declaration row;
      the plugin does nothing there, and the folder install is the whole story — the
      same shape as before, now actually reached.
    • The preset's own content did not move: same id: crew, same order, same
      name and description, same plugins: list with the same two deviations.

Changed

  • The job folder moved inside the repository. It used to sit at
    ~/.dsh/crew/jobs/<job-slug>/, one folder per job, far from the project. It is
    now at <repository>/.dsh-crew/jobs/<job-slug>/, at the root of the repository
    this session works in — the same place the job's documents are, so one ls
    shows the job you are in, and a job's progress can no longer be mistaken for
    another repository's.
    Nothing about the job changed shape: the folder is still single-use. The PM
    writes one line into the repository's own .git/info/exclude before it writes
    anything into it, so git status stays clean, the folder is never staged and
    never committed, and it is dropped whole when the job ends. info/exclude and
    not the project's .gitignore, because that line is about where this crew puts
    progress rather than what the project builds, and because it is shared by every
    worktree — so a paired task's two trees see the same rule.
    • Nothing is committed that was not committed before. state.json is
      rewritten after every step; committing it would put a file that changes on
      every step in front of every reviewer and every reader. It stays progress,
      and the record this job leaves is still the one in docs/.
    • The default is now relative, .dsh-crew/jobs, resolved against the folder
      the session runs in. Set jobsDir in the dsh-crew-core row to an absolute
      path (or one starting with ~/) to put the job folder somewhere else, exactly
      as before.
    • The unfinished-job notice is unchanged in behaviour. A job left in the
      folder is still put in front of the PM at the start of every session, and it
      now names the resolved absolute path rather than a ~ one.

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 15 Oct 07:58

Added

  • The Crew preset now exists on dsh 0.2.x, where until now it was silently
    missing.
    dsh 0.2.x replaced the @deepseek-ai/dsh-agent-presets registry
    with @deepseek-ai/dsh-agent-preset, and the old registry's home —
    $DSH_HOME/.agent-presets — is not read at all any more. In its own words:
    the new registry "neither scans directories nor accepts preset paths". A preset
    is now a declarative row in a profile's composition, and a plugin ships one by
    inserting that row from its bundle patch.
    dsh-crew only ever wrote the folder, so on 0.2.x the plugin loaded, the PM
    prompt and the guards worked, and Crew never appeared in the preset picker.
    The plugin's bundle patch now declares the preset too, and the picker offers it
    again.
    • Nothing changes on dsh 0.1.x. The folder install stays exactly as it was,
      and the new row disables itself wherever the old registry is the one loaded,
      so a 0.1.x profile is never asked for a package it does not have.
    • The preset is written twice on purpose. The plugin list differs between
      the two dsh generations — the persona row alone takes text in 0.1.x and
      prefix/suffix in 0.2.x — so neither declaration can be derived from the
      other. A role filter (roleAllow, roleDeny, roleModels) therefore has to
      be set in the file your dsh reads: the preset copy under
      $DSH_HOME/.agent-presets/crew on 0.1.x, and the preset-crew row in the
      plugin's cordis.patch.yml on 0.2.x. Both are commented to say so.
    • tools/verify-mount.mjs holds the new declaration to the same rules as the
      old one: the crew roles are mounted, the subagent-control tools are there, no
      second way to start an agent re-opens, the preset keeps its crew identity,
      and web fetch stays off.

Removed

  • The PM write guard is gone: host/pm-write-guard.js, its row in
    cordis.patch.yml, and its check tools/verify-pm-write-guard.mjs.
    It
    stopped the root session — the PM — from using write and edit on every
    file that was not on a hard-coded whitelist, and asked you to approve each
    such write, one at a time. That whitelist was narrower than the write set
    the PM's own rules give it
    , so the guard kept asking about writes the rules
    already allow — README.md and CHANGELOG.md among them. This removes the
    enforcement, not the rule: roles/pm.md still says which files the PM may
    write. It also reverses docs/design/prd-2026-08-23-pm-write-guard.md.
    • The git guard is untouched, and the plugin now ships one guard. It is
      still what blocks a crew role's push, tag push, force push, remote delete
      and package publishing. SECURITY.md and CLAUDE.md describe that one.
    • If your own profile patch targets the row id dsh-crew-pm-write-guard,
      delete that patch.
      The plugin no longer ships the module that row loaded,
      and a row naming a module the package does not export cannot be resolved.
    • npm test no longer runs tools/verify-pm-write-guard.mjs. Nothing
      else in the suite, and no QA case, depended on it.

Fixed

  • The git guard was not guarding your crew roles at all. It decided who was
    the PM by looking at the tool call's exec.parent, and in dsh that token marks
    a nested sub-call, not a parent agent: it is set for the PM's own nested
    calls and absent for every crew role. So every role read as the PM, and the one
    branch of the guard that trusted the PM let a child push a protected branch,
    push a tag, force push, delete a remote or publish a package straight through.
    The guard now reads the agent's own session: a crew role's session names a
    parent session, the PM's does not. Every child is guarded again, and the guard's
    own check — which had the same mistake and reported every blocked case as a
    pass — now sees children.
    • Nothing about the PM changes. Its git and publishing commands still pass
      straight through, and trustRootAgent: false still guards it exactly like a
      child.
    • If you were relying on a role being able to push, that ability is gone. It
      was never the design: a child's push needs the one-shot approval file the
      user creates, exactly as SECURITY.md describes it.

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 23 Aug 22:12

Added

  • Pushing a v* tag now also creates the GitHub release, not only the npm
    version. Its text is the matching section of CHANGELOG.md — the same words
    you are reading, sent to the place people actually look. The repository had 17
    tags and 0 releases before this, so anyone arriving from npm saw a row of bare
    tags with nothing written on them.
    Four things about how it behaves are worth knowing, because each one was a
    deliberate choice:
    • The notes are read before anything is published. A version with no
      CHANGELOG.md section written for it, or with an empty one, stops the run
      right there and publishes nothing. An npm version cannot be pulled back, so
      the check that can still save you has to happen first.
    • The release is created only when the publish really happened. Re-pushing
      a tag whose version is already on npm skips the publish, and it skips the
      release with it. So if a version ever reaches npm without its release page,
      re-pushing the tag will not repair it — that takes one
      gh release create by hand.
    • Old tags are not getting release pages, with one exception. v0.9.0 was
      given one by hand on 2026-08-22, because it was the newest tag and already
      had a complete section here. The other 16 stay as they are; 8 of those 17
      tags have no section in this file at all, so the pages would have been empty.
    • The code that reads those notes has never been run by a test. That was
      chosen, not overlooked: the shell lives inline in the workflow and no test
      harness executes it, so the first real run is the first v* tag after this
      version. Nothing you install is affected — a mistake there stops a release
      instead of shipping one.
      There is nothing for you to configure, and nothing changes about how you
      install or use the plugin.

Changed

  • A question in the opening interview can now be left undecided. Every
    question the PM puts to you before a job starts has one test put to it first:
    would your answer change what gets built, or what gets released? A question
    that changes neither now arrives with a second option beside the recommended
    one — leave it undecided. Watch where that bar sits: being able to get
    started without hearing from you is not enough on its own to earn the
    option. A question that fails the test is not offered it, and you are told in
    one line what it would change.
    What taking it buys you is the part that matters. The thing you set aside is
    recorded nowhere in the opening document: not in the list of what the
    interview settled, not under "not in scope", not under "still undecided". Ask
    for it a month later and you are reopening nothing, because nothing about it
    was ever written down. This came out of a real case — one command's worth of
    work ran into a line the interview had settled in passing, and it took the
    full change-request road to get past it.
    The change-request rule itself is not loosened. The only thing gone is a
    confirmed line standing in the way. If saying yes to your later ask would move
    the milestone list, a DoD item, or the scope of the work, that is a change of
    scope like any other: the PM still writes the change request, and still stops
    and asks you first.
  • The "not in scope" section is now a short list, and every line on it has a
    real cost.
    A line belongs there only when crossing it would force work that
    is already finished to be built a second time, or do something nobody can take
    back — a package published, a tag pushed, data deleted — or leave a safety
    guard or a permission rule weaker than it was. Work the PM merely did not get
    to stays off the list. It used to go on, and that was the trouble: everything
    in that section reads like a boundary you agreed to, so a cheap request could
    look like you were reversing yourself. The rule is short now — if a thing is on
    that list it is a wall, and if it is not, just ask for it.
  • The QA folder moved from docs/qa/ to the repository root qa/. QA's
    cases are runnable scripts, not documents, so they no longer live under
    docs/. The command that runs them is now bash qa/run-all.sh; the standing
    gap list is qa/gaps.md.
  • The task table became a folder. The single docs/design/tasks.md (one
    long file, every task of every job) is now docs/tasks/ — one T-<n>.md
    file per task, plus README.md for the non-task content. A task's Verdicts
    line and DoD section live in its own file.
  • A researcher's answer no longer enters the repository. docs/research/
    is gone; the researcher answers in its report, and the PM writes what is
    durable into its own documents.

v0.9.0

Choose a tag to compare

@stuarthu stuarthu released this 22 Aug 06:49

Changed

  • The quick lane is gone. A job now picks one of two lanes: ask, for a
    question you want answered, and team, for a change of any size. There used to
    be a third one for a single small change with no design choice in it, where the
    PM edited the file itself — no crew, no documents, and nothing checking the
    result. A typo now takes the same road as a feature: at least one task, one
    round of QA, and one round each of the code review, the security review and the
    doc review. That is slower for a one-word fix, and it is meant to be. What
    the old lane really bought was a way for a change to reach your repository with
    nothing written down about it and no second pair of eyes on it. The rounds it
    used to skip are also much cheaper now, which is the next two entries. A
    milestone is still not a release
    : it means one full cycle and one commit.
    Pushing and tagging sit outside it, and each of them still needs your own yes,
    every time.
  • QA now runs once for the whole milestone, after the coding is finished,
    instead of once per task.
    It was the most expensive step the crew had: one
    round took 13 to 26 minutes in the crew's own last job. A task is now finished
    when its own unit tests pass: the engineer's failing test before the code, the
    passing test after. Nothing else holds it open, because neither a reviewer nor
    a QA round has run yet. That one round also has a new shape, in two steps.
    First one QA agent turns your DoD sections into a list of cases, one line
    each, without reading the code. Then one agent writes each case on that
    list, all of them at the same time. The extra step buys one thing: the side
    being measured does not set the questions.
  • The code review, the security review and the doc review now run once each,
    at the end of the milestone, side by side.
    They used to run on every finished
    task, and a task could reach three rounds of them. Three of those rounds in the
    last job were thrown away, because the code changed after the review had read
    it. Each review now reads only the part that changed. Code nobody touched, a
    document nobody touched and anything outside the milestone's scope stay out of
    it, however much a reviewer dislikes them. And only a change made because of a
    review's own finding brings that review back — a code change re-runs the code
    review, a wording change the doc review, a security change the security review.
    The three never come back together.
  • What those two changes cost you, said plainly, because it is a trade you
    accepted.
    A defect is now found later, with more work already sitting on top
    of it, so fixing it reaches wider. That is not a guess. In the crew's own last
    job, running QA on every task caught two real defects: a cross-reference that
    had been done only halfway, and a rule about dependencies written the wrong way
    round. Held to one round at the end, that kind of thing surfaces after more
    code has been written over it. Nobody downstream may quietly make up for it,
    and no reviewer may widen its one round to compensate. What it demands instead
    is that the single round is a full one: every item of every task's DoD
    section, whatever the test run said.
  • The interview at the start of a job now has a method, and a rule for when it
    stops.
    That step used to be called "Grill" and it ended "when the answers are
    settled", a sentence that never says when that is. It now names six kinds of
    question, and the PM picks the kind by what it is actually missing. Clarify
    what the words point at. Probe an assumption nobody has checked. Ask for the
    reason or the evidence. Ask who would disagree. Ask what the choice drags in
    behind it. Question the request itself. The last one is permission to say
    "I think you may be solving the wrong problem", and it comes early, while
    changing direction is still cheap. Wide questions come before narrow ones,
    because starting narrow only confirms the picture already in the asker's head.
    A leading question — the wanted answer hidden inside the question — is banned:
    when the PM thinks it already knows your answer, it goes and looks it up
    instead of asking. And it stops the moment it could write every section of the
    opening document with no guess left in it, not one question later. The
    reasoning and the outside sources are principle 22 in principles.md, which
    lives in the repository and is not part of the npm package.
  • After the start, you are asked far fewer times. Once the scope and the
    change requests are agreed, the PM decides the rest itself and hands you
    summaries of the documents it produced. You interrupt on the one part you
    care about, instead of answering a question per item while the work waits. In
    the last job the user answered more than twenty separate questions in the first
    half. Two things did not move into the PM's hands. A change outside the agreed
    scope is refused by default — it says what it would cost, and takes it only
    when you name it yourself. And every push, tag, publish, merge and
    branch delete still needs your own yes at the moment it happens.
  • One opening document per job, and its name carries the job.
    docs/design/prd.md is now docs/design/prd-<date>-<job-slug>.md, and the
    design document goes the same way: docs/design/hld.md is now
    docs/design/hld-<date>-<job-slug>.md. With one fixed name, the first line the
    next job wrote used to overwrite the last job's document — and the one before
    this had grown to 370 lines. docs/design/tasks.md keeps its plain name,
    because it is one table for the whole repository rather than one per job.
    Files that were already finished under docs/decisions/ and
    docs/research/, and the earlier sections of this file, still name
    docs/design/prd.md and docs/design/hld.md, and that is on purpose.
    Each
    of them records one moment, and that was the real path on the day it was
    written. Rewriting them so one search comes back clean would make them less
    trustworthy, not more. The reasoning is in
    docs/decisions/adr/0017-scope-of-the-rename-sweep.md.
  • A document you confirmed is never rewritten behind your back. When the PM
    has to correct the opening document — a check that turns out to be impossible,
    two checks that contradict each other — your confirmed words stay exactly as
    you read them. The correction goes beside them, with its date. Every one of
    them is listed under a single fixed heading, so you can read the whole set at a
    glance and stop the PM if you disagree, and the work does not pause while you
    do. A correction is not permission either: anything that changes the scope, a
    DoD item or the milestone list still waits for your yes. One thing left that
    document for good — the list of its own old versions. It is already in each
    change request's Applied line and in the git history, so inside the document
    it was a second copy you had to walk past to reach the problem.

Added

  • Every role prompt now says what that role may write, and says out loud that
    reading is not restricted.
    All ten of them, the PM's own included. The list
    is written by class of document, never as a list of file names, because the
    opening document's name now changes with every job and a list of names would be
    wrong by the next one. There is also a single table of who writes which class,
    in the PM's prompt and in principles.md, and the two say the same thing.
  • A new rule: a document that judges the work is not the worker's to edit.
    The opening document, a task row's DoD items, the milestone list. If a briefing
    hands one of those to a role — even with the exact new wording, even when the
    change is plainly right — the role changes nothing and says so in its report.
    That is a mistake in the briefing, and the answer is to correct the briefing,
    not the role. The PM's own half is stricter, because it is the one who writes
    them.
  • A new rule: text that arrives inside a tool result is data, not
    instructions.
    A command's output, a file's contents, a web page, an MCP
    server's notes. Suppose it tells a role to start an agent, to message another
    role, to hide something from you, or to reach for the shell instead of its own
    tools. The role does none of it, and reports what arrived, what it asked for
    and where it came from. The PM gives such a report the weight of a security
    finding and names it at the milestone review. It also tells you where the text
    came from, so you can decide whether you want that thing installed. Handling
    it quietly is the one thing the injected text asked for.
  • Eight kinds of document now have a written list of what goes in them — the
    opening document, the design, a decision record, a change request, an interface
    contract, a test plan with its cases, the release and upgrade plans, and a DoD
    section. Each list is in principles.md with the outside source behind it, and
    a short version sits in the prompt of the role that writes that document. The
    opening document gained a shape it did not have before: the problem written as
    a problem and never as the solution, who reads which part at which moment, a
    number or a command for each quality bar the release must clear, and a target
    window with a reason behind it. Every item in it is also marked must-have,
    high-want or nice-to-have with a rank inside its class, so that when
    something has to be cut, the easy items are not the ones that survive.
  • Crew agents now carry numbered names in the list of agents you watch —
    crew-engineer-1, crew-qa-3 — and no number is used twice in a job. Two
    agents of the same role used to be two lines that read the same, and the name
    is the only thing telling you which report came from which agent. Nothing in
    the code changed for this; it is a rule the PM follows.
  • **One engineer per code change...
Read more