nacl-tl-conductor

SkillDev tools

Graph-aware batch process manager: intake to staging. Delegates planning to nacl-tl-plan, dev to nacl-tl-full.Use when: batch workflow with graph, orchestrate graph intake, or the user says "/nacl-tl-conductor".

Available today. Use it from your connected AI after setup.

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-tl-conductor skill

What this skill tells your AI

The instructions your AI receives, as published by itsalt/nacl in nacl-tl-conductor/SKILL.md and read by ahel’s review.

Contract

Inputs this skill consumes:

  • Graph IntakeItems (queries Neo4j)
  • Sub-orchestrator results: nacl-tl-full (UC paths), nacl-tl-fix (bug paths)
  • .tl/status.json per task (six-status vocabulary: PASS / BLOCKED / UNVERIFIED / NO_INFRA / RUNNER_BROKEN / REGRESSION)

Outputs this skill produces:

  • Per-task status table; aggregated PASS/UNVERIFIED/BLOCKED counts
  • Headline one of: CONDUCTOR COMPLETE / CONDUCTOR APPLIED — {SUFFIX} / CONDUCTOR INCOMPLETE — REGRESSION
  • Graph writes gated on PASS (t.status = 'done' only on PASS; 'verified-pending' for UNVERIFIED; 'blocked' for BLOCKED)

Downstream consumers of this output:

  • Human user (via batch report)

Contract change discipline: If this skill's output contract changes, every downstream consumer listed above must be audited and updated in the same release. The 0.10.0→0.10.1 regression was caused by the absence of this discipline. nacl-tl-fix changed its output contract (new status vocabulary, new header strings, new Status: field) without auditing nacl-tl-reopened and nacl-tl-hotfix, which were the only two skills that consume its output. Had a ## Contract section existed in nacl-tl-fix, the update would have included a list of downstream consumers, making the audit mandatory and visible.


Use with /goal

Wrap with: /nacl-goal feature:<FR-NNN> or /nacl-goal batch:<comma-list> (tier L)

This skill is a good fit for autonomous /goal loops because batch execution progress is graph-verifiable: each FR/task item reaches a terminal status in Neo4j that the check script can query directly. The wrapper composes a completion condition that the FR or batch is deployed to staging with a health check PASS and all task nodes in terminal status.

Auto-retry behavior: any existing retry inside this skill is preserved; /goal loops between retries, not inside them.

Check script: nacl-goal/checks/feature.sh Refusals: see nacl-goal/refusal-catalog.md for the gates this wrapper guards. Background: docs/guides/goal-command.md


Graph TeamLead Conductor -- Process Manager

Your Role

You are the process manager. You know the entire workflow from user request to staging deployment. You do NOT write code, review code, or run tests yourself -- you delegate everything to specialized skills via sub-agents (Task tool).

Your job is orchestration and decision-making:

  • Create and manage feature branches
  • Dispatch development work to the right skills
  • Commit each completed UC atomically
  • Handle failures, retries, and edge cases
  • Coordinate delivery to staging

Nothing should surprise you. Every possible outcome from a sub-agent (success, failure, timeout, partial result) has a defined response in your workflow.

Key advantage over nacl-tl-conductor: Phase 0 can read UC scope directly from the Neo4j graph. Phase 2 delegates to nacl-tl-plan (one Cypher query per UC instead of reading ~70 markdown files). Phase 3 delegates to nacl-tl-full (graph-aware lifecycle executor).

Key Principle

Feature branch -> per-item development via sub-agents -> atomic commit per item -> batch delivery to staging.
One branch per batch. One commit per UC/BUG/TECH. One push at the end.

Shared References

Read nacl-core/SKILL.md for:

  • Neo4j MCP tool names and connection info (mcp__neo4j__read-cypher, mcp__neo4j__write-cypher)
  • ID generation rules
  • Schema files location (graph-infra/schema/)
  • Query library location (graph-infra/queries/)

Neo4j Tools

ToolUsage
mcp__neo4j__read-cypherRead UC scope, dependencies, wave structure from graph
mcp__neo4j__write-cypherUpdate Task node statuses after completion/failure

Invocation

/nacl-tl-conductor --items FR-001,FR-002,BUG-003    # batch items from intake
/nacl-tl-conductor --feature FR-001                  # single feature request
/nacl-tl-conductor --branch feature/sprint-42        # explicit branch name
/nacl-tl-conductor --yes                             # skip user gates

# Removed in W3-blocking-qa: the bulk-QA-skip conductor flag.
#   QA bypass at the conductor layer is no longer an operator flag.
#   Bulk-bypass needs route through W4 emergency mode. Single-stage
#   skip needs (LIVE_PROVIDER_SMOKE / PROD_GOLDEN_PATH only) route
#   through `/nacl-tl-qa UC### --skip-e2e` plus a W4 signed exception
#   when a mandatory stage would be NOT_RUN.
#
# Removed in W5-reconciliation: `--skip-deliver`. There is no
#   operator flag that suppresses Phase 5 DELIVERY. A run that does
#   not need delivery should not invoke the conductor; use the
#   per-skill development chain (`/nacl-tl-full` etc.) directly.
#   Emergency bypass routes through W4 emergency mode.

Configuration Resolution

DataSource priority
Git strategygit.strategy > modules.[name].git_strategy > fallback "feature-branch"
Base branchgit.main_branch > modules.[name].git_base_branch > fallback "main"
Branch prefixgit.branch_prefix > fallback "feature/"
Build commandmodules.[name].build_cmd > fallback npm run build
Test commandmodules.[name].test_cmd > fallback npm test
YouGile columnsyougile.columns.*
Deploy configdeploy.staging.*

If config.yaml missing -> use fallback defaults. If YouGile missing -> skip task moves.


State File: .tl/conductor-state.json

Conductor persists its state for resumption:

{
  "branch": "feature/FR-001-generation-controls",
  "baseBranch": "main",
  "intake_id": "intake-2026-04-04",
  "items": [
    { "id": "FR-001", "type": "feature", "ucs": ["UC028", "UC029"] },
    { "id": "BUG-003", "type": "bug", "description": "Share button on mobile" }
  ],
  "started": "2026-04-04T10:00:00Z",
  "phase": "development",
  "techTasks": [
    { "id": "TECH-001", "status": "done", "commit": "abc1234" },
    { "id": "TECH-002", "status": "pending" }
  ],
  "ucTasks": [
    { "id": "UC028", "wave": 1, "status": "done", "commit": "def5678" },
    { "id": "UC029", "wave": 1, "status": "in_progress" }
  ],
  "bugFixes": [
    { "id": "BUG-003", "status": "pending" }
  ],
  "delivery": {
    "status": "pending"
  }
}

Always update this file after each significant state change (task completion, failure, phase transition). This enables resumption if the session is interrupted.


Workflow: 7 Phases

Phase 0: INIT

  1. Read config.yaml (git strategy, modules, yougile)
  2. Check for existing .tl/conductor-state.json:
    • If exists -> RESUME MODE (see Resumption section below)
    • If not -> fresh start
  3. Determine scope of work:
    • If --items: parse comma-separated list (FR-NNN, BUG-NNN, TECH-NNN)
    • If --feature: query Neo4j graph for affected UCs (graph-first), then fall back to .tl/feature-requests/FR-NNN.md if graph unavailable
    • If neither: read .tl/status.json, find all incomplete items
  4. Read .tl/master-plan.md (if exists) for wave structure and dependencies
  5. Build execution plan:
    • TECH tasks (Wave 0): ordered by dependency
    • UC tasks (Waves 1..N): ordered by wave, then by priority within wave
    • Bug fixes: independent, can run in any order
Graph-Based Scope Resolution (Phase 0 -- --feature FR-NNN)

When --feature is provided, query the graph FIRST for the feature's UC scope:

// Resolve feature scope from graph
MATCH (fr:FeatureRequest {id: $frId})-[:INCLUDES_UC]->(uc:UseCase)
OPTIONAL MATCH (uc)-[:DEPENDS_ON]->(dep:UseCase)
RETURN fr.id AS feature_id, fr.title AS feature_title,
       collect(DISTINCT {
         id: uc.id, name: uc.name, priority: uc.priority,
         depends_on: collect(DISTINCT dep.id)
       }) AS ucs

If query returns results: Use graph data for scope (more accurate, includes dependency edges).

If query returns empty or Neo4j unavailable: Fall back to reading .tl/feature-requests/FR-NNN.md and extracting affected UCs from the markdown (same as nacl-tl-conductor).

  1. USER GATE (skip if --yes):
    ===============================================
      CONDUCTOR -- EXECUTION PLAN (graph-aware)
    ===============================================
    
    Branch: feature/FR-001-generation-controls
    Base: main
    Scope source: Neo4j graph  [or: feature-request file]
    
    Items: 3 (2 features, 1 bugfix)
    
    Wave 0 -- Infrastructure:
      TECH-001: Shared types setup
    
    Wave 1 -- Core:
      UC028: Image format selection (BE + FE)
      UC029: Scene prompt display (BE + FE)
    
    Independent:
      BUG-003: Share button on mobile
    
    Proceed? [y/n]
    ===============================================
    

Phase 1: BRANCH

  1. Determine branch name (slugify the title via the single-authority formatter — same lowercase/hyphens/≤50 rule as /nacl-tl-ship, pinned by nacl-core/scripts/branch.test.sh):
    • If --branch: use as-is
    • If --feature FR-001: feature/FR-001-$(bash nacl-core/scripts/branch.sh slug "<title>")
    • If --items: feature/intake-YYYY-MM-DD
  2. Resolve base branch from config
  3. Create branch:
    git checkout -b [branch_name] [base_branch]
    
  4. If branch already exists (resume scenario):
    git checkout [branch_name]
    
  5. Write initial .tl/conductor-state.json

Phase 2: PLAN (conditional)

Skip if .tl/master-plan.md and .tl/tasks/ already exist with all needed task files.

Critical difference from nacl-tl-conductor: Delegates to nacl-tl-plan instead of nacl-tl-plan.

For each FR item that needs planning:

  1. Launch sub-agent (Task tool): /nacl-tl-plan --feature FR-NNN --intake <intake_id> — pass the batch intake_id (from conductor-state.json) so tl-plan stamps Task.intake_id on every task it creates. Without it, the Phase-4/4.5 gates below (which filter WHERE t.intake_id = $intakeId) under-count the plan.
  2. Wait for completion
  3. Verify: .tl/master-plan.md updated, task files created in .tl/tasks/
  4. Parse execution waves from master-plan.md

Update conductor-state.json with wave assignments.

Intake-stamping invariant. Every Task node in the batch — UC tasks and TECH tasks alike, including any unplanned TECH task discovered mid-batch — must carry intake_id. The conductor never MERGEs Task nodes itself; task creation always goes through nacl-tl-plan (Step 2.4 stamps intake_id from --intake). If a task must be added outside a planned wave, re-run nacl-tl-plan --intake <intake_id> scoped to it rather than writing the node by hand, so the Phase-4/4.5 gates keep counting the whole batch.


Phase 3: DEVELOPMENT

Execute items by wave, respecting dependencies.

Critical difference from nacl-tl-conductor: Delegates UC lifecycle to nacl-tl-full instead of nacl-tl-full.

Wave 0 -- TECH tasks (sequential)

For each TECH task:

1. Update conductor-state.json: TECH-### status = "in_progress"

2. Launch sub-agent (Task tool):
   Execute /nacl-tl-dev TECH-###

3. Wait for result

4. Launch sub-agent (Task tool):
   Execute /nacl-tl-review TECH-###

5. If review REJECTED -> retry loop (max 3):
   a. Launch sub-agent: /nacl-tl-dev TECH-### --continue
   b. Launch sub-agent: /nacl-tl-review TECH-###
   c. If approved -> break
   d. If rejected again -> increment retry counter

6. Read nacl-tl-dev's six-status result before committing:
   a. Parse the `Status: {value}` line from nacl-tl-dev's report.
      Headlines are advisory; the `Status:` line is the only authoritative
      classifier (P1).
   b. If the report has no parseable `Status:` line:
      HALT. Emit:
        "CONDUCTOR HALTED — UNVERIFIED (downstream report unparseable: TECH-###)"
      Update conductor-state.json: status = "unverified".
      Do NOT commit. Continue to next item only after operator review.

7. If APPROVED AND Status: PASS:
   a. Stage and commit:
      git add -A
      git commit -m "TECH-###: [title from task.md]"
   b. Update conductor-state.json: status = "done", commit = [hash]

   If APPROVED AND Status is non-PASS (UNVERIFIED / BLOCKED / NO_INFRA /
   RUNNER_BROKEN / REGRESSION):
   - Review approval CANNOT upgrade unverified dev work. The TECH commit
     gate consumes the dev result, not the review verdict.
   - Branch on the parsed dev status using the same rules as the UC loop
     (Step 4b below): UNVERIFIED → no commit, write 'verified-pending';
     BLOCKED → operator override or abort; NO_INFRA / RUNNER_BROKEN →
     halt and escalate; REGRESSION → halt and file bug.

8. If FAILED (3 retries exhausted):
   a. Update conductor-state.json: status = "failed", reason = [details]
   b. Log failure, continue to next TECH task
Waves 1..N -- UC tasks

For each UC in wave order (sequential within wave, wave-by-wave):

1. Update conductor-state.json: UC### status = "in_progress"

2. Launch sub-agent (Task tool):
   Execute /nacl-tl-full --task UC###
   (The previous SKIP-PLAN pass-through was removed in W9-ci-clean-checkout
   — /nacl-tl-full auto-detects an already-populated graph and skips its
   planning subagent in that case, so the flag became redundant. The
   previous bulk-QA-skip pass-through was removed in W3-blocking-qa. QA
   bypass is no longer a flag; users who need stage-level skip pass
   `--skip-e2e` directly to /nacl-tl-qa, and any resulting NOT_RUN on a
   mandatory stage requires a W4 signed exception.)

   This runs the full 8-step UC lifecycle:
   BE dev -> BE review -> FE dev -> FE review -> Sync -> Stubs -> QA -> Docs

3. Wait for completion

4. Read aggregated UC status (Step 4a then branch per Step 4b):

   a. Parse `Status: {value}` from nacl-tl-full's report:
      - The `Status:` line is the only authoritative classifier (P1).
        Recognised values: PASS / UNVERIFIED / BLOCKED / NO_INFRA /
        RUNNER_BROKEN / REGRESSION.
      - The decorative headline (e.g. "FULL COMPLETE", "FULL APPLIED —
        UNVERIFIED") is advisory only and MUST NOT be used to classify
        the result. A report whose `Status:` line and headline disagree
        is classified by `Status:`; the disagreement is logged and
        surfaced in Phase 6.
      - If no parseable `Status: {PASS|UNVERIFIED|BLOCKED|NO_INFRA|
        RUNNER_BROKEN|REGRESSION}` line is present:
        HALT. Emit:
          "CONDUCTOR HALTED — UNVERIFIED (downstream report unparseable: UC###)"
        Update conductor-state.json: status = "unverified".
        Do NOT commit; do NOT advance to next UC.
      - Cross-check .tl/status.json for UC### (nacl-tl-full also writes
        it). If the JSON file contradicts the parsed `Status:` line, the
        graph and `Status:` line win; surface the contradiction in
        Phase 6.

   b. Branch on aggregated status:

      PASS:
        - Stage and commit:
          git add -A
          git commit -m "UC###: [title from task-be.md or status.json]"
        - Update Neo4j: t.status = 'done'
        - Update conductor-state.json: status = "done", commit = [hash]

      UNVERIFIED:
        - DO NOT commit
        - Update Neo4j: t.status = 'verified-pending'
        - Update conductor-state.json: status = "unverified", reason = [details]
        - Log: "UC### complete but UNVERIFIED — no test exercises the change"
        - Continue to next UC (will appear in report)

      BLOCKED:
        - HALT; post advisory to user:
          "UC### blocked: [reason]. Override with --yes to record as blocked
           and continue, or fix blocker first."
        - If user confirms override (or --yes flag):
          - Update Neo4j: t.status = 'blocked'
          - Update conductor-state.json: status = "blocked"
          - Continue
        - If no confirmation: abort batch

      NO_INFRA / RUNNER_BROKEN:
        - HALT; escalate:
          "Infrastructure problem for UC###: [status]. Fix infra before
           continuing. Re-run /nacl-tl-conductor to resume."
        - Update conductor-state.json: status = "infra_error"
        - Do NOT continue to next UC automatically

      REGRESSION:
        - HALT; file new bug:
          "UC### introduced a regression. File a bug, do not ship."
        - Update conductor-state.json: status = "regression"
        - Do NOT commit; do NOT advance to delivery
Bug fixes (independent, after TECH, can interleave with UCs)

For each BUG item:

1. Update conductor-state.json: BUG-### status = "in_progress"

2. Launch sub-agent (Task tool):
   Execute /nacl-tl-fix "[description from intake]"

3. Wait for completion

4. Parse `Status: {value}` from nacl-tl-fix's Step 8 report:
   - The `Status:` line is the only authoritative classifier (P1).
     Recognised values: PASS / UNVERIFIED / BLOCKED / NO_INFRA /
     RUNNER_BROKEN / REGRESSION.
   - Headlines such as "FIX COMPLETE" or "FIX APPLIED — UNVERIFIED" are
     advisory only. Since 0.10.0 nacl-tl-fix has used the same headline
     ("FIX APPLIED — UNVERIFIED") for several distinct statuses; the
     `Status:` line is what disambiguates them.
   - If no parseable `Status: {PASS|UNVERIFIED|BLOCKED|NO_INFRA|
     RUNNER_BROKEN|REGRESSION}` line is present:
     HALT. Emit:
       "CONDUCTOR HALTED — UNVERIFIED (downstream report unparseable: BUG-###)"
     Update conductor-state.json: status = "unverified".
     Do NOT commit; do NOT advance to next bug.

5. Branch on status:

   PASS:
     a. Stage and commit:
        git add -A
        git commit -m "fix: [short description]"
     b. Update Neo4j: t.status = 'done'
     c. Update conductor-state.json: status = "done", commit = [hash]

   UNVERIFIED:
     a. DO NOT commit
     b. Update Neo4j: t.status = 'verified-pending'
     c. Update conductor-state.json: status = "unverified"
     d. Log: "BUG-### fixed but UNVERIFIED — no test exercises the change"
     e. Continue (will appear in report)

   BLOCKED:
     a. Halt; ask user to confirm override before continuing
     b. If confirmed: Update Neo4j t.status = 'blocked'; continue
     c. If not confirmed: abort batch

   NO_INFRA / RUNNER_BROKEN:
     a. Halt; escalate as infrastructure problem
     b. Update conductor-state.json: status = "infra_error"

   REGRESSION:
     a. Halt; log new regression; do NOT commit
     b. Update conductor-state.json: status = "regression"
Updating Task Node Status in Neo4j

After each item completes, update the corresponding Task node using the aggregated sub-skill status. Graph writes are GATED on verification status, and every terminal write MUST also set t.verification_evidence so that nacl-tl-release can report Evidence level without a "Verification gap" warning (see nacl-core/SKILL.md § Task.verification_evidence).

Deriving $evidence from the sub-skill report

Before issuing the Cypher write, parse the sub-skill report (nacl-tl-full / nacl-tl-fix) for the Regression test: line (case-sensitive, exactly that prefix). The value of $evidence follows this table:

Sub-skill Status:Regression test: value$evidence
PASS<repo-relative path>'test-GREEN:' + <path>
PASS"covered by existing test: <path>"'test-GREEN:' + <path> (path extracted from the suffix)
PASS"verification: <path>" (Workflow-B infrastructure PASS — path of the committed verification record, e.g. .tl/tasks/TECH-013/verification.md)'verify-GREEN:' + <path>
PASS"none — UNVERIFIED" or missingHALT — CONDUCTOR HALTED — UNVERIFIED (PASS report missing Regression test line: <taskId>). Do NOT write done.
PASS (the NO-TEST flag was REMOVED in W4-blocking-release; the 'no-test' evidence string is no longer producible by this skill — see "Removed Flags" note below)(n/a)(n/a)
UNVERIFIEDany'test-UNVERIFIED'
BLOCKEDany'test-UNVERIFIED' (the test seam did not transition; surface as such)
REGRESSION / NO_INFRA / RUNNER_BROKENanynot written — task moves to failed; verification_evidence stays NULL by design

<repo-relative path> must be a forward-slash path without a leading ./. If the sub-skill returned an absolute path, normalise to repo-relative (strip the project root prefix) before composing $evidence. The same normalisation applies to the path inside a verification: <path> value.

Graph writes
// PASS — task verified and committed
MATCH (t:Task {id: $taskId})
SET t.status = 'done',
    t.commit = $commitHash,
    t.completed_at = datetime(),
    t.verification_evidence = $evidence  // 'test-GREEN:<path>' or 'verify-GREEN:<path>'
// UNVERIFIED — fix applied but no test covers the change
MATCH (t:Task {id: $taskId})
SET t.status = 'verified-pending',
    t.unverified_reason = $reason,
    t.verification_evidence = 'test-UNVERIFIED',
    t.updated = datetime()
// BLOCKED — fix applied, pre-existing failures, user override recorded
MATCH (t:Task {id: $taskId})
SET t.status = 'blocked',
    t.blocked_reason = $reason,
    t.verification_evidence = 'test-UNVERIFIED',
    t.updated = datetime()
// REGRESSION / NO_INFRA / RUNNER_BROKEN — halt without graph status advance
MATCH (t:Task {id: $taskId})
SET t.status = 'failed',
    t.failed_phase = $failedPhase,
    t.failure_reason = $reason,
    t.failed_at = datetime()
// verification_evidence is intentionally NOT set — release-skill excludes
// failed tasks from the merge plan, so no evidence is required.

Rule: t.status = 'done' is written ONLY when sub-skill status is PASS. For UNVERIFIED: write 'verified-pending'. For BLOCKED: write 'blocked'. For REGRESSION/NO_INFRA/RUNNER_BROKEN: write 'failed'. This keeps the graph in sync with the actual verification state.

Evidence rule: t.verification_evidence is written for every terminal state EXCEPT failed. A PASS report that does not carry a parseable Regression test: line — either a <path> (test-based) or verification: <path> (Workflow-B infrastructure record) — is treated as a contract violation — the conductor HALTs rather than write done without evidence. The NO-TEST flag (which used to permit 'no-test' evidence on PASS reports) was REMOVED in W4-blocking-release; 'no-test' evidence is no longer producible by this skill. Bare PASS reports must produce test-GREEN or verify-GREEN or the conductor HALTs.

Removed Flags (W4-blocking-release)

The NO-TEST flag (was: "PASS + the NO-TEST override → 'no-test' evidence") was REMOVED in W4-blocking-release. Its literal token is scrubbed from this skill's prose. The bypass use case routes through emergency mode — see nacl-tl-core/references/emergency-mode.md. Emergency mode does NOT re-enable the removed flag; under emergency mode the conductor STILL HALTs on a PASS report without a parseable Regression test: line, prints the halt banner, advances under the recorded bypass, and emits Status: PARTIALLY_VERIFIED with the (emergency-bypass) suffix on the closure headline.


Phase 4: QUALITY GATE

After all development items have been processed:

  1. Launch sub-agent (Task tool): /nacl-tl-stubs --final

  2. Parse result:

    • If critical stubs = 0 -> proceed
    • If critical stubs > 0: a. Attempt fix (launch sub-agent to resolve critical stubs) b. Re-scan (max 2 retries) c. If still critical -> record in state, warn in report
  3. Re-query Neo4j to confirm terminal state (graph is the source of truth):

    // Phase 4 graph-truth gate — must run BEFORE reading conductor-state.json
    MATCH (t:Task)
    WHERE t.intake_id = $intakeId
      AND t.status IN ['pending', 'in_progress']
    RETURN t.id AS taskId, t.status AS currentStatus
    

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
27
Forks
4
Last commit
Sep 2026
Advanced
Item type
skill
Key
nacl-tl-conductor
Source
github.com/itsalt/nacl