/nacl-migrate-ba — BA Markdown → Neo4j
SkillDocs & knowledgeMigrate an OLD-methodology BA layer from Markdown to Neo4j. Parses docs/00-context, docs/01-business-processes, docs/02-business-entities, docs/03-business-roles, docs/04-business-rules, docs/99-meta/glossary via deterministic Python scripts (no LLM parsing), then writes BA nodes and relationships to the graph via mcp__neo4j__write-cypher. Use when: migrate BA from markdown, import old BA into graph, nacl-migrate-ba. Fails loudly on unknown formats, add a new adapter rather than guessing.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the /nacl-migrate-ba skill
What this skill tells your AI
The instructions your AI receives, as published by itsalt/nacl in nacl-migrate-ba/SKILL.md and read by ahel’s review.
Role
You delegate parsing, validation, and Cypher generation to stdlib Python
scripts under $NACL_HOME/nacl-migrate-ba/scripts/. You never parse
Markdown yourself; the scripts own that entirely. Your own job is:
- Run the scripts in order.
- Read their JSON outputs.
- Execute the Cypher plan against Neo4j via
mcp__neo4j__write-cypher. - Gather live counts via
mcp__neo4j__read-cypherand hand them toaudit_ba.py. - Present results and errors to the user in plain language.
The scripts produce structured JSON with status, error_code, message,
and remediation. When a script fails, print the message and remediation
verbatim.
Invocation
/nacl-migrate-ba [project_path] [--dry-run] [--adapter=<name>]
| Parameter | Required | Description |
|---|---|---|
project_path | No | Project root (default: cwd). Must contain docs/. |
--dry-run | No | Run parse + validate + Cypher generation; skip all Neo4j writes and audit. |
--adapter | No | Force an adapter (inline-table-v1 | frontmatter-v1). Default: auto-detect. |
Prelude — locate NaCl home and verify Python
Run this first. Emit the messages verbatim on failure.
# Resolve $NACL_HOME
if [ -z "$NACL_HOME" ]; then
for candidate in "$HOME/projects/NaCl" "$HOME/NaCl" "$HOME/code/NaCl" "$HOME/src/NaCl"; do
if [ -f "$candidate/nacl-migrate-core/nacl_migrate_core/__init__.py" ]; then
export NACL_HOME="$candidate"
break
fi
done
fi
if [ -z "$NACL_HOME" ] || [ ! -f "$NACL_HOME/nacl-migrate-core/nacl_migrate_core/__init__.py" ]; then
echo "Unable to locate NaCl repo. Set NACL_HOME=/path/to/NaCl and retry." >&2
exit 1
fi
# Verify Python 3.11+
python3 -c 'import sys; sys.exit(0 if sys.version_info >= (3,11) else 1)' 2>/dev/null \
|| { echo "Python 3.11+ required. On macOS: brew install python@3.11"; exit 1; }
All subsequent commands use $NACL_HOME/nacl-migrate-ba/scripts/....
Soft preflight (direct invocation)
When invoked directly (i.e. not via the /nacl-migrate orchestrator),
run a quick ID-pattern scan and warn — but do not block — if any token in
the project's filenames or YAML frontmatter does not match a known adapter
pattern. The orchestrator path runs the same scan at Phase A.5 with a
hard gate; this version is non-blocking so a single-layer rerun stays
ergonomic.
mkdir -p .nacl-migrate
python3 "$NACL_HOME/nacl-migrate-ba/scripts/preflight_ids.py" \
--project "$PWD" \
--output .nacl-migrate/preflight.json || true
Exit code 1 from the script means unknown patterns were found. Surface
the report's patterns_unknown list to the user with this message:
Preflight surfaced N unknown ID-shaped tokens. These will likely be dropped at parse. Review
.nacl-migrate/preflight.jsonand either widen an adapter (recommended) or pass--forceto proceed anyway.
If the user passes --force, continue. Otherwise stop.
Preconditions
config.yamland.mcp.jsonexist at project root (produced bynacl-init).- Neo4j is reachable via
mcp__neo4j__get-schema— run it once before Phase 1 and stop if it errors. - At least one BA folder exists under
docs/. If none, skip this skill with: "No BA layer detected underdocs/. This project may be SA-only. Skippingnacl-migrate-ba."
Phase 0 — Detect adapter
python3 "$NACL_HOME/nacl-migrate-ba/scripts/detect_ba.py" \
--project "$PWD" \
--output .nacl-migrate/detect-ba.json
Read the JSON.
status: "ok"+chosen: "<name>"+ambiguous: false→ proceed with that adapter.ambiguous: true→ show the candidate list to the user; ask them to pass--adapter=<name>.chosen: null+ambiguous: false→ no adapter matches; stop and ask the user to add a new adapter pernacl-migrate-core/README.md.reason: "no_ba_files_found"→ skip the skill (see preconditions above).status: "error"→ printmessage+remediationand stop.
If --adapter was passed, skip this phase and use the user-provided value.
Phase 1 — Parse Markdown → IR
python3 "$NACL_HOME/nacl-migrate-ba/scripts/parse_ba.py" \
--project "$PWD" \
--adapter "<chosen_adapter>" \
--output .nacl-migrate/ba-ir.json
Expected stdout: a counts table and (optionally) a warnings tally. Confirm
warnings: 0 or surface each warning code to the user.
On error exit (code ≠ 0), surface message + remediation and stop.
Phase 2 — Validate IR
python3 "$NACL_HOME/nacl-migrate-ba/scripts/validate_ba_ir.py" \
--input .nacl-migrate/ba-ir.json \
--output .nacl-migrate/ba-validation.json
Validator runs 10 checks (V1–V10) covering ID uniqueness, reference integrity, orphan states, source-file presence.
- Exit 0 → all pass, continue.
- Exit 1 → at least one check failed. Print the failure list from stdout and stop. If the user wants to proceed anyway, they must fix the IR or rerun with adapter adjustments.
Phase 3 — Generate Cypher plan
python3 "$NACL_HOME/nacl-migrate-ba/scripts/generate_ba_cypher.py" \
--input .nacl-migrate/ba-ir.json \
--output .nacl-migrate/ba-cypher.json
Plan is an array of batches, each with cypher, params, kind (node or
edge) and label (human-readable). Batches are sized so one MCP call per
batch is sub-second.
If --dry-run: stop here. Report the plan's summary to the user and exit.
Phase 4 — Execute Cypher against Neo4j
Preflight:
mcp__neo4j__get-schema() # must succeed
If it errors, stop and tell the user to start the Neo4j stack.
Execute:
Read .nacl-migrate/ba-cypher.json. For each batch in batches, call:
mcp__neo4j__write-cypher(
query = batch.cypher,
params = batch.params
)
Report progress per batch: [i/N] {label} rows={len(params.rows)} ok (or fail: <msg>).
On any batch failure:
- Log the batch index, label, and the Cypher error.
- Stop execution. The partial graph is idempotent — rerunning after a fix will MERGE over the already-written nodes without duplicating.
Phase 5 — Gather live counts
Query Neo4j via mcp__neo4j__read-cypher for every node label and
relationship type listed in audit_ba.py (keep this in sync — the script
declares canonical lists).
For each node label:
MATCH (n:{Label}) RETURN count(n) AS n
For each relationship type:
MATCH ()-[r:{TYPE}]->() RETURN count(r) AS n
Write the results to .nacl-migrate/ba-live-counts.json:
{
"nodes": {"BusinessProcess": 10, "WorkflowStep": 74, ...},
"relationships": {"CONTAINS": 10, "HAS_STEP": 74, ...}
}
Batch the reads if convenient (single UNION query) — the shape of the
output file is the only hard requirement.
Phase 6 — Audit
python3 "$NACL_HOME/nacl-migrate-ba/scripts/audit_ba.py" \
--ir .nacl-migrate/ba-ir.json \
--counts .nacl-migrate/ba-live-counts.json \
--output .nacl-migrate/ba-audit.json
Exit 0 → "All counts match." — migration is clean. Exit 1 → at least one mismatch. Surface the mismatch list verbatim and ask the user to investigate before declaring migration complete.
Phase 7 — Report
Write MIGRATION-REPORT-BA.md at project root. Template:
# BA Migration Report
**Project:** {project_path}
**Adapter:** {adapter}
**Ran at:** {ISO timestamp}
## IR counts vs live Neo4j
| Node label | IR | Live | Status |
| ... | ... | ... | ... |
(Copy the table from ba-audit.json.)
## Relationships
| Type | IR | Live | Status |
| ... | ... | ... | ... |
## Warnings during parse
{list from ba-ir.json warnings[], or "none"}
## Validation
{summary from ba-validation.json: passed/failed, any failures}
## Next steps
- If audit is clean: proceed to `/nacl-migrate-sa` (if SA layer exists)
or continue to `/nacl-migrate` aggregation.
- If audit has mismatches: investigate each mismatched label via Cypher.
Rerun after fixes — writes are idempotent.
Idempotency
- All Neo4j writes are
MERGEkeyed on canonical IDs; safe to re-run. - Re-detection and re-parse produce the same IR (modulo timestamps).
- If the adapter or the source Markdown changes and yields a different
canonical ID for a given source file, the old node becomes orphaned.
audit_ba.pydetects this as a count mismatch — investigate rather than auto-delete.
Error handling summary
| Situation | Response |
|---|---|
Prelude can't find $NACL_HOME | Stop. Ask user to export NACL_HOME. |
| Python < 3.11 | Stop. Ask user to upgrade. |
| Adapter unknown / ambiguous | Stop. Present candidates. |
| Parse warnings | Continue. Surface tally to user. |
| Validation failure | Stop. Print failed checks. |
| Neo4j unreachable | Stop before any write. |
| Individual batch failure | Stop after that batch; partial writes are safe to resume. |
| Audit mismatch | Report. Do not claim success. |
Reads / Writes
Reads
{project}/docs/**/*.mdmcp__neo4j__get-schema(preflight)mcp__neo4j__read-cypher(live counts in Phase 5)
Writes
{project}/.nacl-migrate/detect-ba.json{project}/.nacl-migrate/ba-ir.json{project}/.nacl-migrate/ba-validation.json{project}/.nacl-migrate/ba-cypher.json{project}/.nacl-migrate/ba-live-counts.json{project}/.nacl-migrate/ba-audit.json{project}/MIGRATION-REPORT-BA.mdmcp__neo4j__write-cypher(Phase 4)
Checklist
- Prelude:
$NACL_HOMEresolved, Python 3.11+ present - Phase 0: adapter detected or user supplied
- Phase 1: IR emitted, zero warnings (or tally surfaced)
- Phase 2: V1–V10 all pass
- Phase 3: Cypher plan generated
- Phase 4: every batch executed via
mcp__neo4j__write-cypher - Phase 5: live counts gathered
- Phase 6: audit clean
- Phase 7:
MIGRATION-REPORT-BA.mdwritten
Signals
- GitHub stars
- 27
- Forks
- 4
- Last commit
- Sep 2026
ahel review
K1binfo
installs-packages
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Item type
- skill
- Key
nacl-migrate-ba- Source
- github.com/itsalt/nacl