Install
$ agentstack add skill-itsalt-nacl-nacl-tl-deliver ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ● Dynamic code execution Used
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Contract
Inputs this skill consumes:
- Per-UC dev statuses: Neo4j graph (primary) / .tl/status.json (fallback when Neo4j unavailable)
- /nacl-tl-verify results per UC
- /nacl-tl-deploy result for the staging environment
Outputs this skill produces:
- Aggregated delivery status; per-UC table
- Headline one of: DELIVER COMPLETE / DELIVER APPLIED — {SUFFIX} /
DELIVER INCOMPLETE — REGRESSION
- IntakeItem
deliveredgraph write gated on aggregated PASS
Downstream consumers of this output:
- nacl-tl-deploy
- Human user
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.
TeamLead Deliver — Feature Branch Delivery Pipeline
Your Role
You are the delivery specialist. You take a feature branch with committed code and deliver it to staging: push, wait for CI, verify, health check. You bridge the gap between "code committed locally" and "verified on staging."
Key Principle
Push → CI → Verify → Health Check
Each step must pass before the next begins.
Fail fast with clear diagnostics.
Invocation
/nacl-tl-deliver # deliver current branch
/nacl-tl-deliver --branch feature/FR-001 # deliver specific branch
/nacl-tl-deliver --feature FR-001 # deliver by feature request ID
/nacl-tl-deliver --env staging # target environment (default: staging)
/nacl-tl-deliver --env production # target production (extra safety checks)
Removed Flags (W4-blocking-release)
The SKIP-VERIFY flag (was: "push + CI only, no staging verification") and the SKIP-DEPLOY flag (was: "push + CI + verify, no health check") were REMOVED in W4-blocking-release. Their literal tokens are scrubbed from this skill's prose. The bypass use case routes through:
- Signed exceptions (for known, planned carve-outs) — file
under .tl/exceptions/.yaml with explicit affected_gates; consumed by the release skill. See nacl-tl-release/SKILL.md § "Release Blocking Gates (Strict-Only)".
- Emergency mode (for reactive bulk-bypass) — three env vars
on the invoking shell. See nacl-tl-core/references/emergency-mode.md.
Neither path re-enables the removed flags. The flag surface is gone.
Configuration Resolution
| Data | Source priority | |------|---------------| | Git strategy | git.strategy > modules.[name].git_strategy > fallback "feature-branch" | | Base branch | git.main_branch > modules.[name].git_base_branch > fallback "main" | | Build command | modules.[name].build_cmd > workspace package.json scripts.build. No npm run build fallback. Missing → DELIVER HALTED — NO_INFRA (scripts.build undeclared) (P2). | | Test command | modules.[name].test_cmd > workspace package.json scripts.test. No npm test fallback. Missing → DELIVER HALTED — NO_INFRA (scripts.test undeclared) (P2). | | Package manager | build.package_manager > package.json packageManager > lockfile detection. Mixed → BLOCKED (clean-checkout-pm-ambiguous) (Step 4b). | | Runtime assets | runtime_assets (list of paths relative to build output, per workspace). Missing assets in clean-checkout → BLOCKED (clean-checkout-runtime-assets-missing) (Step 4b). See nacl-tl-core/references/config-schema.md § runtime_assets. | | Smoke endpoints | deploy.smoke.endpoints (list of paths). Default: ["/api/health"] (records PASS_HEALTH_ONLY). | | Entrypoint | build.entrypoint > package.json main > dist/index.js. | | Migrate cmd | build.migrate_cmd > undefined (then migrate stage SKIPPED). | | Test database URL | build.test_database_url > undefined (then BLOCKED if migrate would run). | | Staging URL | deploy.staging.url > no default | | Health endpoint | deploy.staging.health_endpoint > fallback /api/health | | CI platform | deploy.ci_platform > detect from .github/workflows/ | | YouGile columns | yougile.columns.* |
If config.yaml missing → use fallback defaults. If YouGile missing → skip task moves.
State File: .tl/delivery-status.json
Persists delivery progress for resumption:
{
"branch": "feature/FR-001-generation-controls",
"env": "staging",
"started": "2026-04-04T12:00:00Z",
"ship": {
"status": "done",
"commit": "abc1234",
"pr": "#42",
"prUrl": "https://github.com/org/repo/pull/42"
},
"ci": {
"status": "done",
"runUrl": "https://github.com/org/repo/actions/runs/123",
"duration": "3m 12s"
},
"verify": {
"status": "in_progress",
"ucs": ["UC028", "UC029"]
},
"clean_checkout": {
"status": "pending",
"commit": null,
"artifact_path": null
},
"deploy": {
"status": "pending"
},
"graph": {
"status": "pending"
}
}
Always update after each step completes. This enables resumption.
Workflow: 6 Steps
Step 1: PRE-CHECK
- Verify current branch:
``bash current=$(git rev-parse --abbrev-ref HEAD) ``
- If
--branchprovided and current != branch →git checkout [branch] - If no branch specified → use current branch
- Safety: refuse to deliver from
main/masterdirectly (suggest using CI/CD instead)
- Check for uncommitted changes:
``bash git status --porcelain ``
- If uncommitted changes exist → STOP: "Uncommitted changes detected. Run /nacl-tl-conductor or commit manually first."
- Goal-context exception (2.14+): when
NACL_GOAL_RUN_IDis set and
.tl/goal-runs/$NACL_GOAL_RUN_ID/plan.lock.json carries preexisting_dirty_files[], those exact paths are EXPECTED dirt — another agent's in-flight work in the shared worktree (Smart WIP). Tolerate them: they are not staged, not pushed, and do not block delivery. Any uncommitted path NOT in that list still STOPs as above.
- Check for unpushed commits:
``bash git log origin/[branch]..HEAD --oneline 2>/dev/null ``
- If branch doesn't exist on origin → that's OK, we'll push it
- If branch exists and we have new commits → will push them
- Run tests (all modules):
``bash cd [module_path] && [test_cmd] ``
- If tests FAIL → STOP: report failures, suggest fixing before delivery
- Run build (all modules):
``bash cd [module_path] && [build_cmd] ``
- If build FAILS → STOP: report errors
- Check for existing delivery-status.json:
- If exists and branch matches → RESUME MODE (skip to incomplete step)
- Write initial
.tl/delivery-status.json
→ If any pre-check fails: stop immediately with clear error message and suggested fix.
Step 2: SHIP (push + PR)
- Push to origin:
``bash git push -u origin [branch] ``
- If push rejected (behind remote):
``bash git pull --rebase origin [branch] `` Then retry push. If merge conflict → STOP, report to user.
- Create PR if feature-branch strategy and PR doesn't exist:
``bash # Check if PR already exists gh pr list --head [branch] --state open ``
- If no PR:
``bash gh pr create \ --title "[branch-title]" \ --body "$(cat .yaml; it does NOT relax this step, but it lets the downstream release skill accept the resulting UNVERIFIED aggregate for the specific gates the exception names. The deliverable carve-out targets are upstream-qa-unverified and LIVE_PROVIDER_SMOKE / PROD_GOLDEN_PATH (W3 names).
- Emergency mode (
NACL_EMERGENCY=1+ companion env vars)
for reactive bulk-bypass. The deliver run prints a bypass banner per refusal, writes an event under .tl/emergencies/, and produces a (emergency-bypass) Status: suffix. See nacl-tl-core/references/emergency-mode.md.
Step 4 proceeds as below in every standard delivery:
- Pre-verify dev status check:
Before invoking /nacl-tl-verify for any UC, resolve its dev status using the following source-of-truth hierarchy:
Primary source — Neo4j graph: Query each UC's Task node: ``cypher MATCH (t:Task {id: $ucId}) RETURN t.status AS status ` Use mcp__neo4j__read-cypher. If the Task node exists, its status field is authoritative and overrides any value in .tl/status.json`.
Fallback — .tl/status.json: Use only when Neo4j is unavailable (connection error / timeout). Log: `` WARN: Neo4j unavailable — falling back to .tl/status.json for UC### dev status. Graph may be ahead of file; results have reduced confidence. ``
The graph always wins when both sources disagree. Example: graph says blocked → status is BLOCKED even if .tl/status.json says done.
| UC dev status | Action before verifying | |---------------|------------------------| | PASS (done) | Proceed with /nacl-tl-verify normally | | UNVERIFIED (verified-pending) | Post advisory: "UC### dev status UNVERIFIED. /nacl-tl-verify will run but results have reduced confidence. Proceed? [yes/no]". If yes → run verify; if no → skip UC (mark as skipped in delivery) | | BLOCKED (blocked) | Same as UNVERIFIED: advisory + user gate | | Not found / old-style "done" | Proceed (backward-compat) | | REGRESSION (failed) | DO NOT run /nacl-tl-verify. Log: "UC### skipped — REGRESSION status" |
- Determine which UCs to verify:
- If conductor-state.json exists → read completed UC list
- If
--feature FR-001→ read FR's UC list from feature-request artifact - Fallback: verify all UCs found in
.tl/status.jsonwith status "done"
or "verified-pending" (with user gate for the latter)
- For each UC that passes the pre-verify gate (Step 4.0):
Launch sub-agent (Task tool): `` Execute /nacl-tl-verify UC### ``
nacl-tl-verify internally runs:
/nacl-tl-verify-code(static analysis, fast)/nacl-tl-qa(E2E on staging, only if code analysis says PASSNEEDSE2E)
- Collect results:
- PASS: UC verified on staging
- FAIL: UC has issues (code analysis or E2E failure)
- YouGile transitions (handled by nacl-tl-verify):
- PASS: DevDone → Testing → ToRelease
- FAIL: DevDone → Testing → Reopened
- Update delivery-status.json:
``json "verify": { "status": "done", "results": { "UC028": "PASS", "UC029": "FAIL", "UC030": "UNVERIFIED_DEV_SKIPPED" } } ``
- Decision:
- If ALL UCs PASS → proceed to Step 5
- If SOME UCs FAIL → report which failed; **exclude FAIL UCs from the delivery
artifact** (they are not passed to Step 5, not stamped in Step 6, and their IntakeItems are NOT written delivered). Proceed to Step 5 for PASS UCs only. This exclusion is symmetric with the IntakeItem stamping rule in Step 6: only PASS UCs ever receive the delivered stamp — FAIL UCs are explicitly omitted, not silently skipped.
- If ANY UC is UNVERIFIED (dev status) and user declined verify → USER GATE:
"X UCs have UNVERIFIED dev status. Deliver partial set or halt?"
- If user confirms partial delivery → proceed to Step 5 for PASS UCs only
- If user halts → DELIVER APPLIED — UNVERIFIED; do NOT write IntakeItem 'delivered'
- If ALL UCs FAIL → STOP, recommend /nacl-tl-reopened
→ Output: verification report per UC
Step 4b: CLEAN-CHECKOUT GATE (Strict-Only; W9-ci-clean-checkout)
This gate runs AFTER Step 4 VERIFY but BEFORE Step 5 DEPLOY HEALTH CHECK on every delivery. It exists because 17 of the ~60 baseline signals (the largest single bucket) are config / infra / CI drift that ONLY surface on a clean runner: pnpm version mismatch, Prisma generate missing, TESTDATABASEURL unset, tsconfig divergence, drizzle journal drift, pm2 entry-point confusion, and non-TS runtime assets (ffmpeg, ffprobe, prompt markdown, fonts, locale data) absent from build output. The pattern is "first CI run on a clean runner exposes drift after the wave is declared done." A pre-existing local node_modules/ and a warm dist/ mask these failures; only a shallow clone into a fresh directory followed by a full install + build + smoke catches them.
VERIFIED is refused unless this gate completes with PASS.
Procedure
- Determine wave-tip commit and project package manager:
commit = $(git rev-parse HEAD)on the branch being delivered.package_manager = config.yaml → build.package_manager(default
resolved from packageManager field in package.json if present, else from lockfile presence: pnpm-lock.yaml → pnpm, package-lock.json → npm, yarn.lock → yarn). The clean-checkout gate uses this single resolved value; mixed package managers in one workspace fail the gate with BLOCKED — clean-checkout-pm-ambiguous.
- Shallow clone into a fresh directory:
``bash tmpdir=$(mktemp -d -t nacl-clean-checkout-XXXXXX) git clone --depth 1 --branch "$branch" "$repo_url" "$tmpdir/repo" cd "$tmpdir/repo" git checkout "$commit" ``
- The directory MUST be fresh (no inherited
node_modules/,
dist/, .next/, prisma/generated/, or other build cache). Local pnpm/yarn/npm caches MAY be reused (this is a CI runner simulation, not an offline test).
- Install:
``bash # pnpm: respects packageManager field and pnpm-lock.yaml pnpm install --frozen-lockfile # npm: npm ci # yarn: yarn install --frozen-lockfile ``
- If install fails → BLOCKED with
clean-checkout-install-failed.
Capture stderr tail (50 lines) to evidence.
- Build (all workspaces):
``bash pnpm -r build # or: npm run build / yarn build ``
- If build fails → BLOCKED with
clean-checkout-build-failed. - If
config.yaml → build.requires_prisma_generate: true, the
build step MUST include prisma generate upstream of the compile (either via a prebuild script or a workspace-level equivalent). Missing → BLOCKED with clean-checkout-prisma-generate-missing.
- Verify runtime assets present in build output:
Read config.yaml → runtime_assets (list of paths relative to each workspace's build-output root). For each entry, assert the file or directory exists under the built artifact tree. Missing any required runtime asset → BLOCKED, NOT a WARNING.
See nacl-tl-core/references/config-schema.md § runtime_assets for the schema and defaults for common project shapes.
- Migrate (only if DB tooling is present):
- If
config.yaml → build.migrate_cmdis set, run it against a
scratch database (the URL comes from config.yaml → build.test_database_url — if absent, the gate reports BLOCKED with clean-checkout-test-database-url-undefined when the migrate step would otherwise run).
- If no DB tooling configured → migrate stage is recorded as
SKIPPED (not BLOCKED) and the gate proceeds.
- Run-smoke (boot the entrypoint, hit health, hit one product endpoint):
``bash pnpm start & # or: node dist/index.js, or service-specific entry PID=$! # Wait for health (curl loop with timeout) # Hit /api/health -> expect 200 # Hit one product endpoint named in config.yaml -> deploy.smoke.endpoints[0] -> expect 2xx kill $PID ``
- Entrypoint resolution comes from
config.yaml → build.entrypoint
(default: package.json main field, else dist/index.js).
- Product-endpoint smoke list comes from
config.yaml → deploy.smoke.endpoints (list of paths; default:
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ITSalt
- Source: ITSalt/NaCl
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.