dsh-hogwarts-loop
Verified@dyikai/dsh-hogwarts-loop Β· v1.0.5 Β· MIT Β· Web UI
Hogwarts-loop as a DeepSeek Harness plugin: per-role subagent dispatch on configurable models, with a client settings page.
Install
dsh plugin add @dyikai/dsh-hogwarts-loop Confirm the layer applied with dsh --profile default --dump-config β see the install guide.
Source
Tags
Creators
Readme
dsh-hogwarts-loop
Plan β build β review β commit, as a DeepSeek Harness plugin. One subagent per cast member, each dispatched on its own configurable model.
@dyikai/dsh-hogwarts-loop packages the hogwarts-loop discipline: take a large engineering effort from a plan to reviewed, committed code in small, independently verified checkpoints. The methodology ships as a skill, every cast member is a managed subagent started through one tool, and the model behind each role is set in the client instead of being baked into the prompt.
What it adds
| Capability | Form | Where it lives |
|---|---|---|
| The loop methodology and each role's detailed duties | the hogwarts-loop skill (also triggerable as /hogwarts-loop) |
skill/SKILL.md, skill/references/*.md |
| Finding record, batch validation, convergence cap, and evaluation guide | markdown contract + synthetic and repository-derived cases | skill/references/review-contract.md, skill/references/evaluation.md, skill/assets/eval-*.md |
| Create or continue the same cast member within a parent session | the hogwarts_dispatch tool |
registered by index.js |
| Edit provider / model / effort / output cap per role | Settings β Hogwarts Loop, plus the Plugins page row for this plugin | client.js (browser half) |
// First call creates Harry; after CP1 passes, the next call continues him.
hogwarts_dispatch({ role: "harry", description: "CP1 build", prompt: "<first brief>" })
hogwarts_dispatch({ role: "harry", description: "CP2 build", prompt: "<next brief>" })
One parent session, one child per role. Checkpoints and fix rounds keep the same childId; changing description, completing a CP, or becoming inactive does not create a new Harry. Each role is independent, so Harry and Severus retain separate histories. Dispatch is nonblocking: wait for a role's report before assigning its next CP; concurrent calls to that role do not create parallel workers.
Bindings survive plugin reload and resuming the same parent session. A continuation failure never silently creates a replacement. Older histories with ambiguous role bindings require intervention instead of guessing. A genuinely new parent session starts its own cast, rehydrated from the plan.
Results distinguish started from continued. Creation reports the configured provider/model; continuation identifies the existing child rather than claiming it uses newly edited defaults. cwd is creation-only: a non-empty cwd on a later call is rejected. Omit it to preserve the child's established working directory. The roster label keeps the first description; later briefs do not relabel the child.
Conditional ablation
The skill requires a complexity report on every Harry delivery. When the ablation policy triggers, Harry runs controlled removal/substitution experiments without weakening acceptance, verifies the combined survivor, and gives Severus the evidence to audit. This is a workflow gate, not a runtime code-size scanner; direct manual dispatch skips the skill's checkpoint discipline. Thresholds and counting rules have one canonical home.
Review discipline
The review contract keeps a few rules: validate blocking candidates before repair, preserve evidence and finding IDs, and independently check the fix rather than repeating the whole review. Complete repair/re-review rounds and evidence-only batches have separate budgets; normal proof gathering is not counted as another repair round.
One reviewer is the default. Specialists cover an actual changed risk or unresolved question, not every file in a broad category. Required human approvals remain in force. These are workflow instructions, not host permissions or runtime enforcement.
Evaluation distinguishes fixture checks from real before/after review comparisons. A repository-derived pair and repair replay complements the synthetic cases; scoring and provenance stay outside reviewer context. None of these artifacts is a model-quality score.
Working plans, progress lists and review reports stay in the session or task-scoped
temporary storage by default, not in Git or behind .gitignore. Only an explicitly
approved, maintained roadmap/design document is durable; the shipped plan template
is a product asset, not a filled-in execution report.
Install
Requires Node.js β₯ 20 and a DeepSeek Harness profile.
From the Add plugin wizard
Type @dyikai/dsh-hogwarts-loop into the Add plugin wizard's search box and click Install.
With the dsh CLI
dsh plugin --profile web add @dyikai/dsh-hogwarts-loop
dsh web
No build step and no restart, but see the reload note below.
From a local checkout
plugin_manager -> { action: "install_bundle", target: "<path-to-this-repo>" }
To uninstall:
plugin_manager -> { action: "remove_bundle", target: "@dyikai/dsh-hogwarts-loop" }
After installing β or after changing client.js β reload the page once so the browser half loads and the settings section appears. The host half reassembles together with the plugin row.
Use it
The plugin is not a task and has no start button. Once installed it is always present, providing three things: the hogwarts_dispatch tool, the hogwarts-loop skill, and the settings page. "Making it work" means triggering the skill, which in turn directs the main agent to dispatch. The plugin stays loaded even when nobody triggers it.
Three ways in:
- Call the skill directly β type
/hogwarts-loopand its content is injected into the current session. - Say what you want β "plan this milestone with the hogwarts loop, then implement, review and commit each checkpoint". The skill carries a description, so the model also loads it by itself when a task matches.
- Dispatch once by hand β "use
hogwarts_dispatchto send harry to do X". This uses the plugin directly and skips the checkpoint / review discipline in the skill.
How to tell it is really working:
- a
hogwarts_dispatchtool call shows up in the session; - repeated dispatches to the same role return the same
childId; - the child's session log records
agentModelandconfig.modelequal to that role's configured route (for exampleharryondeepseek-flash,severusondeepseek-v4-pro), not the main session's model.
Configure the cast
Settings β Hogwarts Loop (the settings entry at the bottom of the sidebar) is the main entry. It registers as a settings.section with order 60.
The same form has a second entry point: Plugins β Installed plugins β @dyikai/dsh-hogwarts-loop β the hogwarts-loop row β Configure (plugins.row.config).
The page lists every cast member, one row each: provider and model are dropdowns fed by the host's model catalog (remote.session.modelCatalog(), grouped per provider); switching the provider switches the model to that provider's first entry. Reasoning effort is a dropdown built from the levels the selected model actually supports (blank = the model default), and the max output tokens field may be left empty. Below them, Dispatch settings holds the subagent backend and the delegation depth cap.
Save writes every change as a single atomic, version-fenced mutate to this plugin's row in the current profile. The next newly created role uses it β no reinstall and no restart. Continuing an existing role preserves its child session and does not apply new creation defaults or replace it to switch models.
Defaults (the behaviour when nothing is overridden):
| Role | Seat | Default model |
|---|---|---|
harry |
implementer (writes the code, runs the gates) | deepseek-flash |
severus |
reviewer (correctness lead; read-only) | deepseek-v4-pro |
alastor |
security specialist | deepseek-v4-pro |
filius |
performance specialist | deepseek-flash |
minerva |
concurrency specialist | deepseek-flash |
argus |
closeout UI walkthrough | deepseek-flash |
voldemort |
escalation implementer (one escalated problem only) | deepseek-v4-pro |
Escalation: Voldemort
Harry gets at most three attempts at a problem (one attempt = one build β gate β review round). If all three fail β the gate is still red, or Severus keeps raising the same root cause as P1 β the problem is escalated once to voldemort. The escalation brief must carry the checkpoint acceptance criteria, what each of the three attempts changed, the exact failure evidence for each, and the possibilities already ruled out.
Voldemort is exactly the same implementer as Harry: same checkpoint scope, same gates, same "green tree plus raw evidence", and he does not commit either. The only differences are a stronger model and the history of three failed attempts. His mandate is one specific problem, not the whole checklist: he changes what that problem needs, he does not take over the remaining checkpoints, he does not "clean up while he is here", and once the problem is fixed and has passed Severus, control returns to Harry. He gets at most two attempts, and his work must also pass Severus before it is committed. If both fail, the loop stops: report the problem, the five attempts (3 + 2), the evidence, what was ruled out, and the options β no fourth Harry, no third Voldemort, and no quietly widening the scope to turn the gates green.
These two numbers live in the Escalation section of skill/SKILL.md β the skill text is the rule. The cast itself is defined by DEFAULT_ROLES in index.js. The separate checkpoint-wide repair-round and evidence budgets (and their precedence with these attempt limits) are in skill/references/review-contract.md.
The form writes this plugin's profile row (the
hogwarts-loopentry incordis.patch.yml). Editing that row'sconfigby hand or in bulk works too; the field definitions are theConfigexport inindex.js.
Albus is you β the main session. Its model is chosen by the session's model picker and is not controlled by this plugin; the plugin only decides the route of every subagent it dispatches.
Why the form looks like this
- The form registers twice β in the settings panel's
settings.sectionand in the Plugins page'splugins.row.config(key@dyikai/dsh-hogwarts-loop#hogwarts-loop) β and both render the sameFormBody. - The settings-panel entry resolves this plugin's namespace through the shared
settings.describemirror (matching thehogwarts-loopsuffix, so aninclude:prefix still works) and then gets a version-fenced write handle viactx.configForms.get(ns). The Plugins entry receives the boundformdirectly from the page, so it never has to guess a namespace. - On the host side,
Configmarks the two top-level objectsdelegationandrolesas.volatile()(schemastery cannot nest volatile, so the reference has to sit on the object). That is both what makes the settings service treat the entry as editable and serve the settings panel, and what guarantees new role creation reads the current defaults rather than a snapshot taken when the plugin activated. - When the model catalog cannot be read (expired login, failed catalog request), the same fields fall back to text inputs instead of breaking the page.
- A wrong creation-time model is never a silent failure:
hogwarts_dispatchvalidates the route before creating a role and returns an actionable tool error.
Verify the routing
After dispatching a role, the child's session log records the real route. Session directories live under ~/.dsh/sessions/<encoded-cwd>/<childId>/, where <encoded-cwd> is the project path with separators turned into dashes:
node -e '
const fs = require("node:fs"), zlib = require("node:zlib");
const base = `${process.env.HOME}/.dsh/sessions/<encoded-cwd>/<childId>`;
const f = fs.readdirSync(base).find((n) => n.endsWith(".zstd"));
const ev = zlib.zstdDecompressSync(fs.readFileSync(`${base}/${f}`))
.toString("utf8").split("\n").filter(Boolean).map(JSON.parse);
console.log(ev.find((e) => e.type === "subagent/descriptor")?.data);
console.log(ev.find((e) => e.type === "request/header")?.data.header.config);
'
Expect agentModel / config.model to equal that role's configured model.
Repository layout
.
βββ package.json # @dyikai/dsh-hogwarts-loop; dsh.bundle.patch + dsh.client
βββ cordis.patch.yml # inserts the hogwarts-loop row
βββ index.js # host half: Config schema, skill registration, hogwarts_dispatch
βββ client.js # browser half: per-role config form in Settings and on the Plugins page
βββ locale/{en,zh}.json # plugin name and description in the Plugin Manager
βββ icon.svg # plugin icon
βββ skill/
βββ SKILL.md # the loop overview + "dispatch with hogwarts_dispatch"
βββ references/*.md # planner / implementer / reviewer / loop / example /
β # review-contract / evaluation
βββ assets/
βββ plan-template.md # working plan: risk, review ledger, convergence
βββ eval-inputs.md # synthetic seed targets (reviewer-safe)
βββ eval-expectations.md # answers kept separate from the targets
βββ eval-legacy-inputs.md # repository-derived paired / staged snapshots
βββ eval-legacy-expectations.md # provenance, scoring and replay protocol
Known limits
- The settings entry is a page the plugin registers itself (
settings.section), not a form the host auto-generates fromConfigβ that renderer does not exist in this client version yet (autoGenerateis not consumed by any built-in client). - The first dispatch uses a continuable
spawnbackend; later dispatches resume that child. One-shot backends cannot satisfy the persistent-role contract.fork-style backends inherit the parent's route and cannot provide independent per-role model selection. delegation.maxDepthdefaults to1: a child cannot dispatch further down.- Children join the parent session's current agent preset and use its tools.
- The plugin is profile-level: install it once and every session in this profile sees
hogwarts_dispatchand thehogwarts-loopskill.