fix(org-import): reconcile mode + audit-event emission #137
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "fix/org-import-reconcile-and-audit"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Closes the additive-import zombie bug — re-running
/org/importwith a tree shape that reparents same-named roles leaves the prior workspace online becauselookupExistingChild's dedupe is parent-scoped (differentparent_id→ "different" workspace, even when name matches). Caught 2026-05-08 after a dev-tree re-import left 8 orphans co-existing with the new tree on canvas until manual cascade-delete.Three layers
B —
mode: "reconcile"on/org/importAfter the import loop, online workspaces whose name matches an imported name but whose id isn't in the result set are cascade-deleted. Default mode
""/"merge"preserves existing additive behavior. Empty-set guards prevent accidental "delete everything" if either array comes up empty.Helper extraction —
WorkspaceHandler.CascadeDeleteLifted from the existing Delete HTTP handler so OrgImport's reconcile path shares the same teardown sequence (#73 race guard, container stop, volume removal, token revocation, schedule disable, event broadcast). The HTTP Delete handler still inlines the same logic; deduplication tracked as tech-debt follow-up.
C —
emitOrgEvent→structure_eventsRecords
org.import.started+org.import.completedwith mode, created/skipped/reconcile_removed counts, duration_ms, error. Replaces the lost-on-restart stdout-only log shape for an audit-trail surface queryable by SQL. Closes the "what happened at 20:13?" debugging gap that motivated this fix.Verification (live against local platform)
created_count=0, skipped_count=9reconcile_removed_count: 1Tests
7 new unit tests:
walkOrgWorkspaceNames— flat / nested /spawning:falsesubtree (still counted) / empty-name skippedemitOrgEvent— success path + DB-error-swallow (telemetry must never fail the request)errString— nil + non-nilFull handler suite green.
Test plan
Follow-ups (separate tickets)
Delete()HTTP handler vsCascadeDeletehelper (same logic in two places)/tmp/rfc-sop-v2.md, awaiting Hongming GOstartedevent fires before YAML is loaded sonameis empty whendiris used; emit after parse for completenessPersona-drift note
Pushing as
claude-ceo-assistantper the interim force-merge path; flagged here for the SOP-6 per-phase approval gate work.🤖 Generated with Claude Code