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,128 @@
|
||||
---
|
||||
id: ops-put
|
||||
name: blobs/put — the verified Sink op, the pin hand-over, and the {token, digest} response
|
||||
status: pending
|
||||
depends_on: [ops-foundation, ops-call-harness]
|
||||
scope: moderate
|
||||
risk: high
|
||||
impact: component
|
||||
level: implementation
|
||||
tags: [wave-7, ops, put, tokens, hand-over, verification]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
`blobs/put` (ops-surface.md §Op family) — the client→server streaming
|
||||
op: `{namespace, digest?, len?}` offer + the byte stream in; the
|
||||
server verifies against the canonical derivation **before commit**;
|
||||
the response is **`{token, digest}`** (ADR-012 §2).
|
||||
|
||||
- **The op shape**: the Sink handler kind (alkcall ADR-046's
|
||||
`Pub`/Sink — the client→server streaming shape) with the
|
||||
**`channel_open` marker** (alkcall ADR-047); the establishment
|
||||
phase carries the offer ack. The **write** action (ACL;
|
||||
closed-by-default).
|
||||
- **Verification before commit** (ADR-006; ops-surface.md): the
|
||||
server hash-checks the received stream against the canonical
|
||||
derivation (`H("blob <len>\0" + content)`) before commit. A
|
||||
**known-length offer** (`len` present) is the encouraged one-pass
|
||||
path; an **unknown-length offer** rides the store's pre-threshold
|
||||
buffering path (ADR-003's option (d) semantics — the facade's
|
||||
`put(None, …)` form, the mid-stream stage flush and the
|
||||
restart-over-staged-bytes derivation pass riding unchanged). When
|
||||
the offer carries `digest?`, the derived key is checked against it
|
||||
— a mismatch rejects before commit (the offered digest is a
|
||||
claim; the derivation is the authority).
|
||||
- **The pin hand-over** (ops-surface.md §Pin hand-over; ADR-010 §4):
|
||||
the put lands pinned (pin-before-publish — the facade's put
|
||||
returns the `Pin` guard); the handler owns the guard; the custody
|
||||
split is the foundation's design, realized here:
|
||||
- **fleet**: the guard **drops at response completion** — the pin
|
||||
row's expiry is the only protection from that moment (the TTL
|
||||
machinery stays live on the ops path); the token mints over the
|
||||
committed row (the token↔(key, owner) mapping, owner = the
|
||||
serving instance's id);
|
||||
- **non-fleet**: the token registry **takes custody** of the guard
|
||||
— the token is the in-process guard's handle; nothing expires;
|
||||
the guard's fate is the custody lifecycle (conversion on the
|
||||
embedder's confirmation, or process-lifetime hold).
|
||||
The response returns `{token, digest}` — **both fields always
|
||||
present** (ADR-012 §2): `digest` is the stored entry's canonical
|
||||
digest hex (the unknown-length putter cannot know it beforehand and
|
||||
needs it for every later op); `token` is the minted opaque handle.
|
||||
Known-length putters receive the same shape.
|
||||
- **The fanout seam's producer side** (POC #3 finding A3): the put's
|
||||
stream reader is the canonical fanout producer — **one read** of
|
||||
the incoming stream (never polled twice), the **store arm**
|
||||
persists, **subscriber arms** serve concurrent fetchers of that
|
||||
digest while the put lands (serve-while-persisting); the wiring
|
||||
into the foundation's fanout registry. Backpressure: bounded
|
||||
subscriber channels — a slow subscriber stalls the broadcast,
|
||||
which is correct for a put (the put must land); the
|
||||
drop-and-late-join escape is the fetch side's policy.
|
||||
- **The dedup re-put**: CAS dedup means no new entry — the re-put
|
||||
renews the pin row (fleet: the joint tx's upsert-renewal of
|
||||
`(key, owner)`) or re-mints the custody guard (non-fleet); the
|
||||
re-put backstop's mechanics ride the facade's CAS unchanged.
|
||||
- **Tests through the harness**: the known-length round-trip (the
|
||||
response's digest matches the git derivation — the byte-exact
|
||||
standard); the unknown-length round-trip (the buffering path
|
||||
through the wire); the verification-failure shape (a
|
||||
byte-corrupted stream → no commit, the typed error, zero residue
|
||||
on both tiers); the offer-digest mismatch rejection; the hand-over
|
||||
custody split (fleet: the guard dropped at response — observable
|
||||
via the row's expiry path; non-fleet: the registry holds the guard
|
||||
— observable via have-present); the minted token renews via
|
||||
have's renewal form (the cross-task contract); the dedup re-put;
|
||||
the ACL denial (no write grant → denied).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] put exported as a Sink spec/handler pair with the
|
||||
`channel_open` marker; the establishment reply fields carry the
|
||||
offer ack
|
||||
- [ ] Server-side verification before commit; an offer-digest
|
||||
mismatch rejects before commit; a verification failure leaves
|
||||
zero residue on both tiers
|
||||
- [ ] The response is `{token, digest}`, both fields always present;
|
||||
`digest` is the canonical hex (the unknown-length putter's
|
||||
later-op handle)
|
||||
- [ ] The hand-over custody split implemented per ADR-010 §4 (fleet:
|
||||
the guard drops at response completion — the row is the
|
||||
signal; non-fleet: the registry holds the guard — nothing
|
||||
expires)
|
||||
- [ ] The fanout producer side wired: one read of the incoming
|
||||
stream, the store arm + subscriber arms (serve-while-
|
||||
persisting)
|
||||
- [ ] The dedup re-put renews (row upsert fleet-form / custody
|
||||
re-mint non-fleet)
|
||||
- [ ] Harness tests green; `cargo test` (default) green;
|
||||
`cargo test --features ops` green; clippy/fmt green on both
|
||||
shapes; `cargo doc --no-deps` clean
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/ops-surface.md (§Op family — the put bullet; §Pin
|
||||
hand-over for remote puts — the cross-wire contract)
|
||||
- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md
|
||||
§2 (the {token, digest} response; both fields always present)
|
||||
- docs/architecture/decisions/010-backend-io-seams-and-fleet-state-home.md
|
||||
§4 (the guard drops at response time — the TTL-machinery-alive
|
||||
rationale; the non-fleet custody)
|
||||
- docs/architecture/decisions/006-verification-posture-and-transfer-encoding.md
|
||||
(verification before commit)
|
||||
- docs/architecture/decisions/003-backend-contract-and-dispatch.md
|
||||
(the staged-put contract; the unknown-length path the wire rides)
|
||||
- docs/research/poc-largeblob-findings.md finding A3 (the fanout
|
||||
producer shape)
|
||||
- tasks/ops-foundation.md (the token layer, the custody design, the
|
||||
fanout registry), tasks/ops-call-harness.md (the harness),
|
||||
tasks/ops-json-ops.md (the renewal form the minted token rides)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
Reference in new issue
Block a user