Harden ZoOps operations and discovery#1
Conversation
All three sync functions previously only upserted — they never removed rows whose source disappeared. This caused deleted zo-sites (zoops-dev, m365-value-explorer, etc.) to persist indefinitely, while new services (paperclip) never appeared. - syncZoSitesToServices: delete zo-site rows with no backing zosite.json - syncSpaceRoutesFromSnapshot: delete space-page rows absent from snapshot - syncServicesFromLogs: new function that upserts log-detected services and prunes ones whose /dev/shm/*.log file no longer exists; skips IDs already owned by another type to avoid overwriting zo-site entries - server.ts: import and call syncServicesFromLogs on startup, background interval, and the manual /api/sites/sync endpoint Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds getSpaceBaseUrl() to settings.ts that checks the spaceBaseUrl settings key first, then falls back to deriving from the ZO_BASE_URL env var (handle.zo.computer → handle.zo.space). Removes the hardcoded thomstech.zo.space string from syncSpaceRoutesFromSnapshot(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e75d479349
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let removed = 0; | ||
| if (validIds.length > 0) { | ||
| const placeholders = validIds.map(() => "?").join(","); | ||
| removed = (db.prepare(`DELETE FROM services WHERE type='service' AND id NOT IN (${placeholders})`).run(...validIds)).changes; |
There was a problem hiding this comment.
Preserve manually registered services during log sync
When the background scan runs with any /dev/shm/*.log files present, this delete removes every type='service' row whose random manual ID is not one of the log-derived IDs. Since registered services are inserted with type: "service" and is_auto_detected=0, they will be silently deleted on startup or within five minutes unless their ID happens to match a log filename; restrict this cleanup to auto-detected log rows.
Useful? React with 👍 / 👎.
| import { realpathSync } from "fs"; | ||
| import { relative } from "path"; | ||
|
|
||
| const WORKSPACE_ROOT = realpathSync("/home/workspace"); |
There was a problem hiding this comment.
Avoid import-time crashes when workspace root is absent
In checkouts or CI containers where /home/workspace has not been created, this top-level realpathSync throws before any route handler runs; in this repo location (/workspace/zoops) it causes bun test to abort while importing backend-lib/explorer.ts, and the server imports the same module unconditionally. Resolve the root lazily or tolerate a missing workspace so the app can start and return validation errors for explorer requests instead of crashing at import time.
Useful? React with 👍 / 👎.
| const slug = r.path.replace(/^\//, "") || "home"; | ||
| const id = `space_${slug}`; |
There was a problem hiding this comment.
Encode Space route IDs without path separators
For nested Space routes such as /case-files/archive, this creates a service ID like space_case-files/archive. The Services and Sites pages call /api/services/${id}/check without encodeURIComponent, so the slash becomes an extra path segment and Hono's /api/services/:id/check route does not match; those discovered routes cannot be manually checked from the UI. Sanitize route IDs the same way other discovered service IDs are sanitized, or encode them before building API URLs.
Useful? React with 👍 / 👎.
What changed
Why
ZoOps could report stale automation schedules as current, treat private or non-HTTP inventory as failed or unchecked, miss current Zo Space surfaces, and expose control actions without a verified managed-service boundary. Existing QA validated HTTP success but not inventory semantics or cross-process writers.
Impact
The Command Center now prioritizes real operational exceptions, automatically checks eligible public HTTP services, keeps private surfaces visible without anonymous false alarms, and reports stale automation data as one freshness warning. Monitoring remains read-only and normal operation makes no AI calls.
Validation