Porting .claude/ tooling to .factory/ (Droid) — executable checklist
Porting the .claude/ tooling to .factory/ — executable checklist
Section titled “Porting the .claude/ tooling to .factory/ — executable checklist”Approved by Chris 2026-09-30 (“yes port the skills”), as Factory-native copies, not symlinks. Rationale and what it unblocks:
2026-09-30-factory-missions-catalog.md§1.1. This file is the step-by-step; read it first and work top to bottom.
✅ EXECUTED 2026-09-30
Section titled “✅ EXECUTED 2026-09-30”
.factory/is now a generated tree, not a hand-maintained one. That is the one structural change from the plan below, and it is what makes Step 4’s parity guard enforceable rather than aspirational:
scripts/lib/claude-to-factory.mjs— the single shared, deterministic transform (substitution table, protected fragments, thedisable-model-invocation/ Claude-only / model-invocable classifications, the droid frontmatter table).scripts/port-claude-to-factory.mjs— writes.factory/skills/+.factory/droids/and prunes anything it would not generate.--checkexits 1 instead of writing.scripts/test/skills-parity.test.mjs— regenerates in memory and diffs. Picked up byscripts/gate.shthrough the existingnode --test scripts/test/*.test.mjsglob; no wiring change was needed. Edit the.claude/original and re-run the port script; a hand edit inside.factory/fails the gate.Four deviations from the steps below, each deliberate:
- 21 skills, not 20.
.claude/skills/bh-deploy-mac-dev.mdwas a bare.mdat the skills root, so neither harness ever discovered it — it has beengit mvd tobh-deploy-mac-dev/SKILL.md, which fixes it for Claude Code too.- 9 skills get
disable-model-invocation: true, not 8 —bh-cloud-offloadspends on remote compute and was already flagged in the table below. Every skill is now classified into exactly one ofDISABLE_MODEL_INVOCATION/MODEL_INVOCABLE, and the test fails on any new skill that is in neither, so the decision cannot be skipped by default.- Four skills carry a “Claude Code only” banner (Chris’s call when the gap surfaced):
bh-investigation-loop,bh-overnight-audit-loop,bh-overnight-swarm,bh-cloud-offloadare built onWorkflow/ScheduleWakeup/RemoteTrigger, which Droid has no equivalent for. They are ported for completeness with a banner naming the specific missing primitive. The rewrite ontoTaskfan-out +Loop/CreateAutomationis still open work.- Two stale claims in the
.claude/sources were fixed rather than propagated, since the port would otherwise have copied them into a second tree:bh-area-backgroundandbh-investigation-loopboth asserted.claude/skills/is gitignored. It is tracked (the repo.gitignorere-includes it), so both sections were rewritten around “uncommitted”, which is the hazard that actually still exists.Step 5’s items 3-7 are interactive and still need a human in a fresh session (
/skills,/droids). Everything scriptable is green: 32/32 node tests pass.
Why this is needed at all
Section titled “Why this is needed at all”Droid discovers project skills in .factory/skills/, .agents/skills/, .agent/skills/ and
personal ~/.factory/skills/; custom droids in .factory/droids/. This repo’s tooling lives in
.claude/, so every bh-* skill and agent is invisible to every Droid session — including
Mission workers, which inherit skills and custom droids but cannot see these. Claude Code sessions
keep using .claude/ unchanged; nothing there gets deleted or moved.
Use the installed port-claude-code-artifact-to-droid skill for the general mapping rules; this doc
carries the repo-specific decisions on top of it.
Step 0 — already done (2026-09-30, pre-clear)
Section titled “Step 0 — already done (2026-09-30, pre-clear)”-
.gitignore:.factory/needs no re-include block (unlike.claude/, it is not in~/.gitignore_global, verified withgit check-ignore). Added/.factory/settings.local.jsononly.
Step 1 — the 20 skills
Section titled “Step 1 — the 20 skills”Copy .claude/skills/<name>/ → .factory/skills/<name>/, including supporting files, then apply
the body edits in Step 3.
| Skill | Size | Supporting files | disable-model-invocation |
|---|---|---|---|
bh-add-content |
7.1 KB | — | no |
bh-appstore-release |
24.7 KB | — | yes (uploads a store build) |
bh-area-background |
8.9 KB | — | no |
bh-cloud-offload |
4.3 KB | — | yes (spends on remote compute) |
bh-deploy |
18.9 KB | — | yes (installs to device / publishes web demo) |
bh-determinism-repin |
6.5 KB | — | yes (a re-pin must be a human call) |
bh-dev-chunk |
6.5 KB | — | no |
bh-diagnose-render-timing |
5.7 KB | — | no |
bh-economy-sim |
5.9 KB | — | no |
bh-godot-engine-upgrade |
10.3 KB | — | yes (swaps the engine under the project) |
bh-investigation-loop |
12.3 KB | 4 | no |
bh-live-debug-deploy |
5.3 KB | — | yes (device deploy) |
bh-overnight-audit-loop |
11.3 KB | 1 | yes (long unattended loop) |
bh-overnight-swarm |
11.7 KB | — | yes (long unattended loop) |
bh-polish-audit |
11.3 KB | — | no |
bh-sdd-concurrent |
10.0 KB | — | no |
bh-telemetry |
16.7 KB | — | no |
bh-tunable-batch |
4.6 KB | — | no |
bh-vfx-iteration |
11.1 KB | — | no |
bh-write-test |
6.1 KB | — | no |
disable-model-invocation: true keeps the skill user-invocable (/bh-deploy) while preventing
Droid — or a Mission worker running at --auto high — from choosing it on its own. That is the
mechanism standing between an autonomous run and a live App Store upload.
Also present: .claude/skills/bh-deploy-mac-dev.md, a bare .md at the skills root. It is not
in a directory with a SKILL.md, so it is not a skill in either harness — it is a reference doc
bh-deploy points at. Decide: either give it a proper bh-deploy-mac-dev/SKILL.md home in
.factory/skills/, or leave it where it is and keep the cross-reference. Do not silently drop it.
Step 2 — the 4 agents → custom droids
Section titled “Step 2 — the 4 agents → custom droids”.claude/agents/<name>.md → .factory/droids/<name>.md.
| Agent | Claude model |
Droid model |
Claude tools |
Droid tools |
|---|---|---|---|---|
bh-implementer |
opus |
claude-opus-5-5 |
(unset = all) | omit the field entirely |
bh-reviewer |
opus |
claude-opus-5-5 |
Read, Grep, Glob, Bash |
["Read","Grep","Glob","Execute"] |
bh-visual-qa |
sonnet |
claude-sonnet-5-5 |
(unset = all) | omit the field entirely |
bh-feedback-triage |
sonnet |
claude-sonnet-5-5 |
Read, Grep, Glob, Bash |
["Read","Grep","Glob","Execute"] |
Load-time rules that will fail validation if ignored:
TodoWriteandSkillare always granted; never list them.ExitSpecModeandGenerateDroidcannot be enabled — listing either is a hard error.tools: allis rejected; omit the field to allow everything.- Tool IDs are case-sensitive.
- A droid that pins a model blocked by policy silently falls back to the parent’s model, so confirm the pin took effect rather than assuming it.
/droids has an Import from Claude Code flow that does this mechanically, but it maps a model
family to “the first available model in that family”, which can land on claude-opus-4-5 rather
than 5.5. Either hand-write the four files, or import and then fix every model: line by hand.
bh-visual-qa needs image input. Verify the pinned model actually has vision before trusting it —
glm-5.3 does not, which is exactly how this class of gap goes unnoticed.
Step 3 — body edits during the copy
Section titled “Step 3 — body edits during the copy”The instruction bodies are Claude-Code-flavoured. Swap, in every ported file:
| Find | Replace with |
|---|---|
Bash tool references |
Execute |
Write tool references |
Create |
Task/subagent dispatch by subagent_type |
same tool name, but .factory/droids/ paths |
model: "opus" / "sonnet" in dispatch examples |
claude-opus-5-5 / claude-sonnet-5-5 |
.claude/agents/…, .claude/skills/… paths |
.factory/droids/…, .factory/skills/… |
~/.claude/… personal paths |
~/.factory/… where the artifact was ported; leave alone where it genuinely still lives under ~/.claude (memory stores do) |
CLAUDE_CODE_* env vars |
drop; Droid has no equivalent (subagent models are set in subagentModelSettings) |
| “Claude” as the agent’s name | “Droid” |
Leave alone: CLAUDE.md itself (Droid reads it natively), memory-store paths under
~/.claude/projects/… (still the live convention), and every game/engine/Godot detail.
Three repo-specific notes worth carrying into the ported bodies while you are in there:
- Commit messages go via
git commit -F <file>, never chained heredocs, and a transient.git/index.lockcan abort a chain (memorybullet-heaven-droid-tooling-gotchas). - A worktree-isolated session refuses complex shell (loops,
sed -i ''chains, heredoc pipes) — write a script file and run it plainly. - Mission/
droid execautonomy:--auto mediumblocksgit push;--auto highdoes not.
Step 4 — the anti-drift guard
Section titled “Step 4 — the anti-drift guard”Two real copies of 20 documents will diverge. Add scripts/test/skills-parity.test.mjs:
- For each
.factory/skills/<n>/SKILL.md, find the.claude/skills/<n>/SKILL.mdtwin. - Compare the bodies (everything after the frontmatter fence), normalised for the Step 3 substitution table so an intended swap is not a diff.
- Fail with the list of drifted skills; a skill present in one tree and absent from the other is also a failure.
- Same for
.claude/agents/↔.factory/droids/.
Wire it into node --test scripts/test/*.test.mjs (the directory form fails MODULE_NOT_FOUND on
Node 22 — use the glob). This is what converts a silent fork into a red gate.
Step 5 — verification (none of this is optional)
Section titled “Step 5 — verification (none of this is optional)”node --test scripts/test/*.test.mjs— parity test green.node --test scripts/test/docsite-frontmatter.test.mjs— still green (this doc is wired).- Start a fresh Droid session in the repo (skills are discovered at session start).
/skills→ Effective tab: all 20bh-*present and Enabled, none Invalid. Check the winning source is Project for each./droids: all 4 listed; open each detail view and confirm the resolved model is the 5.5 pin, not a fallback, and that the tools list matches the table in Step 2.- Ask for one read-only skill by name and confirm it loads, e.g.
/bh-write-test. - Confirm a
disable-model-invocationskill still works when you invoke it but is not offered spontaneously. git status— only.factory/**,.gitignore,scripts/test/skills-parity.test.mjsand docs changed. Nothing under.claude/modified.
Step 6 — commit
Section titled “Step 6 — commit”One commit, message shaped like the repo’s existing ones:
chore(droid): port .claude skills + agents to .factory (Droid-native copies)Include Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>.
Pull/rebase first — Toby shares main — and push immediately after the merge rather than at session
end, per the standing rule in CLAUDE.md.
Then what
Section titled “Then what”The port unblocks Missions. Next step after this lands is 2026-09-30-factory-missions-catalog.md
§1.2: scripts/mission-qa.sh, then M3 (tunable batch) as the smoke-test mission.