Why gates exist
Section titled “Why gates exist”LLM agents optimistically mark tasks complete. “I’ve implemented the feature” is a natural language claim, not proof. In a system where stage transitions trigger deploys, PR merges, and ticket closure, optimistic completion propagates errors downstream.
Gates convert “the agent said so” into “the filesystem and API state confirm it.”
Gate architecture
Section titled “Gate architecture”Three enforcement surfaces, one contract:
.sdlc/sdlc.yaml gates: section ← declaration ↓.sdlc/bin/sdlc_gate_check.py ← runtime enforcement ↓.sdlc/doctor/runtime-alignment.ts ← structural validation (doctor)If a gate exists in only one surface, it is a bug. ADR-012 requires declaration and code sync. Schema: .sdlc/schemas/gate-dsl.schema.json. Runner: .sdlc/bin/sdlc_gate_runner.py.
Declared gate sets
Section titled “Declared gate sets”From sdlc.yaml:
gates: integration: - integration-report - joint-e2e-pass deploy: - ci-target-aligned - staging-smoke-or-url post-deploy: - evidence-manifest - production-http-200 reflect: - reflect-report-pass workspace-rebind: - rebind-report - ci-target-alignedEach orchestrator stage transition calls sdlc_gate_check.py with the relevant gate set. Exit code 1 blocks the transition.
Ticket-level gates (feature-flow)
Section titled “Ticket-level gates (feature-flow)”Before a ticket moves to done:
| Field | Meaning |
|---|---|
subagent_dispatched | Feature-flow ran in subagent/worktree, not main session |
feature_flow_complete | All feature-flow.yaml success_criteria satisfied |
qa_verdict | QA stage returned PASS |
visual_gate | Pixel diff passed (clone mode) |
evidence_dir | Path to .sdlc/evidence/{TICKET}/ exists |
pr_url | GitHub PR created |
review_url | Human or automated review recorded |
Invalid ticket statuses (consolidated, merged_without_agent) fail at wave-complete and mark-done — these indicate protocol bypass.
Feature-flow success criteria
Section titled “Feature-flow success criteria”From feature-flow.yaml:
success_criteria: - planning.plan is defined - implementation.typescript_check == "pass" - implementation.build_check == "pass" - review.review_status == "APPROVED" - qa.qa_status == "PASS" - pr_creation.pr_url is defined - plane-in-review.ticket_state == "In Review" - plane-closure.ticket_state == "Done" - plane-closure.plane_comment_id is definedsdlc_gate_check.py validates these before setting feature_flow_complete: true.
Orchestrator stage order
Section titled “Orchestrator stage order”Stages must complete in order:
hydrate → classify → discovery → decomposition → plane-hierarchy→ architecture → workspace-rebind → wave-planning → execution→ integration → deploy → post-deploy → board-sync → reflectStages depending on work-items hierarchy (architecture through reflect) block until work-items-hierarchy (or plane-hierarchy in legacy manifests) completes.
Protocol guard
Section titled “Protocol guard”The orchestrator may edit specific infra paths during deploy stage only:
| Allowlisted pattern | Rationale |
|---|---|
.github/workflows/deploy.yaml | Pipeline target |
.github/workflows/release.yaml | Version bump |
.github/workflows/dependency-updates.yaml | Lockfile maintenance |
.sdlc/integrations/cloudflare-pages/cloudflare-pages.yaml | Deploy integration |
.github/workflows/deploy.yaml | CI deploy pipeline (wrangler pages deploy) |
{target_root}/e2e/smoke.spec.ts | Deploy smoke |
{target_root}/CHANGELOG.md | Release record |
{target_root}/package.json | Version field only |
Never allowlisted: {target_root}/src/**, feature E2E specs beyond smoke.
Implementation: sdlc_protocol_guard.py with _deploy_stage_allowlist(path).
Trade-off: deploy infra edits in main workspace are permitted; feature code edits are not. This avoids requiring a dedicated deploy worktree for every pipeline retarget while preventing orchestrator implementation bypass.
Run closure gates (mark-done)
Section titled “Run closure gates (mark-done)”A run is not complete until:
| Gate | Check |
|---|---|
handoff_recorded | LATEST.md does not match stub pattern |
sdlc_state_committed | sdlc.yaml, manifest, INDEX delta committed or explicitly deferred with reason |
doctor:operational | CI target alignment pass |
Stub pattern: "STUB — Agent did not enrich".
Consequence: next session must hydrate from git + LATEST.md alone. If that fails, the run is incomplete regardless of deploy status.
Workspace rebind gates
Section titled “Workspace rebind gates”When target_root changes:
python3 .sdlc/bin/sdlc_workspace_rebind.py --target packages/api --profile python-fastapiActions:
- Update
sdlc.yamlworkspace block - Patch CI workflow
working-directoryfields - Patch Cloudflare Pages integration
root_directory - Update
architecture.mdtarget_root row - Run doctor structural gate
workspace:ci-target-aligned
Stage workspace-rebind blocks until rebind report exists and doctor passes.
Playbook micro-gates
Section titled “Playbook micro-gates”Domain playbook pipeline (/sdlc:learn) has separate gates in sdlc_playbook.py:
| Gate | Phase | Blocks |
|---|---|---|
research-gate | F0 → F2 | Playbook materialization if research fails |
contamination-check | F3 | Touch of agents, rules, skills, hooks, memories, sdlc.yaml, workflows without --wire, or {target_root}/ |
Macro gates (sdlc_gate_check.py) and micro-gates (sdlc_playbook.py) are intentionally separate — different lifecycles, different failure modes.
Type coercion
Section titled “Type coercion”Manifest CLI must store booleans as YAML booleans, not strings:
# Correctupdate-ticket --field plane_evidence_uploaded --value true → true
# Wrongupdate-ticket --field plane_evidence_uploaded --value true → 'true'String 'true' fails boolean gate checks silently until manifest inspection.
Operational consequences
Section titled “Operational consequences”Gates add latency. Every stage transition runs Python checks. This is acceptable because the cost of a false “done” (bad deploy, missing evidence, unmerged PR marked complete) exceeds gate execution time.
Gates survive agent replacement. A new agent session cannot inherit a previous agent’s optimistic stage marks — it must pass the same checks.
Gates require maintenance. When a new success criterion is added to a workflow, sdlc_gate_check.py and sdlc.yaml gates: must update together. Doctor catches declaration drift.
Gates are not user-facing. They are infrastructure. Operators interact through manifest status and doctor reports, not raw gate script output.