Skip to content

Make agentop and the installer depend only on documented contracts #1500

Open

Description

@huang195

Part of #1437 (assessment: laptop data plane on Praxis) · prep work, useful to Cortex on its own

Status: there is no shipped Cortex–Praxis integration today (the paused cmd/cortex-praxis converter is not one), and the interim path is not decided (LLM traffic to praxis-ai as an inference server, or all traffic through Praxis via #1491). Nothing here depends on that choice: each item is useful to Cortex now, and would make a possible move to Praxis easier whichever path is taken.

Problem

If the data plane ever changes, to Praxis or anything else, agentop and the installer should not notice. Today they depend on Go proxy internals beyond the documented session API:

  • the full Go config struct;
  • the reload and stats endpoints' exact JSON;
  • the proxy's binary name.

Any change to those internals, or a different proxy build, breaks agentop exec, server, pricing, doctor, service, setup, uninstall, claude-code enable and the TUI's edit flows without warning. Some drift is already visible: edit/configmap.go:319 names a /reload/status field, last_success_unix, that doesn't exist.

What Cortex does today

agentop exec decodes /config into the whole Go config.Config (local_endpoint.go:289).

  • It uses three fields: listener.forward_proxy_addr, tls_bridge.mode and tls_bridge.ca_dir (:294).
  • The risk is the decode itself: json.Unmarshal into the whole struct fails if any field's type changes in a newer proxy.
  • The helpers that read those fields (wantedFromLoaded, bridgeEnabled in cmd_claudecode.go) take *config.Config and are shared with claude-code enable, so the two can't drift. Narrowing exec means changing that shared signature too.
  • /config serves the resolved config (after presets), which exec relies on. docs/README.md:82 says it's "as written, not as resolved"; that sentence is wrong.

13 non-test agentop files import core/config. Most of them call config.Load on ~/.cortex/config.yaml (27 calls): a dependency on the YAML schema, not on /config. Fixing exec removes none of the 13 imports.

Stats and health endpoints agentop relies on are described only in prose, with no schema, no fixtures and no single page (docs/laptop-service.md, framework-architecture.md, docs/pricing.md):

view=summary is missing from the API reference. agentop sends it on every timeline fetch (apiclient/snapshot.go:32). It's described in cmd/agentop/README.md and in the server's own GET / index, but not in docs/session-api.md. Neither documents the response's view echo, which agentop uses to detect the summary.

The proxy is recognised by name in at least six places, not one:

  • agentop: isProxyComm; proxyRunning, which repeats the literal instead of calling it (setup_probe.go:230); ourProxy, which compares the path binDir/cortex (:210); foreignHolderLines (cmd_service.go:785)
  • install.sh: :710, and :722 for the pre-rename binary

Related hardcoded identity: about ten filepath.Join(binDir, "cortex") sites, setupBinaryNames, the service label io.rossoctl.cortex and cortex.service, and the pid file proxy.pid. The name check exists to catch a recycled pid in that pid file.

The proxy's command-line contract is not written down anywhere complete. agentop invokes --version (parsed for its last word and for " (cpex) "), --local --write-config, --local --supervise, --supervise --config <path> (launchd) and --config <path> (systemd). The test stub cortexStub covers only the first three; docs/laptop-service.md mentions some in prose.

Proposal

  1. agentop exec decodes only what it uses: a local struct instead of config.Config, with the shared claude-code helpers taking that struct too.
  2. Document the stats and health contract on one page: the JSON of /reload/status, /config (the fields agentop reads), /pricing/table, /tls-bridge/unread, /healthz, /readyz, the default ports, and view=summary with its echo. Add JSON fixtures that agentop's decoders and the proxy's handlers are both tested against. Fix the /config sentence in docs/README.md. Coordinate with Feed recording, sessions and cost through one input that does not depend on the Go listeners #1497 item 5 and Decouple plugins from Go pipeline internals: session facts and serializable records #1499 item 2: one schema-and-fixtures effort.
  3. One constant for the proxy's process name, and one for its binary filename, used by every check above; install.sh references the constant in a comment. Write down the full command-line contract, including the service-unit invocations.
  4. Label each config section data plane or control plane in docs/README.md's config table, with its reload rule. That tells any future data-plane build which sections it must honour.
  5. Hot-reload as a stated contract. agentop edits ~/.cortex/config.yaml and waits on /reload/status (edit/local.go; also edit/pluginwrite.go:113 for agentop server). Document that as the contract, rather than an implementation detail of core/reloader, including two caveats: tls_bridge edits report success without being applied, and last_success is compared against agentop's clock.

Design questions

  1. Should /config stay the source for exec, or should the session API expose the three values directly?
  2. Should the process-name check become an executable-path check? The pid file already exists; the name check is what catches a recycled pid.

Done when

  • agentop exec builds without decoding config.Config.
  • The stats and health endpoints and view=summary are documented on one page, with fixtures that fail on drift in either the proxy or agentop.
  • The proxy's process name and binary filename each appear in one constant, plus install.sh, which references them in a comment.

Evidence read at main @ e455a04 (2026-10-25); not built or run.

Activity

  1. added
    enhancementNew feature or request
    laptopThe local (laptop) tool: install, abctl, the numbers
    on Oct 25, 2026
  2. changed the title [-]Praxis prep: Make agentop and the installer depend only on documented contracts[/-] [+]Make agentop and the installer depend only on documented contracts[/+] on Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestlaptopThe local (laptop) tool: install, abctl, the numbers

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions