feat(cli): pic projects ls + §7 M3/track-complete docs

§7 M3 (part 3) — the ownership track becomes inspectable, completing M1–M3.

- server: GET /api/v1/admin/projects (projects_api) lists every project + its
  owned-group count (projects repo list_with_counts, one GROUP BY); behind the
  /admin middleware, like the groups list.
- CLI: pic projects ls (cmds/projects.rs + client projects_list); the
  apply_ownership plan-preview journey now also asserts projects ls (plat owns
  exactly 1 group; teamb listed).
- docs: design §7 + CLAUDE.md record M3 shipped and the whole §7 multi-repo
  ownership track (M1 claim · M2 attach ceiling · M3 preview + projects ls)
  COMPLETE; deferred = structural-divergence + declarative tree shape + per-env
  approval gating.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-06 21:26:10 +02:00
parent 30fe160f16
commit f673922d89
11 changed files with 244 additions and 29 deletions

View File

@@ -174,6 +174,16 @@ impl Client {
decode(resp).await
}
/// `GET /api/v1/admin/projects` — every §7 project + its owned-group count.
/// Backs `pic projects ls`.
pub async fn projects_list(&self) -> Result<Vec<ProjectDto>> {
let resp = self
.request(Method::GET, "/api/v1/admin/projects")
.send()
.await?;
decode(resp).await
}
/// `GET /api/v1/admin/groups/{id_or_slug}` — group + path + children.
pub async fn groups_get(&self, ident: &str) -> Result<GroupDetailDto> {
let ident = seg(ident);
@@ -1708,6 +1718,17 @@ pub struct GroupListItem {
pub owner: Option<String>,
}
/// One row of `pic projects ls` (§7 M3): a project + how many group nodes it
/// owns.
#[derive(Debug, Deserialize)]
pub struct ProjectDto {
pub slug: String,
pub name: String,
#[serde(default)]
pub owned_groups: i64,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Serialize)]
pub struct CreateRouteBody<'a> {
pub host_kind: HostKind,