RunContext: Authority Inversion (v0.5.0)
SPINE v0.5.0 introduces authority inversion - a fundamental architectural shift where RunContext becomes the sole source of runtime truth. All core modules now operate ON RunContext rather than alongside it, eliminating dual-truth bugs and simplifying session handoff.
The Problem: Dual Truth
Before v0.5.0, modules like FivePointProtocol returned a ProtocolResult AND could optionally write to RunContext. Two places carried state. When they diverged, which was correct?
BEFORE (v0.4.0):
FivePointProtocol.audit()
-> returns ProtocolResult (truth #1)
-> optionally writes to RunContext (truth #2)
-> downstream code reads from... which one?
The Solution: Authority Inversion
Three rules govern all inverted modules:
- If
run_contextprovided - RUNTIME mode: write all state to RunContext, returnNone - If
run_contextis None - STANDALONE mode: return authoritative Result object (backward compatible) - Never dual-write - never build BOTH RunContext state AND Result object
AFTER (v0.5.0):
FivePointProtocol.audit(output_metadata, run_context=ctx)
-> writes findings to ctx
-> returns None
-> downstream reads from ctx (sole truth)
RunContext State Machine
RunContext carries all state through a strict lifecycle:
CREATED -> CLARIFIED -> SCOPED -> PLANNED -> EXECUTING -> VERIFYING -> COMPLETED
|
FAILED / HALTED / NEEDS_HUMAN
Each transition corresponds to a lifecycle method:
| Method | Transition | Artifact Produced |
|---|---|---|
ctx.clarify() |
CREATED -> CLARIFIED | ClarificationArtifact |
ctx.set_scope() |
CLARIFIED -> SCOPED | ScopeArtifact |
SkillCompiler.compile(ctx) |
SCOPED -> PLANNED | PlanArtifact |
ctx.begin_execution() |
PLANNED -> EXECUTING | - |
ctx.verify() |
EXECUTING -> COMPLETED/FAILED | VerificationArtifact |
Four Inverted Modules
FivePointProtocol
The Five-Point Protocol (Clarify, Scope, Plan, Execute, Verify) now writes all phases directly to RunContext.
# Runtime mode - RunContext is sole truth
ctx = RunContext.create(task="Audit deployment", source="user")
protocol = FivePointProtocol(constraints)
result = protocol.run_full(task, output_metadata, run_context=ctx)
# result is None - all truth in ctx
assert result is None
assert ctx.verification is not None
# Standalone mode - backward compatible
result = protocol.run_full(task, output_metadata)
# result is authoritative ProtocolResult
assert result.passed is True
VerdictRouter
Routes task results to memory stores (Tier A/B/C) based on verdicts. In runtime mode, writes routing events to RunContext.
# Runtime mode
router.route(task_id="t1", verdict=ACCEPT, content="...", run_context=ctx)
# Returns None - routing truth in ctx.execution_log
# Read from RunContext
events = [e for e in ctx.execution_log if e.event_type == "verdict_routing"]
tier = events[0].data["tier"] # "TIER_A"
OODALoop
The OODA cycle (Observe-Orient-Decide-Act-Reflect) writes all phase data to RunContext. Key change: LoopContext is NOT used as truth in runtime mode - RunContext owns the cycle state.
# Runtime mode
await ooda.run(goal="Analyze system", run_context=ctx)
# Returns None - cycle data in ctx.execution_log
cycles = [e for e in ctx.execution_log if e.event_type == "ooda_cycle"]
ContentPipelineExecutor
The 5-stage content pipeline (Analyze, Bridge, Validate, Refine, Execute, Review) writes stage results in real-time to RunContext via _write_stage_to_run_context().
Cognitive Compiler
v0.5.0 adds a compile-time validation pipeline that catches broken plans before execution.
SkillCompiler
Compiles RunContext (in CLARIFIED or SCOPED state) plus a SkillRegistry into a PlanArtifact:
- Build task description from ctx.clarification + ctx.scope
- Match skills via keyword + embedding similarity
- Enforce tool availability - tool_checker callback verifies MCP tools exist at compile time
- Include meta-skill (5PP) if configured
- Flatten into ordered PlanSteps with dependency tracking
- Topological sort (Kahn’s algorithm) for execution order
- Detect parallel groups for concurrent execution
- Validate via PlanValidator
- Write plan to ctx (state -> PLANNED)
compiler = SkillCompiler(
registry=skill_registry,
tool_checker=lambda tool: tool in available_mcp_tools,
)
plan = compiler.compile(ctx) # ctx must be CLARIFIED or SCOPED
# ctx.plan is now set, ctx.state == PLANNED
PlanValidator
Validates every compiled plan against 6 structural checks:
| Check | What It Catches |
|---|---|
| non_empty | Empty plans with no steps |
| no_orphans | Steps depending on non-existent steps |
| dag_acyclic | Circular dependency chains |
| required_skills_ordered | Dependencies executing after dependents |
| policy_consistency | Halt gates placed after irreversible steps |
| optional_output_safety | Required phases consuming optional outputs |
Policy Tags
Plan steps can carry policy tags (destructive, irreversible, approval-gate) that trigger PolicyDecisions at execution time:
decision = ctx.check_policy()
if decision.verdict == PolicyVerdict.REQUIRE_REVIEW:
# Human must approve before execution continues
plan_summary()
Unified observability method on RunContext that returns a complete view:
- Skills included and excluded (with reasons and missing tools)
- Step count, tool count, parallel groups
- Policy decision and halt-on-failure steps
- Whether human approval is required
- Optional dependency warnings
Serialization and Session Handoff
All RunContext state serializes to JSON, enabling session handoff:
# Save at end of session
ctx.save("run_001.json")
# Resume in next session
ctx = RunContext.load("run_001.json")
# All artifacts, events, findings, verification preserved
Design Principles
- Sole Source of Truth - If RunContext disappears, system breaks. If Result object disappears, system still works.
- No Dual Write - Never build BOTH RunContext state AND Result object when run_context is provided.
- Return Value Irrelevance - In runtime mode, return value is None. Downstream reads from RunContext.
- Backward Compatibility - Standalone mode (no run_context) unchanged. Legacy code still works.
- Serialization Preservation - Round-trip JSON serialization preserves all authority.
Test Coverage
84 acceptance tests verify the authority inversion:
| Test Suite | Tests | Focus |
|---|---|---|
| test_run_context_wiring.py | 36 | Authority inversion for all 4 modules |
| test_compiler_enforcement.py | 12 | SkillCompiler tool availability |
| test_plan_validation.py | 23 | PlanValidator 6-check validation |
| test_policy_tag_enforcement.py | 13 | Policy tag enforcement |
Key test patterns:
assertIsNone(result)- verifies runtime mode returns None- No dual-write guards - verifies only RunContext holds truth
- Serialization round-trip - verifies JSON preserves authority
- 9-phase truth audit - comprehensive system integrity check
Version History
| Version | Change |
|---|---|
| v0.5.0 | Authority inversion (4 modules), SkillCompiler, PlanValidator, policy tags, plan_summary() |
| v0.4.0 | Deep memory (Tier 6-7), federated memory, OODA hooks |
| v0.3.30 | Agent OS initial: OODA loop, episodic memory, agent processes |