feat(cli): collections manifest + ls + journeys; rename to kv::shared_collection (§11.6 C5)
Finish the §11.6 KV slice: declarative authoring, read-only inspection, the
runtime journey, and docs — plus a forced SDK-name fix.
- SDK name: `shared` is a Rhai RESERVED keyword, so `kv::shared(...)` is a parse
error. Renamed the handle constructor to `kv::shared_collection(...)`
(keeps the user's "shared" intent; unambiguous). Updated everywhere.
- CLI manifest: `collections = [...]` on `[group]` only (ManifestApp has no such
field + deny_unknown_fields → an app manifest carrying it is a hard error);
Manifest::collections() accessor; build_bundle emits it.
- plan/apply: a `collection` row group in `pic plan`; created/deleted counts in
the apply summary; collections threaded through PlanDto/NodePlanDto/
ApplyReportDto.
- read-only `pic collections ls --group` → GET /admin/groups/{id}/collections
(viewer+), backed by ApplyService::collection_report + CollectionInfo.
- journey: a group declares `catalog`; app A writes, app B (same subtree) reads
it back; a sibling-subtree app gets CollectionNotShared; `collections ls` +
re-apply NoOp. Full suite 114/114.
- docs: groups-and-project-tool §11.6 (KV-only MVP shipped + deferrals),
sdk-shape.md (the shared-collection handle + trust model), CLAUDE.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1044,6 +1044,25 @@ Resolved items now live inline next to their topic. What genuinely remains:
|
||||
real shared-scope authz model. Optionally, trigger/route **templates** (§4.5) if cardinality
|
||||
demands.
|
||||
|
||||
> **Shipped — §11.6 KV slice (full shared read/write).** A group declares a collection
|
||||
> group-shared (`[group]` manifest `collections = ["catalog"]`, owner-polymorphic marker table
|
||||
> `0052_group_collections`, `kind='kv'`); the data lives in `0053_group_kv_entries`, keyed by the
|
||||
> owning `group_id` (NOT app — a shared row belongs to the group). Scripts read/write via the
|
||||
> **explicit** `kv::shared_collection("catalog")` handle (a distinct `GroupKvHandle`; `shared`
|
||||
> alone is a Rhai reserved word, hence `shared_collection`). The service resolves the owning group
|
||||
> from `cx.app_id`'s ancestor chain (nearest-group-wins) — **that walk is the isolation boundary**:
|
||||
> a foreign app's chain never contains the owning group, so the name returns `CollectionNotShared`.
|
||||
> **Trust model:** reads are open to any subtree script (anonymous public HTTP included — the
|
||||
> declaration *is* the grant); **writes require an authenticated editor+** on the owning group
|
||||
> (`GroupKvWrite`, fails closed on an anonymous principal). CASCADE on group delete; an app delete
|
||||
> leaves the shared data. Live- + journey-validated (app A writes, app B reads; a sibling-subtree
|
||||
> app gets `CollectionNotShared`).
|
||||
>
|
||||
> **Deferred (documented gaps):** write-triggers/events on shared collections (the "group trigger
|
||||
> has no app to watch" problem); docs/files/topics/queue shared collections (the `kind` column
|
||||
> generalizes); per-group total-size quotas + write-rate limits; CAS/`set_if`; app-declared
|
||||
> collections. Multi-node tree-apply leans on the runtime backstop for no-op edges, as elsewhere.
|
||||
|
||||
### 11.1 Re-sequencing review (post-Phase-3)
|
||||
|
||||
A review after Phase 3 shipped surfaced three adjustments to the 4→6 plan; carried here as decisions
|
||||
|
||||
Reference in New Issue
Block a user