feat(apply): [project] parent_group attach-point ceiling

§6/§7 M2: a project's attach point is its ceiling — applies are refused for
any node not strictly within the declared subtree, so a repo can't reach (or
claim) above its local root even with the RBAC to do so.

- `[project] parent_group = "<slug>"` (ManifestProject + ProjectDecl); absent
  = instance root = no ceiling (the default, backward-compatible).
- `check_within_attach`: the attach group must be a PROPER ancestor of a group
  node (you can't apply the attach point itself) or an ancestor (inclusive) of
  an app node's group; resolved via `groups.ancestors`. Enforced read-only in
  apply_owner (prologue) + apply_tree (per node), before the claim.
- `ApplyError::OutsideAttachPoint` → 422 (a scope error, like Invalid).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-06 20:57:03 +02:00
parent 5bd72956b1
commit 5bb1ccad5e
3 changed files with 88 additions and 1 deletions

View File

@@ -525,7 +525,7 @@ impl IntoResponse for ApplyError {
fn into_response(self) -> Response {
let (status, body) = match &self {
Self::AppNotFound(_) => (StatusCode::NOT_FOUND, json!({ "error": self.to_string() })),
Self::Invalid(_) => (
Self::Invalid(_) | Self::OutsideAttachPoint(_) => (
StatusCode::UNPROCESSABLE_ENTITY,
json!({ "error": self.to_string() }),
),