agentic-ai-engineering/pipeline/demo.py

222 lines
11 KiB
Python

#!/usr/bin/env python3
"""
Pipeline Demo — end-to-end walkthrough of all pipeline modules.
Demonstrates:
1. Confidence Ladder (5-level Verdict)
2. Rejection Envelope (4-field blocked action)
3. State Gate (pre-merge completeness check)
4. Expertise file (per-cron compounding memory)
Run: python -m pipeline.demo
"""
import json
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from pipeline.confidence_ladder import Verdict, VerdictLevel, VerdictReport
from pipeline.rejection_envelope import RejectionEnvelope, Blocker
from pipeline.state_gate import StateGate, FieldSpec, PipelineState
from pipeline.expertise import append_entry, render_context, purge_domain
def hr(title: str):
print(f"\n{'=' * 60}")
print(f" {title}")
print(f"{'=' * 60}")
# ═══════════════════════════════════════════════════════════════════════════════
# 1. STATE INIT
# ═══════════════════════════════════════════════════════════════════════════════
hr("1. STATE INIT — PipelineState with core_fields")
state = PipelineState(
{
"plan_file": "plans/feat-123.md",
"branch_name": "feat/instant-polls",
"issue_number": 42,
"rogue_field": "should be silently dropped",
},
core_fields=["plan_file", "branch_name", "issue_number", "backend_port", "frontend_port"],
)
print(f" plan_file: {state.get('plan_file')}")
print(f" branch_name: {state.get('branch_name')}")
print(f" issue_number: {state.get('issue_number')}")
print(f" rogue dropped: {state.get('rogue_field')}") # should be None
assert state.get("rogue_field") is None, "rogue fields are silently dropped"
# ═══════════════════════════════════════════════════════════════════════════════
# 2. PLAN PHASE — PERFECT
# ═══════════════════════════════════════════════════════════════════════════════
hr("2. PLAN PHASE — Verdict: PERFECT")
report = VerdictReport(pipeline_id="demo-2026-06-11")
report.add(Verdict(
VerdictLevel.PERFECT,
"Plan covers all acceptance criteria with test coverage map",
evidence=["AC1 coverage: tests/test_instant_polls.py", "AC2 coverage: tests/test_instant_polls.py"],
source="plan-review",
))
print(f" Ve rdict: {report.aggregate_label}")
print(f" Pass? {report.passed}")
# ═══════════════════════════════════════════════════════════════════════════════
# 3. BUILD PHASE — VERIFIED
# ═══════════════════════════════════════════════════════════════════════════════
hr("3. BUILD PHASE — Verdict: VERIFIED")
state.update(backend_port=9105, frontend_port=9205)
report.add(Verdict(
VerdictLevel.VERIFIED,
"Build completed, CI confirmed all 14 tests pass",
evidence=["exit code 0", "CI run #4185: all tests green"],
source="ci",
))
print(f" Verdict: {report.aggregate_label}")
print(f" Pass? {report.passed}")
print(f" Verdicts: {len(report.verdicts)} phases so far")
# ═══════════════════════════════════════════════════════════════════════════════
# 4. SECURITY REVIEW — PARTIAL (the critical gate)
# ═══════════════════════════════════════════════════════════════════════════════
hr("4. SECURITY REVIEW — Verdict: PARTIAL (green-but-dead check)")
report.add(Verdict(
VerdictLevel.PARTIAL,
"Static analysis clean, dependency scan clean — but no live penetration test was run",
evidence=["semgrep: 0 findings", "pip audit: 0 known vulns", "NOTE: no live pen test — needs staging env"],
source="security-review",
))
print(f" Verdict: {report.aggregate_label}")
print(f" Pass? {report.passed} (PARTIAL = green + documented gap)")
print(f" Blocked for merge? {report.aggregate.is_blocking}")
print()
print(" ▶ THIS is the green-but-dead prevention:")
print(" The pipeline passes (green) but the security gap is")
print(" documented, not forgotten. The reviewer knows what's missing.")
assert report.aggregate == VerdictLevel.PARTIAL
assert report.passed is True
assert report.aggregate.is_blocking is False
# ═══════════════════════════════════════════════════════════════════════════════
# 5. REJECTION ENVELOPE — leader tries to write a file
# ═══════════════════════════════════════════════════════════════════════════════
hr("5. REJECTION ENVELOPE — leader blocked from writing files")
blocker = Blocker(
name="no_direct_write",
match=lambda a: a.get("tool") in ("write_file", "patch", "execute_code"),
reject=lambda a: RejectionEnvelope.block(
received=a.get("tool", "unknown"),
reason="LEADER_MUST_NOT_WRITE_FILES",
allowed="Use delegate_task with a writer subagent",
hint="Leaders read, delegate, synthesize. Delegate file writes.",
),
)
# This gets blocked
blocked = blocker.evaluate({"tool": "write_file", "path": "src/main.py"})
print(f" Action: write_file('src/main.py')")
print(f" Result: BLOCKED")
print(f" Reason: {blocked.reason}")
print(f" Allowed: {blocked.allowed}")
print(f" Hint: {blocked.hint}")
print(f" Timestamp: {blocked.timestamp}")
assert blocked is not None
# This passes through
allowed = blocker.evaluate({"tool": "read_file", "path": "state.json"})
print(f"\n Action: read_file('state.json')")
print(f" Result: {'PASSES THROUGH (not blocked)' if allowed is None else 'BLOCKED'}")
# ═══════════════════════════════════════════════════════════════════════════════
# 6. STATE GATE — pre-merge completeness check
# ═══════════════════════════════════════════════════════════════════════════════
hr("6. STATE GATE — pre-merge completeness check")
ship_gate = StateGate(
FieldSpec("plan_file", required=True, predicate=lambda v: isinstance(v, str) and v.startswith("plans/")),
FieldSpec("branch_name", required=True),
FieldSpec("issue_number", required=True, type_hint=int),
FieldSpec("backend_port", required=True, type_hint=int),
FieldSpec("frontend_port", required=True, type_hint=int),
label="pre-ship",
)
# Complete state should pass
result = ship_gate.validate(state)
print(f" Fields checked: {result.fields_checked}")
print(f" Pass: {result.passed}")
if result.missing:
print(f" Missing: {result.missing}")
assert result.passed, "Complete state should pass ship gate"
# Incomplete state should fail
incomplete = PipelineState(
{"plan_file": "plans/feat-123.md", "branch_name": "feat/instant-polls"},
core_fields=["plan_file", "branch_name", "issue_number", "backend_port", "frontend_port"],
)
fail_result = ship_gate.validate(incomplete)
print(f"\n --- With missing fields ---")
print(f" Pass: {fail_result.passed}")
print(f" Missing: {fail_result.missing}")
assert not fail_result.passed
assert "issue_number" in fail_result.missing
# ═══════════════════════════════════════════════════════════════════════════════
# 7. EXPERTISE — store what was learned
# ═══════════════════════════════════════════════════════════════════════════════
hr("7. EXPERTISE — store what the run learned")
append_entry(
"demo-pipeline",
domain="pipeline",
insight="PARTIAL verdict is the key innovation — it lets documented gaps pass without blocking, preventing green-but-dead",
source="cron:pipeline-demo",
verified=True,
)
append_entry(
"demo-pipeline",
domain="confidence-ladder",
insight="VERIFIED level requires independent confirmation (CI, review) — the agent trusting itself is only PERFECT",
source="cron:pipeline-demo",
verified=True,
)
append_entry(
"demo-pipeline",
domain="state-gate",
insight="Core_fields filter ensures no rogue keys leak into pipeline state — strict struct, not a dict",
source="cron:pipeline-demo",
verified=False,
)
print(render_context("demo-pipeline"))
# ═══════════════════════════════════════════════════════════════════════════════
# 8. JSON PORTABILITY
# ═══════════════════════════════════════════════════════════════════════════════
hr("8. JSON PORTABILITY — serializable for cron/API delivery")
serialized = report.to_json()
restored = VerdictReport.from_json(serialized)
print(f" Round-trip aggregate matches: {restored.aggregate == report.aggregate}")
print(f" Verdict count: {len(restored.verdicts)}")
print(f"\n Final JSON payload (compact):")
print(f" {json.dumps(json.loads(serialized), indent=2)}")
# ═══════════════════════════════════════════════════════════════════════════════
# CLEANUP
# ═══════════════════════════════════════════════════════════════════════════════
purge_domain("demo-pipeline", "pipeline")
purge_domain("demo-pipeline", "confidence-ladder")
purge_domain("demo-pipeline", "state-gate")
if __name__ == "__main__":
print(f"\n{'=' * 60}")
print(f" ✅ DEMO COMPLETE — all 8 stages verified")
print(f"{'=' * 60}")