Skip to content

feat(cli): introduce CNC Agent Protocol v1 - #1483

Draft
yyyyaaa wants to merge 18 commits into
mainfrom
yyyyaaa/agentic-cnc-cli-audit
Draft

feat(cli): introduce CNC Agent Protocol v1#1483
yyyyaaa wants to merge 18 commits into
mainfrom
yyyyaaa/agentic-cnc-cli-audit

Conversation

@yyyyaaa

@yyyyaaa yyyyaaa commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Important

This draft is a WIP snapshot, not currently merge-ready. The implementation was built from b0843e230; main is now 210 commits newer, and a synthetic merge reports 30 conflicted paths, including jobs/functions code that main has since removed. The protocol work needs an explicit port-forward/reconciliation pass before normal review and merge.

Summary

This PR introduces CNC Agent Protocol v1 and migrates all seven existing CNC command families onto a Constructive-owned, registry-backed runtime:

  • contexts
  • authentication
  • raw GraphQL execution
  • code generation
  • GraphQL server
  • GraphQL explorer
  • jobs

Agents and other non-terminal consumers now get a typed, discoverable, noninteractive execution boundary with validated JSON/JSONL output, stable errors and exit codes, cancellation, recursive redaction, and deterministic documentation projections.

This is the execution foundation for later semantic app graphs, declarative change sets, MCP, preview deployment, and durable operations. Those features are intentionally outside this PR.

Why

Previously, command definitions, help, prompts, implementation, and generated agent instructions were separate sources of truth. Structured invocation could still prompt, mix prose into stdout, depend on mutable process state, expose secrets through diagnostics, or claim that a dry run was safe while changing files.

Long-running commands behaved as terminal processes instead of observable services, context and credential state was not safely coordinated across processes, and codegen could leave partially applied output or delete files without a reliable ownership boundary.

This PR fixes that command boundary before higher-level agent workflows are added.

CNC Agent Protocol v1

The new @constructive-io/cli-runtime package provides:

  • TypeBox-authored command, input, output, event, and error contracts
  • Ajv 8 runtime validation using JSON Schema Draft 2020-12
  • stable command IDs and shell paths
  • strict argv and explicit environment binding
  • transport-independent execute(input, context) operations
  • schema-validated results and domain events
  • recursive sensitive-value redaction
  • cancellation through AbortSignal
  • deterministic human, JSON, and JSONL rendering
  • registry-generated help, schemas, Markdown, Skills, and completions
  • contract snapshots that detect unversioned breaking changes
  • dual CommonJS/ESM publication with Node 18.17+ support

The machine protocol is versioned independently as constructive.dev/cli/v1.

JSON emits exactly one terminal envelope. JSONL emits operation.started, validated domain events, and exactly one completed, failed, or cancelled terminal event. Structured stdout contains protocol records only, and structured stderr remains empty.

Exit Meaning
0 success
1 known operation failure
2 invalid invocation
70 internal or contract failure
130 cancellation

CLI behavior

--agent enables strict noninteractive execution, suppresses terminal effects and update checks, and defaults to JSONL. Explicit JSON or JSONL output also forbids prompts.

--agent
--interactive
--non-interactive
--format human|json|jsonl
--cwd <path>
--yes
--no-color
--debug
--help
--version

Safety flags are command capabilities rather than misleading universal switches. Mutation approval, destructive codegen overwrite, and dry-run behavior appear only on commands that can enforce them.

Registry-backed discovery includes:

cnc commands --format json
cnc schema execute --format json
cnc help codegen
cnc docs export --target .constructive/agent-docs --dry-run
cnc completion zsh

Both cnc and constructive remain supported.

State and authentication safety

Context and authentication operations now use versioned, schema-validated state with:

  • atomic temp-write, fsync, and rename publication
  • bounded directory locking with token ownership
  • live-process detection before stale-lock reclamation
  • restrictive state and credential permissions
  • refusal to silently replace corrupt or newer state
  • migration backups
  • context-name and HTTP(S) endpoint validation
  • traversal and symlink-escape protection
  • atomic credential and active-selection cleanup on context deletion

Agent remote operations require --context or CNC_CONTEXT; they never fall back to mutable global context selection. Human mode retains current-context fallback for compatibility.

Agent tokens come from CNC_TOKEN or --token-stdin, ambiguous sources are rejected, and token values never enter protocol output.

Raw GraphQL execution

cnc execute now:

  • parses the GraphQL document before sending it
  • requires an operation name when the document is ambiguous
  • rejects subscriptions
  • supports explicit anonymous execution
  • applies a configurable 30-second default timeout
  • responds to cancellation
  • distinguishes HTTP, network, timeout, parse, and GraphQL failures
  • preserves partial GraphQL data alongside errors
  • requires both --allow-mutation and --yes for agent or CI mutations
  • resolves endpoint and credentials from one immutable state snapshot

Transactional code generation

Codegen now computes a complete plan before applying filesystem changes. It:

  • treats --cwd as the root of all discovery and output
  • guarantees that every dry run performs zero writes
  • validates and classifies declarative JSON configs before execution
  • records generated ownership and content hashes
  • prunes only files previously owned by the generator
  • detects modified generated files and requires explicit overwrite approval
  • serializes planning and writes across concurrent processes
  • stages output on the target filesystem
  • restores the previous tree after failure
  • publishes the ownership manifest last
  • returns deterministic plan fingerprints and changed-file artifacts

Multi-target generation has one planning and application authority, so a child target cannot report success before the coordinated transaction commits.

Observable services

Server, explorer, and jobs commands emit:

service.starting
service.ready
service.stopping
service.stopped

They use injected event reporting and AbortSignal, return lifecycle handles, avoid process.chdir, suppress browser opening in agent mode, await signal cleanup, and terminate cancellation with exit 130.

Supporting GraphQL, Graphile, cache, and jobs packages now use instance-owned pools, caches, diagnostics, runtime environment, and lifecycle handles, so closing one operation cannot invalidate another concurrent operation.

Public-key signature authentication remains fail-closed because no cryptographic verifier exists yet.

Post-implementation audit

An adversarial review independently reproduced or qualified each issue before it was fixed:

Finding Resolution
Jobs reported readiness before initialization completed Await initialization before readiness and keep failures observable
Concurrent codegen processes applied stale plans Protect planning and application with target-scoped output locks
Manifest publication escaped rollback Stage the manifest inside the filesystem transaction
Auth mixed endpoint and token snapshots Resolve both from one immutable state read
Registry accepted malformed metadata and source-less bindings Validate metadata, bindings, IDs, paths, and projected schemas
Age-only state locks could be stolen from live processes Add ownership tokens and PID/host liveness checks
Codegen cast config without runtime validation Add Ajv validation and explicit single/multi classification
Hidden codegen collection reported false success Make target planning private and use one coordinated apply boundary
Published legacy handlers bypassed registry safety gates Remove legacy handlers and wildcard exports
Protocol modules exceeded maintainable boundaries Split execution, generation, transactions, and upload runtime into cohesive modules

Normal CNC routing already used the registry before the legacy-authority fix, but wildcard package exports still made the old handlers directly callable. Explicit exports preserve supported entry points, including the config-manager compatibility path, without publishing bypass routes.

Commit structure

The foundational change is split into signed, conventional subsystem commits for:

  1. the typed CLI runtime
  2. scoped Graphile storage caches
  3. per-instance GraphQL runtime environments
  4. observable jobs lifecycles
  5. transactional codegen operations
  6. CNC command-family migration
  7. CLI documentation
  8. packed-artifact CI

Ten subsequent fix(...)/refactor commits each contain one independently reproduced audit correction.

Compatibility and release boundary

This branch contains:

  • @constructive-io/cli@7.30.5
  • @constructive-io/cli-runtime@0.1.0
  • @constructive-io/graphql-codegen@4.55.2

Compatibility behavior:

  • existing human command names and both binaries remain available
  • canonical options use kebab case
  • retained historical spellings emit CLI_DEPRECATED warnings
  • the protocol shell, state, auth, and raw GraphQL surface support Node 18.17+
  • codegen and service feature packs remain registry-lazy optional dependencies at their Node 22.19+ dependency floor
  • omitted feature packs return CAPABILITY_UNAVAILABLE
  • Inquirerer remains behind the human prompt adapter
  • Incur is not a dependency

Verification

  • CLI runtime: 30 tests
  • Constructive CLI: 99 tests and 4 snapshots
  • GraphQL codegen: 409 tests and 123 snapshots
  • presigned URL plugin: 16 tests
  • focused cache, Graphile, server, explorer, and jobs lifecycle suites
  • affected CommonJS and ESM builds
  • deterministic docs generation with a clean second run and zero-write dry run
  • recursive redaction and ambient-state scans
  • registry contract-diff verification
  • packed core acceptance on Node 18.17 and Node 20
  • packed full-feature acceptance on Node 22.19
  • both binaries and CJS/ESM imports executed outside the monorepo
  • structured stdout/stderr, closed-stdin, exit-code, lifecycle, and SIGINT checks
  • git diff --check

The existing Knative jobs database E2E fixture still cannot provision locally because pgpm-verify and metaschema_public are absent. It fails during fixture migration before the changed lifecycle code runs; focused lifecycle and concurrency coverage is green.

Recommended review order

  1. Protocol contracts: packages/cli-runtime/src/contracts.ts, registry.ts, bindings.ts, and execution.ts
  2. Terminal adapter and registry: packages/cli/src/commands.ts and packages/cli/src/runtime/registry.ts
  3. State and GraphQL boundaries: packages/cli/src/config, runtime/state-commands.ts, runtime/execute-command.ts, and src/sdk
  4. Codegen transaction: graphql/codegen/src/core/output, target-generation.ts, and multi-generation.ts
  5. Service ownership: packages/cli/src/runtime/service-*, GraphQL server/explorer, Graphile cache/settings, and jobs
  6. Published-package gate: .github/workflows/run-tests.yaml and packages/cli/scripts/verify-packed-artifacts.mjs

Explicit non-goals

  • PGPM composition under CNC
  • generated csdk command migration
  • MCP transport
  • a semantic application graph
  • declarative full-stack change sets
  • full-stack plan and apply
  • durable journals, resume, or rollback
  • preview deployment
  • remote-mutation idempotency
  • opaque natural-language commands
  • TOON or YAML machine output

yyyyaaa added 18 commits July 28, 2026 11:37
Introduce registry-backed command contracts, schema validation, bindings, redaction, structured rendering, discovery, documentation projections, cancellation, and stable execution outcomes for reusable CLI operations.
Give embedded cache, schema, bucket, and presigned-upload services explicit instance ownership so concurrent CLI operations do not share or evict one another's state. Add ownership and configuration coverage for the new factories.
Thread explicit environment, cache, diagnostic, and lifecycle ownership through embedded GraphQL and Graphile services. Scope logger suppression to the current async operation and keep public-key signature authentication fail-closed.
Give workers, schedulers, services, and function runtimes explicit environment and ownership boundaries. Return lifecycle handles, await startup and shutdown, and cover concurrent instances and request cleanup.
Make source resolution cancellable and explicit, enforce declarative config safety, plan generated ownership before writes, preserve sensitive-value boundaries, and return structured artifacts suitable for registry-backed CLI execution.
Route context, authentication, GraphQL execution, codegen, server, explorer, and jobs through typed operations. Add deterministic discovery, strict agent-mode bindings, atomic state handling, service adapters, structured output, and compatibility-preserving human rendering.
Describe the command registry as the implementation authority, publish the structured invocation contract, and update human and agent guidance for discovery, state, GraphQL, codegen, and service operations.
Pack the complete CNC release set once and exercise engine-strict installations, CJS and ESM exports, both binaries, structured output, exit semantics, cancellation, lifecycle events, and signal cleanup across supported Node versions.
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