#678 owner-capability: seal project ownership in the registry + wake the OWNER back on dispatch completion. TDPLAN-0012 — 3 parts built in parallel against a frozen is_owner contract, verified end-to-end by devpulse.

@spawn: owner + registry_id written into the SEALED registry entries (authority lives in registry, not the self-editable passport). ensure_project_has_owner() now keys off citizen_class=manager (was earliest-created, which mislabeled @aipass) and writes the registry entry. get_owner()/is_owner() resolvers added. 315 tests, seedgo 100%.
@hooks: new registry_gate PreToolUse handler seals *_REGISTRY.json — blocks raw writes/tee/sed/rm + Edit/Write/MultiEdit, redirects to drone @spawn; per-clause bypass defeats compound-command smuggling; reads allowed. 82 tests, seedgo 100%.
@ai_mail: wake-back reslope — SKIP_SENDERS blocklist replaced by an is_owner allowlist. Only the project owner is woken when their dispatched agent completes; all other guards intact (depth cap, lock, occupancy, honest messaging, dispatch_wake.log). seedgo 100% on the changed file.

devpulse cross-part verify (REAL unmocked resolver): is_owner resolves devpulse-only; gate 13/13 incl compound-smuggle blocked + reads/drone-@spawn allowed; wake-back wakes owner / skips non-owner / respects depth-cap; 195 new-suite tests green together. Note: AIPASS_REGISTRY.json is gitignored — this ships the CODE; owner data regenerates per-install via ensure_project_has_owner. Still open (PART 4): gate watchdog+feedback on is_owner; portability of owner-only privileges across projects.
This commit is contained in:
AIOSAI
2026-07-10 00:46:17 -07:00
parent ae8f4a843a
commit 874c7fed2e
15 changed files with 1719 additions and 287 deletions
+3
View File
@@ -283,11 +283,13 @@ def _spawn_agent(
# Step 2b: Set owner field — first agent in the project is the owner
passport_path = target / ".trinity" / "passport.json"
passport_registry_id = ""
if passport_path.exists():
passport_data = json_handler.read_json(passport_path)
if passport_data:
passport_data.setdefault("citizenship", {})["owner"] = citizen_number == 1
json_handler.write_json(passport_path, passport_data)
passport_registry_id = passport_data.get("citizenship", {}).get("registry_id", "")
# Step 3: Regenerate .template_registry.json with fresh hashes
regenerate_template_registry(target)
@@ -312,6 +314,7 @@ def _spawn_agent(
detected_profile,
f"@{branch_lower}",
purpose or "New agent - purpose TBD",
registry_id=passport_registry_id,
)
# Step 5: Ensure at least one agent in the project is the owner