Skip to content

Add versioned CLI JSON contract for server supervision #1761

Description

@djthorough

What problem are you trying to solve?

External process supervisors can launch an OpenKnowledge server, but they do not have a complete, versioned CLI contract for observing it or acting on it safely.

ok status --json and ok ps --json provide useful structured output today, but their document shapes are not versioned and omit information needed for reliable supervision. In particular:

  • A live process lock does not establish that the application is ready.
  • Effective runtime settings are not available through a supported inspection surface.
  • Existing runtime, protocol, capability, and launch metadata is not exposed as one documented contract.
  • ok stop and ok clean render important success, no-op, failure, and refusal outcomes as human prose.

Automation must therefore parse terminal messages or reproduce OpenKnowledge's lock inspection, ownership, and safety logic.

A representative workflow is:

  1. Launch ok start for a project and retain the child process.
  2. Poll project-scoped status until the server reports ready or the child exits.
  3. Inspect running project servers when reconciling supervisor state.
  4. Stop a selected server or clean stale state without parsing human output.

Reading .ok/local/server.lock directly is not an adequate substitute. It bypasses OpenKnowledge's stale-lock and ownership classification, does not establish application readiness, and couples consumers to an implementation file.

Proposed solution

Add an opt-in, explicitly versioned output format to the existing server-management commands:

ok status --format json-v1
ok ps --format json-v1
ok stop [target] --format json-v1
ok clean --format json-v1

Keep existing text output and existing --json output unchanged. --format json-v1 would be mutually exclusive with --json.

Each invocation would write exactly one JSON document to stdout, including refusals and no-op outcomes. Diagnostics and logs would remain on stderr, while exit codes would retain their independent meaning.

The v1 documents would share these rules:

  • A top-level schemaVersion: 1 and command discriminator.
  • Stable field names, types, outcome values, and refusal codes.
  • Consumers ignore unknown object fields so additive fields remain compatible.
  • Human-readable detail remains separate from stable machine codes.
  • CLI schema version, server protocol version, and runtime release version remain distinct.

Project status

The project-scoped status contract should include:

  • Resolved project identity.
  • Existing lock/process classification.
  • A separate readiness observation.
  • Process and server-instance identity.
  • Runtime and protocol versions.
  • Advertised capabilities and launch kind.
  • A server-confirmed snapshot of effective runtime settings when available.

Readiness should use these states:

  • ready
  • pending
  • failed
  • draining
  • unreachable
  • not-running
  • unknown

The first four correspond to states reported by the existing readiness endpoint. unreachable means an attributable live local server could not be probed successfully. not-running means no live server was found. unknown means the available evidence cannot establish readiness safely.

A successful result could resemble:

{
  "schemaVersion": 1,
  "command": "status",
  "project": {
    "root": "/path/to/project"
  },
  "server": {
    "state": "alive",
    "readiness": {
      "status": "ready",
      "checkedAt": "2026-09-22T14:30:00.000Z",
      "degraded": []
    },
    "process": {
      "pid": 1234,
      "startedAt": "2026-09-22T14:20:00.000Z",
      "launchKind": "interactive"
    },
    "runtimeVersion": "0.75.0",
    "protocolVersion": 2,
    "capabilities": ["http", "ws", "ui"],
    "runtime": {
      "source": "server",
      "revision": 1,
      "effectiveSince": "2026-09-22T14:20:00.000Z",
      "port": 4242,
      "bind": ["127.0.0.1"],
      "externalUrl": null,
      "idleShutdown": "30m"
    }
  }
}

Effective runtime settings must come from the running server's applied state. They should not be reconstructed by rereading configuration because flags, environment variables, unapplied file changes, and failed reloads can differ from what the process is actually using.

If the running server cannot confirm its effective runtime state, runtime should be null rather than containing values inferred from current configuration.

Machine-wide process inspection

ps should return a versioned object envelope rather than the legacy top-level array:

{
  "schemaVersion": 1,
  "command": "ps",
  "servers": []
}

ps remains a machine-wide inventory surface. It should expose available project, process, lock, version, and capability metadata without probing every discovered server. Project-specific readiness remains the responsibility of status.

Stop and clean outcomes

stop and clean should return finite structured results describing:

  • Targets considered.
  • Actions completed.
  • No-op outcomes.
  • Failures and partial failures.
  • Stable refusal codes.
  • Optional human-readable detail.

Existing safety behavior must remain unchanged, including refusing to stop a server with connected clients unless forced and retaining locks whose ownership cannot be established safely.

Acceptance criteria

  • Existing default output and existing --json output remain unchanged.
  • Every supported --format json-v1 invocation writes exactly one valid JSON document to stdout.
  • status distinguishes process state from application readiness.
  • Readiness uses a bounded probe and preserves existing server-reported readiness states.
  • Effective runtime values come from the running server's applied state, not a fresh resolution of configuration.
  • An unavailable effective-runtime snapshot is reported as unavailable rather than inferred.
  • Runtime snapshots change only after a setting has actually taken effect.
  • ps returns an object envelope containing the discovered servers.
  • stop and clean expose success, no-op, partial failure, and refusal outcomes without prose parsing.
  • Stable codes are documented and covered by contract tests.
  • Human-readable messages may improve without changing their associated codes.

Area

CLI

Alternatives considered

Change the existing --json shapes

This could provide a cleaner contract immediately, particularly for the top-level array returned by ps, but may break existing scripts. A new exact versioned format proves the contract without changing current consumers.

Add fields to the legacy output without versioning it

This would not establish compatibility rules or provide a clear path for future incompatible revisions.

Parse human-readable output

The existing messages are designed for people and should remain free to improve. Automation should not depend on their wording.

Read lock files directly

This duplicates safety-sensitive ownership and stale-lock logic, cannot establish application readiness, and makes external tooling responsible for internal lock compatibility.

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions