Skip to content

Harden ZoOps operations and discovery#1

Merged
sthoms12 merged 6 commits into
mainfrom
agent/zoops-operations-hardening
Jul 17, 2026
Merged

Harden ZoOps operations and discovery#1
sthoms12 merged 6 commits into
mainfrom
agent/zoops-operations-hardening

Conversation

@sthoms12

Copy link
Copy Markdown
Owner

What changed

  • Makes automation snapshots authoritative, classified, and freshness-aware.
  • Adds composite health, persistent public HTTP monitoring, service history, and exception-first dashboard status.
  • Replaces stale static Zo Space discovery with authenticated live metadata plus last-known-good caching.
  • Removes unsafe restart behavior and validates writable API boundaries.
  • Makes AI synchronization optional, explicit, and disabled by default.
  • Improves accessibility, responsive behavior, error states, and release tooling.

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

  • 30 unit tests
  • TypeScript validation
  • Production build
  • 27-route production smoke suite
  • SQLite integrity and foreign-key checks
  • Desktop/mobile and accessibility verification

zocomputer and others added 6 commits June 28, 2026 04:19
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>
@sthoms12
sthoms12 marked this pull request as ready for review July 17, 2026 01:19
@sthoms12
sthoms12 merged commit a3e86b2 into main Jul 17, 2026
@sthoms12
sthoms12 deleted the agent/zoops-operations-hardening branch July 17, 2026 01:19

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread backend-lib/discovery.ts
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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment thread backend-lib/explorer.ts
import { realpathSync } from "fs";
import { relative } from "path";

const WORKSPACE_ROOT = realpathSync("/home/workspace");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread backend-lib/discovery.ts
Comment on lines 407 to 408
const slug = r.path.replace(/^\//, "") || "home";
const id = `space_${slug}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant