You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Umbrella for making TokenOps hard-dependent on the HTTP control plane for the
ledger: remove the local-file / embedded path from the agent, move run state to the
plane, keep per-turn latency sane. Absorbs #55 and #60; coordinates with agentplane-control-plane (see its epic).
Full working plan + wire contract: scratch/remote-only-control-plane-plan.md and scratch/phase-0-contract.md in the repo (contract moves to control-plane/docs/api-contract.md when Phase 1 starts).
Decisions (locked)
POST /v1/ledger/precheck — one consolidated read per governed call.
POST /v1/ledger/events:batch — one batched write; single-op ledger routes become wrappers.
Idempotency key required on every write event; plane dedupes.
Run state (steps/window/velocity) moves to the plane (run_state table); Tier-1 LocalRunState stays as a per-process cache.
Policies carry data_scope ∈ {local, global}; local reads Tier-1 only, global reads the plane via precheck.
No client-side event buffer yet — apply_events(list) is the only write path so a buffer can wrap it later.
No accuracy-loss bounding — budgets stay a soft cap.
trajectory_hint disabled (done); no trajectory/* routes.
RunState → LocalRunState (rename).
Budget fan-out = one spent_add event with all targets; plane applies in one txn.
One runs row per run_id — no per-agent participation rows.
span_id removed from TokenOps entirely; cross-hop link is X-TokenOps-Run-Id only.
No TOKENOPS_EMBEDDED — unit tests use an in-memory FakeLedgerBackend, kept honest by a contract suite parametrized against the real plane over httpx.ASGITransport.
UI lives in the plane — delete src/tokenops/ui/ and the streamlit dependency.
Update (0.3.0, 2026-09-12): the hard HTTP dependency landed — no more local ledger
fallback, from_env() requires a URL, Ledger(backend=...) is wired end to end. Epic
stays open: data_scope-based detector grouping, span_id removal, server/ui
teardown, and Phase 3/4 are still outstanding. See checklist below.
Umbrella for making TokenOps hard-dependent on the HTTP control plane for the
ledger: remove the local-file / embedded path from the agent, move run state to the
plane, keep per-turn latency sane. Absorbs #55 and #60; coordinates with
agentplane-control-plane(see its epic).Full working plan + wire contract:
scratch/remote-only-control-plane-plan.mdandscratch/phase-0-contract.mdin the repo (contract moves tocontrol-plane/docs/api-contract.mdwhen Phase 1 starts).Decisions (locked)
POST /v1/ledger/precheck— one consolidated read per governed call.POST /v1/ledger/events:batch— one batched write; single-op ledger routes become wrappers.steps/window/velocity) moves to the plane (run_statetable); Tier-1LocalRunStatestays as a per-process cache.data_scope ∈ {local, global};localreads Tier-1 only,globalreads the plane viaprecheck.apply_events(list)is the only write path so a buffer can wrap it later.trajectory_hintdisabled (done); notrajectory/*routes.RunState→LocalRunState(rename).spent_addevent with alltargets; plane applies in one txn.run_registrationsmerged intoruns; identity columns write-once;create_runremoved from the agent path → Merge run_registrations into runs; make identity columns write-once; stop agents calling create_run #117.runsrow perrun_id— no per-agent participation rows.span_idremoved from TokenOps entirely; cross-hop link isX-TokenOps-Run-Idonly.TOKENOPS_EMBEDDED— unit tests use an in-memoryFakeLedgerBackend, kept honest by a contract suite parametrized against the real plane overhttpx.ASGITransport.src/tokenops/ui/and thestreamlitdependency.Update (0.3.0, 2026-09-12): the hard HTTP dependency landed — no more local ledger
fallback,
from_env()requires a URL,Ledger(backend=...)is wired end to end. Epicstays open:
data_scope-based detector grouping,span_idremoval,server/uiteardown, and Phase 3/4 are still outstanding. See checklist below.
Phases
Phase 0 — contract & decisions ✅
scratch/phase-0-contract.md)trajectory_hintdisabled in codePhase 1 —
agentplane-control-plane→ 0.2.0 ✅ mergedcontrol-plane#12 merged (closes control-plane#10):
precheck,events:batch,run_state,data_scope, run-scoped halt,PRAGMA user_versionmigrations,registered_at,PATCHnarrowing,start/stop/statusCLI, Dockerfile. Additive —0.1-era clients still work. Destructive fold deferred → control-plane#11 (0.3.0, not
required for this epic).
Phase 2 — tokenops SDK
from_env()requires a URL; delete embedded branch; reworkdemo.pyto launchcontrol_plane.appin-process — shipped in 0.3.0:ControlPlaneClient.from_env()raises withoutCONTROL_PLANE_URL/TOKENOPS_URL,TOKENOPS_EMBEDDEDis gone,tokenops.demolaunchestokenops.control.dev_planein-processtokenops.server(src/tokenops/server/), rewire Docker/compose/Makefile/examples tocontrol-plane serve, collapseshould_mount_run_registration/mount_run_registration, preserve therun_id↔trace_idbinding (Reconcile divergent 'run' vs 'trace' (and 'span') terminology across TokenOps / Chronicle / control-plane #113) —src/tokenops/server/still present;should_mount_run_registration()now always returnsFalse(0.3.0) but the module itself isn't removed yetagentplane-control-planeas a[contract]optional dep (kept out of[dev]until 0.2.0 is on PyPI; plane-backed testsimportorskip) — LedgerBackend protocol + HttpLedgerBackend (remote-only, Phase 2.1) #120LedgerBackendprotocol;HttpLedgerBackend(direct) +FakeLedgerBackend(tests). Only write path =apply_events(list)with a# TODO(buffering)seam.register_run/resolve_run/governance_config_for/patch_run_record(dropssteps/cost_micros) — LedgerBackend protocol + HttpLedgerBackend (remote-only, Phase 2.1) #120Ledger(backend=...)wired intoLedger/ControlPlaneClient(0.3.0): every write routes throughLedgerBackend.apply_events, every spend/inflight/halt read throughread_state(precheck);RunState→LocalRunStaterename shipped (refactor: rename RunState to LocalRunState; add PolicyInstance.data_scope #131)RemoteLedgerViewoverprecheck; Governor groups detectors bydata_scope, oneprecheckper call —PolicyInstance.data_scopeis persisted and round-tripped (refactor: rename RunState to LocalRunState; add PolicyInstance.data_scope #131) but not yet consumed bybuild_governorspan_id(Add Planner→Researcher→Writer triad TokenOps bench #14): no minting, noX-TokenOps-Parent-Span-Id, strip fromSpanContext/Observation/BoundaryStepLedger.record:if cost > 0guard on budget fan-out — Ledger.close_run() + skip zero-cost spend writes (remote-only prep) #121register_runreturns fullRunRegistration— kill register-then-resolvegovernance_cachere-key(base_url, agent)+ two-level lazy TTL (Governance config: one-time bootstrap load + two-level lazy TTL (stop fetching GET /v1/governance/{agent} on every run start) #114); no-TTL per-run_idregistration cacheTOKENOPS_PLANE_UNREACHABLE=fail-fast|passthrough|local-policies-only+ circuit breaker +TOKENOPS_FALLBACK_RUN_CAP_MICROSLedger.close_run()fromtokenops_runfinally(Ledger.runs (per-run RunState) is never evicted — unbounded memory growth for long-lived / shared-governor processes #115) — Ledger.close_run() + skip zero-cost spend writes (remote-only prep) #121 (note: Ledger.runs (per-run RunState) is never evicted — unbounded memory growth for long-lived / shared-governor processes #115 also wants a TTL/max-entries sweep backstop for the shared-governor case — not done, issue stays open)src/tokenops/ui/+streamlitdep (Fix triad.yaml: tool_reject → tool_fix #16); decideexamples/ui/Phase 3 — tests & CI
FakeLedgerBackend(tests/fakes.py) + conftest fixture — LedgerBackend protocol + HttpLedgerBackend (remote-only, Phase 2.1) #120http_backend/any_backend, FastAPITestClient—httpx.ASGITransportis async-only) againstcontrol_plane.app— LedgerBackend protocol + HttpLedgerBackend (remote-only, Phase 2.1) #120test_ledger_backend_contract.pyparametrized over both backends (24 tests) — LedgerBackend protocol + HttpLedgerBackend (remote-only, Phase 2.1) #120tests/examples/(e2e-marked) moved into default CI; CI installsagentplane-control-planefrom source for[contract]/e2e coverage (e2e/live tests excluded from CI, and 3 of 11 are currently broken on main #127, fix: bring e2e tests into default CI, fix 3 stale against attribution hardening #130) — these now exercise real HALT/MUTATE decisions against a real in-process plane, not just plumbingStore(...)test files per the Part 14 taxonomy; drop allTOKENOPS_EMBEDDEDTOKENOPS_URLonly, no DB volumePhase 4 — follow-ups
BufferedLedgerBackend(gated on Inflight ledger is a bare per-segment counter — not safe under async/batched/retried admit·complete #116)Durability: queuedqueueenvelopes:batch(Part 8)trajectory_hintremote re-enableSub-issues
#113 (run/trace/span terminology) · #114 (governance-config TTL) · #115 (
LocalRunStateleak) · #116 (inflight counter) · #117 (run_registrations/runsmerge)