Skip to main content

mdex-software-factory

Operate the MULTI/DEX software factory - a filesystem task queue at tasks/queue with atomic claims, priority-by-rename, operator signal files, and session roles, where every consumer seat is a CLI executor session in its own git worktree with its own isolated local venue, plus a dedicated operator seat for operator-directed work. Ingest when told "you are a TaskConsumer / TaskPrioritizer / TaskProducer / the OperatorTasks console", when booting or taking over a factory seat, and whenever filing follow-up work into the queue.

설치로 이동

소스 정보

저장소
dfinity/public-multidex
최근 소스 활동
2026년 8월 31일 21:36
감지된 SKILL.md 언어
영어
스타
13
포크
6

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
mdex-software-factory
description
Operate the MULTI/DEX software factory - a filesystem task queue at tasks/queue with atomic claims, priority-by-rename, operator signal files, and session roles, where every consumer seat is a CLI executor session in its own git worktree with its own isolated local venue, plus a dedicated operator seat for operator-directed work. Ingest when told "you are a TaskConsumer / TaskPrioritizer / TaskProducer / the OperatorTasks console", when booting or taking over a factory seat, and whenever filing follow-up work into the queue.
metadata
{"title":"The software factory","category":"Operations"}
# Skill: the MULTI/DEX software factory Several Claude sessions work one shared task queue continuously: **TaskConsumers** (typically 3-5, each a CLI executor session in its own git worktree with its OWN local venue - the worker doctrine below) claim and execute tasks; **one TaskPrioritizer** reorders the queue and keeps the board; **TaskProducers** (ad hoc) turn instructions, audits, and community issues into tasks; and one standing **operator seat** hosts operator-directed work. The operator (Dominic) boots sessions and is the escalation path. There is no message bus and no coordinator process: the queue directory IS the coordination, and every mutation is an atomic same-filesystem rename, so races have exactly one winner and losers retry. The operator's control channel to a running seat is a FILE too - `tasks/signal/` (Operator signals, below) - not a chat message. Three things make this factory different from a generic one, and they are all load-bearing: 1. **This machine hosts live systems.** Long-lived bot fleets drive the LIVE subnet at multidex.ai and the cloud engine, and the primary checkout's :8000 local network is the operator's standing demo venue. Seat isolation (own worktree, own `ICP_HOME`, own OS-assigned port) and the `mdex-process-safety` skill are how the factory coexists with all of that. 2. **Nothing is ever pushed.** Merges land on the primary checkout's `main` LOCALLY. Pushing to origin or the public mirror is the operator's act, on explicit instruction only, sequenced by the outward-batch policy - the factory has no push step anywhere. 3. **Task production stays as it is today.** `docs/tasks/` (tracked) remains the authored task corpus - W-style briefs, `done/`, the README queue table. The factory adds a RUNTIME queue (`tasks/`, gitignored) that carries execution state: claims, priorities, dependencies, the board. `scripts/factory.sh` is the ONLY way to touch the queue. It works from the primary checkout or any worktree (it resolves the queue root to the PRIMARY checkout's `tasks/` either way - `git worktree list` puts the primary first). Run `scripts/factory.sh help` for the command table; `scripts/factory-selftest.sh` exercises it against a throwaway queue. ## The queue on disk ``` tasks/queue/ pending tasks, one md file each: PPPPPPPPPP-IIIIIIIIII.md tasks/claimed/<worker>/ in-flight tasks, one subdir per consumer tasks/archive/ terminal tasks: <id>.md (status stamped inside) tasks/factory/ coordination surface: index.md (the board), merge.lock/, .ids/ (the append-only id allocator - never touch, never clean) tasks/handoff/ one <worktree-basename>.md per seat waiting for a successor session: the boot prompt, on disk, where an app restart cannot reach it (`factory.sh handoff-list`) tasks/signal/ operator ORDERS to a seat: <worker>.md, plus all.md (the broadcast). Wakes every `watch`, blocks that seat's next `claim` until acknowledged (Operator signals, below) ``` All six are gitignored runtime state. The tracked corpus in `docs/tasks/` is unaffected and keeps its current conventions exactly. One further piece of runtime state lives outside `tasks/`: each seat's `<worktree>/.factory-worker` (gitignored too), the one-line identity pin factory.sh reads instead of guessing a worker from the cwd's branch - see the TaskConsumer setup. **Naming.** `P` (10 digits) is the priority key; `I` (10 digits) is the immutable id. Both start as the UTC epoch seconds of creation, so an untouched queue lists in FIFO order and `ls tasks/queue` IS the execution order (ascending). The prioritizer renames ONLY the P half. The id is the task's identity forever - in `after:` dependencies, in archive filenames, in board notes - and the allocator guarantees it is never minted twice. **Lifecycle.** `queue/ -> claimed/<worker>/ -> archive/` - claim and archive are `mv`s performed by factory.sh; there are no other states. A task in `queue/` is claimable by definition; if it must not run yet, that is expressed by `after:` dependencies or a `90`-band parking, not by side agreements. **The two layers.** A queue task either inlines its whole brief (small, self-contained work), or names a tracked brief with `docfile: docs/tasks/<file>.md` plus a one-paragraph gist. The brief rides git, so every worktree sees it and law 6 (self-containedness) is satisfied transitively. Closing a `docfile:` task INCLUDES the corpus bookkeeping, on your branch so it rides your merge: move the brief to `docs/tasks/done/` with the `**done <date>**: ...` completion note in the current style, and strike its row in `docs/tasks/README.md` (`~~[W..]~~ ... — **done <date>**: ...`). `factory.sh done` refuses to archive while the brief still sits at its original path - the **bookkeeping law**, mechanical so forgetting is impossible. Each seat edits only its OWN task's README row; rows are one line each precisely so parallel strikes merge clean. **The operator lane.** `lane: operator` in a task's front matter means the task is WAITING ON THE OPERATOR and no consumer seat may claim it. It holds two shapes of work: operator-EXECUTED tasks (engine/subnet rollouts, pushes, anything needing human-held auth - law 8), which the operator resolves with `done`; and operator-INPUT tasks (a decision, credential, or clarification a consumer needs), which pass THROUGH the lane - the answer is embedded in the task file and it is released back to the consumer queue (the OperatorTasks role below). `factory.sh claim` skips lane tasks entirely, including as a last-resort fallback, so a seat gets an itemized `EMPTY` rather than forbidden work; `factory.sh claim --operator` claims only them. `list --operator` / `list --consumer` filter, and the board gives them their own section - being SEEN at rollout time is the point of the lane, not being worked. This is a different axis from priority: band 90 only DELAYS a claim, and on a contended queue "nothing else remains" arrives routinely; a task no consumer may run must be unclaimable, not merely parked. ### The laws (every role) 1. Only `factory.sh` mutates queue state. Never `mv`/`rm`/Write task files by hand, and never operate on a worktree's own `tasks/` copy. 2. The id half of a filename never changes. Only the prioritizer renames the priority half, only via `reprioritize`, only for QUEUED tasks. 3. A claimed task belongs to its worker: nobody else edits or moves it. The prioritizer flags stale claims on the board but never requeues them itself - that is the operator's call (or the owning session's, via `requeue`). 4. The prioritizer never edits task content, never claims, and never touches git history or refs (no commits, merges, checkouts). Its only git contact is the janitor's read-only liveness look at a stale claim's worktree. 5. Nobody DEVELOPS in the primary checkout. It hosts four things only: the runtime queue, the canonical `:8000` local venue (the operator's - see "The operator seat"), the untracked rollout config (`scripts/.subnet.conf`, `scripts/.cloud-engine.conf`, the API-key files), and merges performed under the merge lock. Consumers' only writes there are through `factory.sh` and that final merge; operator-directed development happens on the operator seat, not in the primary. 6. Every task is self-contained: a session with no memory of the conversation that produced it can act on it (a `docfile:` brief counts - it is tracked in every worktree). If it is not, it is not a task yet. 7. Follow-up work any session discovers is filed as a queue task - never a suggested-task chip, never a note in some doc. 8. Consumer seats never touch a remote target and never push. Concretely out of bounds: `scripts/deploy.sh engine|subnet`, `deploy_to_engine.sh`, `deploy_to_subnet.sh`, `start_bots_*`/`stop_bots_*` for engine or subnet, `topup*.sh` against remote targets, anything that reads `scripts/.subnet.conf` or `scripts/.cloud-engine.conf`, and `git push` to ANY remote (origin and the public mirror are operator acts under the outward-batch policy). Live verification happens on the seat's OWN local venue. Remote rollout is a different trajectory with its own human-held auth - `icp` reauth is a web flow that WEDGES in a non-interactive session (piped output swallows its URL and the command parks forever), so a consumer that tries either blocks for hours or, worse, an expired delegation fakes success. When a task's remote part matters: do the local part, note the remote part as DEFERRED in the completion note, and file a follow-up task carrying `lane: operator`. Sole exception: the task text itself explicitly makes a remote deploy the deliverable - and such a task is operator-lane by construction. 9. Nobody edits a QUEUED task file in place - not even its producer. To change task content, own it first: consumers edit only their claimed files; the operator takes a specific task with `claim --operator --id <id>`, edits, and requeues. An in-place Write racing a claim recreates the moved file as a ghost duplicate of a task someone else now owns. 10. **`mdex-process-safety` is binding in every role.** No pattern kills (`pkill -f` took down the live subnet fleet three times), no `pgrep`-based waits (they match the waiting shell itself), kill only PIDs you captured or via the repo's `stop_bots_*` machinery, never leave an `icp network status` poll unattended without a kill-after watchdog, and EVERY `icp` invocation carries `--identity <name>` - never `icp identity default`, whose global state other sessions and connectors mutate mid-run. Read that skill before your first process or `icp` operation; it is short and it is the machine's #1 hazard list. 11. **The posture law.** A branch merges into main only on the committed defaults: `src/backend/main.mo` AND `src/bridge/main.mo` resolve to `#play` (read by `mdx_posture_of`, the one comment-aware reader), and `icp.yaml` carries no `port: 0` line. `factory.sh merge-lock acquire <branch>` enforces it mechanically. The seat-local `#dev` flip and the port-0 gateway edit are legitimate WORKING-TREE state (that is how a seat's venue runs); the law is what lets them exist without ever reaching main. ## Task file format ```markdown --- producer: <who filed it: branch, "dominic", "audit-2026-08-17", ...> class: security-high | security-medium | architecture | prerequisite | feature | hygiene | docs | parked (advisory - the prioritizer's input) touches: backend, tests (see the vocabulary below; "suite" = repo-wide) docfile: docs/tasks/W4-23-clamp-amm-half-spread.md (the tracked brief, when one exists) after: 1785709403, 1785709404 (ids that must archive first) split_from: 1785709400 (lineage, when split) lane: operator (waiting on the operator - execution OR input) --- # <imperative title - what will have happened when this is done> Goal, context, and exact pointers: paths, file:line, the shape of the change, and HOW TO VERIFY (which suites, which gates, what to look at). Out-of-scope notes if the edges are temptingly fuzzy. With a docfile: a one-paragraph gist plus anything the brief lacks (the brief carries the details). ## Factory log <- appended by factory.sh; never write this yourself ``` Only the `# title` line is mandatory (factory.sh refuses drafts without one); `class` and `touches` should almost always be present, `docfile` whenever a tracked brief exists, `after` whenever order matters, and `lane: operator` whenever law 8 puts the work out of a consumer's reach. Fields are greppable plain lines - keep them single-line, comma lists. **`touches:` vocabulary** (the affinity and overlap currency - use these, not ad hoc strings): `backend` (src/backend), `bridge` (src/bridge), `arb` (src/arb), `archive` (ArchiveCanister + archive design), `frontend` (src/frontend), `verifier` (scripts/verify_ledger.mjs + its fixtures), `deploy` (scripts/), `tests` (tests/), `docs` (docs/), `candid`, and `suite` for repo-wide waves. ## Priority bands The prioritizer expresses policy by renaming into bands: new priority = `BB * 10^8 + (id mod 10^8)` - `factory.sh reprioritize <id> <BB>` computes this - which preserves FIFO order among tasks in the same band. | Band | Meaning | |------|---------| | 00 | emergency - drop everything (operator-directed, mostly) | | 01 | security high - live venue / pipeline safety (the W1 shape) | | 02 | security medium - tape & verifier integrity, #production hardening (W2/W3 shapes) | | 03 | architecture / doctrine changes that would force rework of tasks landing after them | | 04 | prerequisites - tasks other queued tasks name in `after:` | | 05-09 | expedited, prioritizer's discretion | | 17-21 | the natural zone: untouched creation keys (epoch seconds land here through 2036) = plain FIFO | | 30 | hygiene, gate/instrument debt, low-severity findings (the W5 shape) | | 40 | nice-to-have, cosmetic, docs polish | | 90 | parked - claim only when nothing else remains | Rationale in one breath: security before features so it is baked in, not bolted on; the tape and the verifier are REQUIREMENTS under the transparency doctrine, not features - rank them accordingly; architecture before the work it would rework; prerequisites before their dependents; everything else FIFO. Band 90 is not a way to say "never": it delays a claim, it does not refuse one. Work a consumer must NOT run belongs in the operator lane (`lane: operator`), a separate axis - a task can be band 01 and operator-lane at once. ## Role: TaskProducer 1. Collect the work: the operator's instruction, an audit round, community issues, or existing findings. One task per independently completable item. 2. Dedupe first: `scripts/factory.sh list`, the board (`tasks/factory/index.md`), a grep of `tasks/archive/`, AND the corpus - `docs/tasks/README.md`'s queue table plus `docs/tasks/done/`. Extend or skip rather than duplicate; the "Already resolved - do not redo" table in the README exists because reporters re-report fixed things. 3. Author to the current corpus standard. Substantial work gets a W-style brief in `docs/tasks/` exactly as today (verified against the working tree by symbol lookup, citations, the how-to-verify section, pushback recorded where a reporter is wrong) and a THIN queue task pointing at it via `docfile:`. Small self-contained items inline everything in the queue task. Either way the self-containedness bar is law 6, and sources are cited (file:line, issue numbers, commit shas). 4. `scripts/factory.sh new <draft>` - it allocates the id and prints the queue path. Draft in your scratchpad; the draft file stays where it was, inert. (New briefs in `docs/tasks/` are ordinary tracked edits - commit them through the normal branch flow, not by writing into the primary.) **File in dependency order**: an `after:` id must be one `new` has ALREADY printed (every filing prints its id) - never a guessed or predicted one. `new` refuses a draft whose `after:` names an id that was never minted, because a nonexistent gate gates nothing: `claim` treats unknown ids as satisfied (live-ids-only, deliberately - in the open-saas factory five guessed-id gates once let four "sequential" tasks run concurrently). File prerequisites first, then the dependents citing the printed ids. 5. Report the filed ids/titles. Do not set priorities - that is the prioritizer's job; your `class:` field is its input. (Genuine emergencies: say so to the operator rather than guessing a band.) Set `lane: operator` on anything a consumer seat is forbidden to execute - engine/subnet rollouts, pushes, anything needing human-held auth or a decision only Dominic can make. That is a statement of fact about the work, not a priority call, so it IS yours to set. Leaving it off does not merely mis-sort the task: seats will claim it, discover they must not act, and requeue it, indefinitely. **Seeding the queue from the corpus:** the standing instruction "work the docs/tasks queue" translates to one thin `docfile:` task per open row of `docs/tasks/README.md`, skipping rows marked IN FLIGHT in another session, with `after:` wiring where a brief names an ordering. Do this once at factory boot and again whenever a new triage round lands in the corpus. ## Role: TaskPrioritizer (exactly one session) Loop forever: **wake -> triage -> reorder -> board -> janitor -> re-arm** - and keep this skill fresh MECHANICALLY, not by feel: every pass, stat the primary copy (`stat -f %m <primary>/.claude/skills/mdex-software-factory/SKILL.md` - one token-free shell call, foldable into the same Bash call as your watch); RE-READ the file whenever that mtime moved past your last read, and in any case at least once an hour. The mtime gate means a protocol change reaches you within one pass; the hourly floor exists because even an unchanged skill needs occasional re-reading - summarization quietly erodes the copy in your context. - **Wake**: `scripts/factory.sh watch 500` blocks until queue/claimed/archive/signal change (prints CHANGED) or times out (TIMEOUT) - act on either, then re-arm. In a harness with background tasks, run `watch 3600` via a background Bash call and you will be re-invoked on completion; otherwise call it in the foreground repeatedly. Either way each pass costs nothing when idle. - **Triage**: `factory.sh list`; read (`factory.sh show <id>`) tasks you have not seen before (the board is your memory of what you have triaged). Judge class and content per the band table - and check `after:` chains: a dependent must sort behind its prerequisites (band 04 the prerequisites forward rather than parking the dependent, when both matter). - **Reorder**: `factory.sh reprioritize <id> <band>`. If it errors, the task was claimed mid-rename - that is fine, drop it. Do not churn: rename only when the current position is actually wrong. - **Rank bands, not tasks - keep the head band BROAD.** A band is a statement that its members are equally important, and that interchangeability is what consumer affinity (`claim --affinity`) converts into warm-context throughput: a seat may take any same-band task it knows the ground for, never anything past the band. A head band holding one task disables affinity exactly where most claims happen. So when you promote several tasks of comparable urgency, put them in the SAME band (within-band FIFO is preserved by the formula); split finer only when one genuinely must precede another - and a true must-precede is usually an `after:` dependency, not a band distinction. - **Board**: `factory.sh index` regenerates `tasks/factory/index.md` - the operator's one-glance view. Refresh it every pass. Its **Seats awaiting successor** section lists `tasks/handoff/` - seats that handed off and are sitting unmanned. Call any entry older than an hour out in your pass summary: a shrinking fleet is invisible otherwise (a CLI seat's self-spawn can fail; a desktop chip can die with an app restart). Its **Operator signals** section lists unacknowledged orders - each line is a seat that CANNOT claim until it acknowledges, so an old entry means a stopped or dead seat: call those out too. - **Janitor**: the index marks claims idle >4h as STALE. Investigate liveness read-only (is the worktree still there? recent commits? `git -C <worktree> log --oneline main..`), REPORT on the board via your pass summary, but do not requeue (law 3). ONE exception the operator has standing approval to act on, and which you should therefore state plainly rather than merely flag: a claim whose seat has been silent for HOURS **and** whose worktree hosts no live session (no recent writes, no successor Terminal, no handoff file being worked) is requeue-eligible even mid-band - an app quit kills desktop sessions MID-TURN and such claims strand their tasks for many hours. Say which claims qualify and why; the requeue itself is still the operator's call, and its note must say what partial work sits on the seat's branch so the next claimant does not redo it. Also `factory.sh merge-lock status`: a wedged lock older than 30 min will be broken by the next acquirer automatically; mention it. And title hygiene: retitle any fleet session whose title has drifted from this skill's conventions (a consumer still wearing a boot auto-title, a spare never cross-titled) - you may rename every session except your own (cross-titling, in the warm-spare pool section), and this sweep is what keeps the resume fan-out's title match honest. - **Resume fan-out**: when the operator reports a harness restart, nudge every fleet session (titles matching `MULTI/DEX worker — *`, plus the legacy `MULTI/DEX — worker started *` until the fleet rolls over) via the session-management tools (list_sessions + send_message, where the harness offers them): "Harness restarted - re-read the skill and follow
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기