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:
@@ -354,6 +354,31 @@ fn plan_previews_ownership_and_blast_radius() {
|
||||
claim(&plat, &team_a);
|
||||
claim(&teamb, &team_b);
|
||||
|
||||
// `pic projects ls` lists both registered projects with their owned-group
|
||||
// counts (plat owns exactly team_a).
|
||||
let projects = String::from_utf8(
|
||||
common::pic_as(&env)
|
||||
.args(["projects", "ls"])
|
||||
.output()
|
||||
.unwrap()
|
||||
.stdout,
|
||||
)
|
||||
.unwrap();
|
||||
let plat_row = projects
|
||||
.lines()
|
||||
.map(common::cells)
|
||||
.find(|c| c.first() == Some(&plat.as_str()))
|
||||
.unwrap_or_else(|| panic!("plat not in projects ls:\n{projects}"));
|
||||
assert_eq!(
|
||||
plat_row.get(2).copied(),
|
||||
Some("1"),
|
||||
"plat owns exactly one group:\n{projects}"
|
||||
);
|
||||
assert!(
|
||||
projects.contains(teamb.as_str()),
|
||||
"teamb must be listed:\n{projects}"
|
||||
);
|
||||
|
||||
// Plan acme with project `platform`: acme is unclaimed → action `claim`; the
|
||||
// blast radius lists the OTHER projects' descendant apps (plat + teamb).
|
||||
fs::write(
|
||||
|
||||
Reference in New Issue
Block a user