| name | dark-factory |
| description | Run the Dark Factory DOT pipeline runner against a goal. Slash command: /factory. Implements StrongDM's Attractor pattern as an external Python runner — .dot files are the versioned artifact, sealed holdouts live in a separate repo, every step is recorded to CXDB, and the Healer clusters failures into diagnoses. Use when you want the goal_harness idea executed as a reproducible external pipeline instead of in-Claude subagent dispatch. |
/factory — Dark Factory Pipeline Runner
Purpose
Run a goal through the Dark Factory .dot pipeline runner at
~/projects/dark-factory/. The runner is the Python implementation of
StrongDM's Attractor pattern (cf. brynary/attractor, strongdm/attractor).
Versus /h / /goal_harness:
| Aspect | /h (goal_harness) | /factory (this skill) |
|---|
| Engine | Claude session dispatches subagents | dark-factory binary (uv install) |
| Artifact | Skill prose + tool calls | .dot graphviz file |
| Recording | Conversation transcript | SQLite CXDB row-per-step |
| Holdouts | Adversarial subagent reads diff | Sealed repo agent never sees |
| Reuse | Tied to this session | Replayable from .dot alone |
| Cost | High (multi-agent context) | Low (one CLI per node) |
Use /factory when you want the goal_harness idea as a reproducible external
pipeline; use /h when you want an interactive in-session loop.
Repos
~/projects/dark-factory/ — the runner (factory code; agent can see this)
~/projects/dark-factory-holdouts/ — sealed scenarios (agent must NEVER read)
- Pushed:
https://github.com/jleechanorg/dark-factory + dark-factory-holdouts
Available pipelines
.dot file | Flow | When to use |
|---|
pipelines/factory/hello.dot | plan → implement → holdout → fix-loop → exit | Add a new feature with a holdout scenario |
pipelines/factory/gates.dot | start → holdout → /es → /er → /code_standards → exit | Validate an already-implemented diff (Attractor-style 4-gate harness as .dot) |
pipelines/factory/pr_gates.dot | start → /es → /er → /code_standards → exit | Validate an already-implemented in-flight PR diff (bypasses holdouts) |
pipelines/slim/minimal_feature.dot | plan → implement → test → review → holdout → gates → exit | Full production pipeline from scratch with a holdout |
pipelines/slim/minimal_pr.dot | plan → implement → test → review → gates → exit | Slim in-flight PR iteration loop with parameterized tests (bypasses holdouts) |
You can also write your own .dot and pass it via --pipeline.
How to invoke this skill
The user types /factory $ARGUMENTS. Parse the arguments and run the steps
below. Do not default to a single pipeline — select the graph that matches
the task (see Pipeline selection below) unless the user passed --pipeline.
Step 0 — Select pipeline (mandatory when --pipeline omitted)
- Read the goal and apply factory-spec Step 0 (greenfield vs brownfield).
- Choose a pipeline from
~/projects/dark-factory/docs/pipeline-selection.md (or the table in
Available pipelines below).
- Tell the user which pipeline you chose and why before running.
- If brownfield replace/delete: encode delete-first rules in the goal; do not
use a greenfield additive pipeline by default.
If $ARGUMENTS already contains --pipeline, skip auto-selection.
Short-name expansion
When --pipeline is a short name (no / or .dot), expand under
$DARK_FACTORY_HOME:
| Short name | Path |
|---|
gates | pipelines/factory/gates.dot |
hello | pipelines/factory/hello.dot |
pr_gates | pipelines/factory/pr_gates.dot |
minimal_feature | pipelines/slim/minimal_feature.dot |
minimal_pr | pipelines/slim/minimal_pr.dot |
review_slim | benchmarks/attractor-spec-review/pipelines/review_slim.dot |
review_full | benchmarks/attractor-spec-review/pipelines/review_full.dot |
Arg parsing
Honor these flags inside $ARGUMENTS:
--pipeline <name> — short name (gates, hello, pr_gates, minimal_pr,
minimal_feature, review_slim, review_full) or path to a .dot. If
omitted, auto-select from the goal (Step 0 above) — never blindly default
to gates.dot or minimal_feature.dot.
--feature <name> — holdout feature (required when the pipeline includes a
holdout_eval node; default hello)
--backend echo|claude|codex|ao|agy — default claude. Use echo for cost-free
dry-runs that verify wiring without LLM calls.
--max-steps <N> — default 100
--cxdb <path> — default ~/.dark-factory/cxdb.sqlite (shared across runs
so the Healer can cluster across history)
--state KEY=VALUE — pre-seed context variables; repeatable. Extremely useful for setting parameterized test commands (e.g. --state slim.test_command="pytest tests/...") during PR iteration loops.
Whatever is left after flag parsing is the goal description.
Steps
-
Verify binary install. The factory runs via the dark-factory binary
(not python -m runner from source). Check:
export DARK_FACTORY_HOME="${DARK_FACTORY_HOME:-$HOME/projects/dark-factory}"
export PATH="$HOME/.local/bin:$PATH"
command -v dark-factory && dark-factory --help 2>/dev/null || true
test -x "$DARK_FACTORY_HOME/bin/dark-factory" || {
echo "ERROR: run $DARK_FACTORY_HOME/install.sh first"
exit 1
}
If missing, tell the user to clone
https://github.com/jleechanorg/dark-factory and run ./install.sh, then stop.
-
Environment:
export DARK_FACTORY_HOME="${DARK_FACTORY_HOME:-$HOME/projects/dark-factory}"
export DARK_FACTORY_HOLDOUTS="${DARK_FACTORY_HOLDOUTS:-$HOME/projects/dark-factory-holdouts}"
export PATH="$HOME/.local/bin:$PATH"
-
Run the pipeline from the target repo (implementation workdir = cwd).
Pipelines and prompts resolve from $DARK_FACTORY_HOME; code changes land in cwd:
cd "<TARGET_REPO>"
dark-factory \
--pipeline pipelines/<PATH_TO_DOT>.dot \
--goal "<GOAL>" \
--backend <BACKEND> \
--feature <FEATURE> \
--cxdb <CXDB_PATH> \
--state <KEY>=<VALUE>
-
Surface the verdict. The last line of stdout is a JSON summary with
final_outcome, pipeline, goal, steps, trace. Report:
- Pipeline name + final outcome
- Per-node trace (one line each:
node outcome preview)
- Exit code of the runner
-
Run the Healer (only if final_outcome != "success"):
df-healer --cxdb <CXDB_PATH>
Render the resulting markdown table inline. Each row is a failure cluster
with a prescription template the user can act on.
-
Sealed holdouts reminder. If the run included a holdout_eval node,
remind the user that you (and any subagent) did NOT see the scenarios at
~/projects/dark-factory-holdouts/holdouts/<feature>/ — only the
PASS/FAIL verdict per scenario name leaked back.
Output contract
End every /factory invocation with:
Pipeline: <name>
Final outcome: PASS | FAIL | exhausted | stuck
Steps: <n>
CXDB: <path> (run `df-healer --cxdb <path>` to re-cluster)
Plus the trace and, on failure, the Healer report.
Adding a new feature
To add a new sealed-holdout feature foo:
- Write
~/projects/dark-factory/specs/foo.md (agent-visible).
- Write
~/projects/dark-factory/prompts/foo/{plan,implement,fix}.md.
- Write
~/projects/dark-factory-holdouts/holdouts/foo/scenarios.yaml
in the sealed repo (the implementing agent must never read this).
- Either reuse
pipelines/factory/hello.dot (which is feature-agnostic) by
passing --feature foo, or write pipelines/factory/foo.dot with custom
nodes.
Honesty rules
- Always tell the user the actual command you ran (
dark-factory ... or df-healer ...). No paraphrasing.
- Always quote the runner's exit code verbatim.
- Never claim a holdout passed if you read the scenarios — that breaks the
adversarial guarantee. If you read
~/projects/dark-factory-holdouts/,
declare the run invalid and rerun the pipeline.
- If
--backend echo was used, label the run as a wiring smoke, not a real
validation.
Known limits
- The
claude backend shells out to claude --print --dangerously-skip-permissions — each node is a fresh non-interactive
session. Long agentic loops should use /h instead.
_parse_verdict falls back to scanning the tail for a standalone PASS/FAIL
token; if the gate command emits a debug line with just "PASS" the runner
will trust it. For high-stakes gates require explicit VERDICT: markers.
- CXDB is per-process — don't share one across threads.