docs(tasks): decompose wave 7 — the ops surface, the alkcall-backed op family behind the default-off feature
This commit is contained in:
1 parent
e84a874715
commit
c9829836b1
8 files changed
+852
-2
No files matched your search
@@ -0,0 +1,160 @@
|
||||
---
|
||||
id: ops-foundation
|
||||
name: the ops module's substrate — the token layer, the namespace binding, and the fanout registry
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
tags: [wave-7, ops, tokens, fanout, namespaces]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
The `ops` feature's state substrate in `src/ops.rs` (currently the
|
||||
ADR-001 stub doc comment) — everything the five op handlers ride, and
|
||||
none of the handlers themselves (they land in the following tasks).
|
||||
The module is a **store-facade consumer** (ADR-001 §4): its store
|
||||
access is the public facade plus the crate-internal renewal seam named
|
||||
below — no backend side-door, no engine types.
|
||||
|
||||
- **Module structure + the ops context**: the feature-gated module
|
||||
shape (`#[cfg(feature = "ops")]`, re-exported from `lib.rs` per the
|
||||
one-module-per-file convention); the ops-layer context type binding
|
||||
the `Arc<Store>`, the module's state (the token registry, the
|
||||
namespace bindings, the fanout registry), and the module config.
|
||||
This context is what the handler pairs (later tasks) capture.
|
||||
- **The pin-token layer** (ADR-010 §4; ADR-012 §2): opaque token
|
||||
minting over a landed put's pin; the **token↔(key, owner) in-memory
|
||||
mapping with process lifetime** (outlives the minting handler —
|
||||
ADR-012 §2's correction of the original handler-lifetime wording);
|
||||
the **validity domain is the minting node** — the registry is
|
||||
per-process state, there is no cross-node form, and fleet peers
|
||||
refuse tokens they did not mint (`present: false`, not an error).
|
||||
Named design points: **the token's minting form** (a CSPRNG id —
|
||||
e.g. `getrandom` gated behind `ops`; a counter or derivable form is
|
||||
rejected as a guessable capability — decide and record), and **the
|
||||
mapping's retention discipline** (when a mapping retires:
|
||||
conversion-confirmed or process end; the mapping carries no expiry
|
||||
machinery of its own — the pin row's expiry is the fleet liveness
|
||||
signal, and nothing expires non-fleet).
|
||||
- **The guard-custody design** (the fleet/non-fleet split ADR-010 §4
|
||||
pins): under fleet, the put handler's `Pin` guard **drops at
|
||||
response completion** — the pin row's expiry is the only protection
|
||||
from that moment, which is exactly why the TTL/3 renewal duty
|
||||
exists (a guard held for the token's whole life would make the TTL
|
||||
machinery dead code on the ops path); under non-fleet, the token
|
||||
registry **holds the guard** — the token is the in-process guard's
|
||||
handle (ADR-012 §2's generalization: sqlite, single-instance
|
||||
postgres, pg-lo, `local`, `mem` — nothing expires outside fleet
|
||||
mode) until the embedder's registration confirms and the guard
|
||||
converts. The custody split is the token layer's core branch.
|
||||
- **The namespace→liveness-source binding** (ops-surface.md §Op
|
||||
family, hand-over contract step 3): the embedder's converter seam —
|
||||
per-namespace registration of the converter the embedder plugs its
|
||||
registry into ("A `Pin` conversion on the server side (token →
|
||||
registered liveness source) is the mechanism the embedder plugs its
|
||||
registry into"). The conversion rides the crate-internal
|
||||
`RegistryCore::convert` machinery (wave 2). Named design point: **
|
||||
the conversion trigger** (an embedder-called confirm verb vs a
|
||||
probe-triggered conversion — decide and record; the hand-over
|
||||
contract's duty text is the embedder's, the mechanism is this
|
||||
module's).
|
||||
- **The crate-internal store-core renewal access** (ADR-010 §4: "the
|
||||
ops module is in-crate and crosses the seam by construction, the
|
||||
same way the put handler holds the `Pin` guard"): the renewal
|
||||
probe's access to `pin_renew(key, owner)` and `pin_check(key)`
|
||||
through the fleet host — the `pin_check` deliberately-unconsumed
|
||||
note from the wave-6 review (W6-3) names this consumer. The exact
|
||||
probe composition (verify-the-mapping-then-renew vs
|
||||
renew-answers-the-verification) is a named design point against
|
||||
ADR-012 §2's three-step handler duty. What never happens: a *
|
||||
consumer* reaching the seam, or a public-API widening for this.
|
||||
- **The fanout registry** (ops-surface.md's fetch fanout seam; POC #3
|
||||
finding A3): per-digest broadcast state — one reader, store arm +
|
||||
subscriber arms; bounded subscriber channels (the backpressure
|
||||
discipline: a slow subscriber stalls the broadcast, which is
|
||||
correct for a put — subscriber count bounded, slow-subscriber
|
||||
handling is policy); the **late-joiner signal** (broadcast ended →
|
||||
the degrade-to-verified-get path); the **drop-and-late-join**
|
||||
slow-subscriber policy as ops-layer policy. The producer wiring
|
||||
lands with `ops-put`, the consumer wiring with `ops-fetch`; the
|
||||
registry's mechanics land here, unit-tested standalone.
|
||||
- **The renewal-cadence boundary** (ADR-013 §1): the module ships the
|
||||
renewal *mechanism* (the have form's handler); the TTL/3 *cadence*
|
||||
— the putter's renewal worker — is embedder composition above the
|
||||
facade (scheduler/queues), never in-crate machinery. Documented on
|
||||
the module, not implemented.
|
||||
- **Dependency posture**: `serde`/`serde_json` (payload handling) and
|
||||
the token CSPRNG enter as `ops`-gated dependencies — the base crate
|
||||
stays lean (convention 8; ADR-013 §4's engine table unchanged —
|
||||
`ops` pulls alkcall plus these, default-off).
|
||||
- **Unit tests**: token mint/lookup/retirement and the mapping's
|
||||
process-lifetime semantics; the custody split (the fleet and
|
||||
non-fleet forms branch as designed); the namespace binding's
|
||||
converter invocation; the fanout registry's subscribe /
|
||||
late-joiner-degrade / slow-subscriber-drop mechanics.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `src/ops.rs` carries the module structure under feature `ops`:
|
||||
the ops context, the token layer, the namespace binding, the
|
||||
fanout registry — no op handlers yet (they land in
|
||||
ops-json-ops/ops-fetch/ops-put)
|
||||
- [ ] The token layer: opaque, unguessable minting (the CSPRNG design
|
||||
point decided and recorded); the token↔(key, owner) mapping with
|
||||
process lifetime; validity domain = the minting node (a
|
||||
per-process registry; no cross-node form exists)
|
||||
- [ ] The guard-custody design decided and recorded: fleet — the
|
||||
guard drops at response completion (the row's expiry is the
|
||||
liveness signal); non-fleet — the registry holds the guard
|
||||
(nothing expires) until conversion
|
||||
- [ ] The namespace→liveness-source binding: per-namespace converter
|
||||
registration riding `RegistryCore::convert`; the conversion
|
||||
trigger design decided and recorded
|
||||
- [ ] The crate-internal renewal access: the ops module reaches
|
||||
`pin_renew`/`pin_check` through the fleet host (in-crate seam
|
||||
crossing; no public-API widening; no backend side-door)
|
||||
- [ ] The fanout registry: per-digest broadcast, bounded subscriber
|
||||
channels, the late-joiner degrade signal, the
|
||||
drop-and-late-join policy point recorded
|
||||
- [ ] New dependencies (`serde`, `serde_json`, the CSPRNG) are
|
||||
`ops`-gated; the default build's dependency tree is unchanged
|
||||
- [ ] Unit tests green; `cargo test` (default) green with the module
|
||||
compiled out; `cargo test --features ops` green;
|
||||
`cargo clippy --all-targets -- -D warnings` and
|
||||
`cargo fmt --check` green on both shapes; `cargo doc --no-deps`
|
||||
clean
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/ops-surface.md (§Op family — the hand-over
|
||||
contract; §ACL mapping; §Store-facing relationship)
|
||||
- docs/architecture/decisions/001-substrate-posture-and-ops-placement.md
|
||||
§3/§4 (placement; the facade-consumer rule)
|
||||
- docs/architecture/decisions/010-backend-io-seams-and-fleet-state-home.md
|
||||
§4 (the token contract: minting, custody, validity domain, the
|
||||
in-crate seam crossing)
|
||||
- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md
|
||||
§2 (the wire shapes: {token, digest}; the have renewal form; the
|
||||
validity domain; the non-fleet generalization)
|
||||
- docs/architecture/decisions/013-family-composition-reactive-boundary-and-feature-graph.md
|
||||
§1 (the renewal cadence is embedder composition), §4 (the feature
|
||||
graph)
|
||||
- docs/research/poc-largeblob-findings.md finding A3 (the fanout seam
|
||||
shape)
|
||||
- src/ops.rs (the ADR-001 stub this task fills)
|
||||
- src/gc.rs (`EngineFleetState::pin_renew`/`pin_check` — the seam the
|
||||
renewal access crosses; `RegistryCore::convert`)
|
||||
- src/store.rs (`Store::gc()` — the crate-internal access path)
|
||||
- tasks/review-wave-6.md (W6-3: `pin_check` deliberately unconsumed —
|
||||
this wave's renewal probe is the named consumer)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
Reference in new issue
Block a user