M01: Resource registry, state, and collectors for executable resources (issue #86) #99
No reviewers
Labels
No labels
accessibility
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
P1
P2
P3
question
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Creatidy/scarcity-router!99
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "m01/resource-state"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implements M01 — Resource registry, state, and collectors for executable resources (issue #86) under the A0 execution-gateway architecture (#85; D-040–D-045). M01 owns observation and normalized state only: it does not rank, choose, admit or execute anything.
One new module,
scarcity_router/resource_state.py, defines the versioned resource-state snapshot contract (schema_version = 1) sibling to capacity v3, plus the in-memory registry that resolves the deferred U-003 staleness policy for the server's state store. A bounded review correction (commit 2) made the administrator registration the single authoritative owner of freshness/polling policy and configured facts.What is introduced
ResourceIdentity): the D-042 target dimensions kept strictly separate — opaqueresource_id, execution channel (server_direct_http|worker_bridged|local_app_adapter), backendprovider/model/optionalvariant, entitlement class (5 D-042 classes + explicitunknown), and confirmed-onlyquota_pool_ids. Client-visible routing profiles are absent by design (M02's concept). The same subscription seen through Desktop/CLI/Windows/WSL does not become multiple pools, and pool sharing is never assumed in either direction: pools exist only as explicit confirmed ids.ResourceStateSnapshot, v1): a pure observation record — identity, health,observed_at, quota facts with observation classes, promotion observations. It deliberately carries no freshness TTL, polling interval, capabilities or cost fields: those are administrator registration state, so a worker-reported observation is structurally unable to enlarge its own freshness window, change its polling cadence or override configured facts (serialized documents containing such fields are rejected as malformed).ResourceRegistration→ResourceRegistryEntry): the registration ownsfreshness_ttl_seconds,poll_interval_seconds,ExecutionCapabilities(three-valued configured facts) andResourceCost(integer micro-USD per million tokens + observation class). The registry read model composes them with the latest observation — one canonical server-side value per resource, never duplicated competing truth.ResourceHealth): the six capacity v3 status values unchanged (identity mapping fromCapacitySnapshot, zero information loss), status-level diagnostics required for non-ok, zero/exhausted quota never a health state.staleis deliberately not a stored status — it is evaluated freshness.QuotaFactpairs an unchangedCapacityWindowwith its D-042 observation class (direct_observation,provider_telemetry,estimate,local_limit,unknown). Adapter tokens are never equated with subscription quota percentages.PromotionObservation): source, observation time, execution-channel/provider/model/plan scopes, validity period, timezone. A promotion record never asserts execution qualification (D-039 remains the proof path; eligibility is deliberately not embedded).classify_freshnessand registry reads expose exactlyfresh/stale/never_observedagainst an injectable instant. A future-dated observation is never treated as fresh: it is rejected at application time (worker reports remain atomic) andclassify_freshnessfails closed onobserved_atafter the evaluation instant; producing server-comparable observation times is documented as the reporting side's (M05) responsibility — no skew-tolerance protocol is invented here. Polling is bounded and pull-driven (refresh_due); no background threads.ResourceRegistry,RegistrySnapshotv1): in-memory per D-041 (the durable SQLite-class store stays M03/M09 scope; nothing persisted here). Fail-closed mutations: observations apply only to registered resources with exactly matching identity and non-future timestamps; registry read state is cross-resource consistent —ResourceRegistryEntryrequiresobservation.identity == entry.identityexactly whenever an observation is present, enforced in the constructor and therefore inRegistrySnapshot.from_dict()round-trips. Monotonicrevision+generated_atidentify reads for audit provenance.WorkerStateReportv1): the M01 contract for M05-reported state — opaque per-deviceworker_id, observation documents produced by the one shared normalization (resource_snapshot_from_capacity), only worker-reachable channels accepted, unique ids, no future-dated observations, canonically ordered, applied atomically. Transport/pairing is M05 and is not implemented here.Preserved contracts (verified unchanged)
capacity.py,status.pyandproviders/*are untouched; the existing Codex and Z.ai collectors remain the single telemetry authority (no second collector/parser; normalization consumes their snapshots).ExecutionEligibilityv1 / D-039: untouched and not embedded in resource state.server.py,cli.py,mcp.py,machine_api.pyuntouched — recommendation-only behavior byte-identical.Security
Snapshots carry only safe-opaque identifiers (same restricted alphabet as capacity v3); no secrets, account identifiers, free-text fields or raw provider payloads are representable (tests assert credential-shaped ids are rejected and the capacity
planlabel is not carried). No network access, no credentials, no live accounts — all tests are deterministic synthetic fixtures.Tests
80 deterministic tests in
tests/test_resource_state.py: identity/vocabulary validation, all six status mappings, failure isolation between resources, zero-vs-unknown-vs-exhausted distinctions, fresh/stale/never-observed boundaries, registration-policy authority (worker cannot enlarge freshness or change polling/configured facts — structurally and behaviorally), cross-resourceResourceRegistryEntry.from_dict/RegistrySnapshot.from_dictrejection, future-timestamp rejection atclassify_freshnessand at apply time, refresh-due cadence, confirmed-pool grouping and non-assumption of sharing, worker-report atomicity, wrong-version/malformed documents failing closed, and canonical deterministic serialization.Validation
Forgejo CI workflow
ci/ jobcheckruns on this PR (pull_request event) and is green on the head commit.Deliberate deferrals
Closes #86 upon merge (the Forgejo issue's acceptance criteria are covered above).