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:
MechaCat02
2026-06-29 22:26:43 +02:00
parent b79d8ef47d
commit 0973344515
19 changed files with 398 additions and 13 deletions

View File

@@ -162,6 +162,15 @@ enum Cmd {
cmd: ExtensionPointsCmd,
},
/// Shared group collections (§11.6) — read-only view of the KV collection
/// names a group offers as cross-app-shared. Authored declaratively via the
/// `[group]` manifest `collections = [...]`; scripts read/write them with
/// `kv::shared_collection("name")`.
Collections {
#[command(subcommand)]
cmd: CollectionsCmd,
},
/// Files inspection — list a collection's blobs, download bytes, or
/// delete a file. Read + delete only; writes go through scripts.
Files {
@@ -558,6 +567,16 @@ enum ExtensionPointsCmd {
},
}
#[derive(Subcommand)]
enum CollectionsCmd {
/// List the shared collections a group declares (§11.6). Group-only —
/// shared collections are owned by groups, not apps.
Ls {
#[arg(long)]
group: String,
},
}
#[derive(Subcommand)]
enum ScriptsCmd {
/// List scripts. With `--app`, scoped to one app; with `--group`, a
@@ -1985,6 +2004,9 @@ async fn main() -> ExitCode {
Cmd::ExtensionPoints {
cmd: ExtensionPointsCmd::Ls { app, group },
} => cmds::extension_points::ls(app.as_deref(), group.as_deref(), mode).await,
Cmd::Collections {
cmd: CollectionsCmd::Ls { group },
} => cmds::collections::ls(&group, mode).await,
Cmd::Files {
cmd:
FilesCmd::Ls {