Skip to content

CLI JSON Output

OpenLogos CLI supports --format json on its machine-facing commands — including status, next, watch, verify, smoke, deploy-done, merge, slice plan, detect, flow show, change-lint and module list — producing structured JSON for programmatic consumption by external tools like RunLogos.

  • Trigger: Append --format json to any supported command
  • Output target: JSON goes to stdout; errors go to stderr
  • Format: Compact single-line JSON (no indentation), suitable for piping
  • Exit codes: Same as human-readable mode
  • Encoding: UTF-8
  • Field naming: snake_case

All commands share a common envelope:

{
"command": "<command-name>",
"version": "<cli-version>",
"timestamp": "<ISO-8601>",
"data": { ... }
}

command is the command name, for example "status", "next", "verify", "smoke", "merge", "slice plan", "detect" or "module list". The top-level version is the CLI version. It is not the contract version (see below).

status, next and verify include a machine contract version in data:

{ "command": "verify", "version": "0.16.0", "timestamp": "…", "data": { "contract": { "version": "1.1.0" }, … } }
  • data.contract.version follows SemVer independently of the CLI version. A major bump means a required field was removed or changed meaning, or a closed enum lost a value. A minor bump adds optional fields or enum values. A patch clarifies without changing shape.
  • Values currently emitted: status / next emit 1.0.0, 1.1.0 (feature groups or slice-verification fields present) or 1.3.0 (plan-package fields present). verify emits 1.0.0, or 1.1.0 when slice-aware fields are present.
  • The npm package ships JSON Schemas for these contracts under spec/schema/: status.schema.json, next.schema.json and verify.schema.json. Each schema embeds its version (x-contract-version) and validates envelope.data. The schemas are backward-compatible supersets, so older responses still validate.
  • Consumers: validate data against the bundled schema. If contract is missing or its major version is unknown, fall back to a conservative mode (follow next only, and treat heuristics as observations). Treat any unknown enum value conservatively. Ignore unknown fields.

The 0.15.0 flow reduction removed several projections. Tools that read --format json should no longer expect them:

CommandRemovedNotes
verifychecklist, ac_traceThe verify gate now has only three criteria: consistent result ledger, zero failed, zero uncovered. gate.reason no longer produces checklist_incomplete or ac_trace_incomplete. If an old response contains them, read it as FAIL.
status / nextplan_state.clarificationDecision clarification is no longer a hard gate. Contract 1.2.0 is no longer emitted.
status / nextMerge-transaction and slice-transaction projectionsMerging is a single openlogos merge <slug> call, and slices are written by openlogos slice plan. Contract 1.4.0 is no longer emitted.
Terminal window
openlogos detect --format json

Returns CLI version, Node.js version, and project detection information:

{
"cli": {
"version": "0.12.9",
"node_version": "v22.0.0"
},
"project": {
"name": "my-project",
"locale": "zh",
"lifecycle": "launched",
"modules": [
{ "id": "core", "name": "核心功能", "lifecycle": "launched" }
],
"description": "项目描述",
"source_roots": { "src": ["src"], "test": ["test"] }
},
"yaml_diagnostics": null
}

project is null when run outside an OpenLogos project.

Terminal window
openlogos status --format json

Returns phase progress, module state, active proposals, and suggestions:

Key fieldDescription
phases[]All 13 phases with key, label, done, skipped, files
modules[]Per-module lifecycle, current phase, phase progress, active change, suggestion
modules[].active_changeProposal step, task progress, deployment decision, conflict detection
modules[].active_change.code_requiredSingle source of truth for “does this proposal need code” (see below)
current_phaseFirst incomplete phase key (or null if all done)
lifecycleProject lifecycle derived from module states
yaml_diagnosticsParse recovery status if YAML has issues

The proposal_step field tracks change proposal lifecycle:

StepMeaning
writingProposal/tasks still has template placeholders
ready-to-deltaProposal + tasks filled, no delta yet, PLAN_APPROVED absent — the plan-exit “approve plan” gate
delta-writingProposal filled; delta tasks not all checked
ready-to-mergeAll delta tasks checked (the spec-exit gate)
merge-generatedLegacy value from the pre-0.15 two-step merge. The single-call openlogos merge goes straight to the spec-complete steps.
spec-complete-requiredCode proposal has no spec-complete marker; run no-delta openlogos merge <slug> or finish merge before planning slices
test-id-requiredSpec-complete is done, but no real UT-* / ST-* / SMOKE-* IDs are available for slice planning
ready-to-implementSpecs merged, code_required, [code] slices not yet written by slice-planner — the slice-exit “approve slices” gate
codingSlices approved; code tasks not all checked
ready-to-verifyAll code tasks checked
verify-passedopenlogos verify passed
verify-failedopenlogos verify failed
ready-to-deployVerify passed, deployment pending (the deliver-entry gate)
deploy-doneDeployment executed
ready-to-smokeDeployment done, smoke pending
smoke-passedopenlogos smoke passed
smoke-failedopenlogos smoke failed

ready-to-delta and ready-to-implement were added by the change-flow redesign and the slice-planner split respectively; consumers (including RunLogos) must recognise them. implementing / in-progress remain legacy-compatible values.

spec-complete-required and test-id-required are blocking diagnostics for code proposals before plan-slices. They are not human gates and --auto must not skip them. In both states, next_node.id must not be plan-slices; the JSON includes reason (no_delta_spec_marker_missing or code_change_requires_real_test_ids) so drivers can route the work to merge/spec-complete or test-ID authoring instead of dispatching slice-planner.

modules[].active_change.code_required (boolean) is the single source of truth for whether a proposal needs code implementation. It equals the internal predicate isCodeRequiredForProposal — true when the proposal carries a ## [code] requirement (a [code] section, [delta]-added UT-*/ST-*/SMOKE-*, or a proposal-level code declaration), false for pure-doc / pure-spec proposals. Consumers should read this field directly instead of re-guessing with keyword regexes.

  • Appears only when active_change is non-null; with no active proposal the whole object (and this field) is absent, so projects without an active proposal see no new fields (golden zero-drift).
  • Consistency: code_required==false ⟹ next_node.id is never code/plan-slices and the slice subflow (when: code_required) is skipped entirely. code_required==true with [code] still on template ⟹ proposal_step=="ready-to-implement", next_node.id=="plan-slices".
  • For no-delta code proposals, code_required==true still requires SPEC_MERGED/MERGED first. Missing spec-complete marker yields spec-complete-required; missing real test IDs after spec-complete yields test-id-required.

These fields drive external orchestrators. All follow the same mount + omit rule: with modules[] present they mount at modules[].*; legacy projects fall back to the top level; consumers read modules[].* first, then the top level. Each is omitted entirely when inactive, preserving golden zero-drift.

loop_state — present only when the implement loop is active (max_iters > 1; builtin launched satisfies this by default):

FieldTypeDescription
subflow_idstringThe loop’s subflow id (e.g. implement)
untilstringConvergence predicate (tests_green | code_slices_green)
max_itersnumberResolved iteration ceiling
iterationnumberCompleted verify rounds (LOOP_ITERS lines for the current module)
convergedbooleanLast verify round green
escalatedbooleaniteration >= max_iters && !converged (hit the ceiling unconverged)
exhausted_skippableboolean | omittedWhether the loop-exhausted gate can be released by --auto; emitted only when the overlay set-loop wrote exhausted_gate

slice_state — present only when the slice loop is active (until == code_slices_green && max_iters > 1; always on under launched):

FieldTypeDescription
totalnumberTotal [code] slices
donenumberChecked slices (section_complete:code count)
currentstring | omittedFirst unchecked [code] line title; omitted when all done
remainingnumbertotal - done

plan_state — a launched diagnostic object so consumers do not mistake tasks.md checkbox progress for a planning failure:

FieldTypeDescription
plan_readybooleanproposal/tasks out of template, no plan-layer block
plan_gate_pendingbooleanStopped at plan-exit: ready-to-delta && PLAN_APPROVED absent
plan_approvedbooleanPLAN_APPROVED present, or already past ready-to-delta
tasks_template_filledbooleantasks.md out of template with valid section structure
tasks_execution_done / tasks_execution_totalnumberCheckbox progress of the current section — must not be used to infer plan readiness
tasks_execution_scopestringdelta | deploy | code | none
diagnosticstringShort human/driver note on the waiting or blocking state

next_node (on openlogos next only) — the orchestration hint for the node to handle this turn, carrying skill / working_agent / review_agent / pre_script / post_script from the resolved flow. It defaults to the current frontier node, with exceptions:

  • In the slice loop (unconverged, below ceiling) it points to the code work node and carries next_node.slice (= slice_state.current, “only do this slice”).
  • At a slice/plan gate it carries next_node.gate_id alongside id — e.g. ready-to-implement with plan-slices done emits id: "plan-slices" + gate_id: "slice-exit", telling the host not to re-dispatch the skill but to treat it as a human gate. Gate-id mappings: ready-to-delta → plan-exit, ready-to-merge → spec-exit, ready-to-implement → slice-exit, ready-to-deploy → deliver-entry.
  • It is omitted for command-level suggestions (all_done, openlogos change <slug>, openlogos launch) and after a gate is auto-released.

GATE_AUTO_PASSED — an append-only audit ledger (JSONL) in the active proposal directory. Each time next --auto auto-releases a skippable:true gate it appends {gate_id, proposal_step, timestamp}. It is audit only, not a state source — historical lines never authorize a later deploy or gate; default next (no --auto) ignores them. State advances only on the real marker (PLAN_APPROVED for plan, SLICES_APPROVED for slice) or actual delta/slice output. Deployment release is gated on the live gate_auto_passed === true in the current next --auto response.

Terminal window
openlogos verify --format json

Returns the Gate 3.6 result. The gate has three criteria: consistent result ledger, zero failed cases, zero uncovered cases. A failing pre-run command also fails the gate.

Key fieldDescription
contract.version1.0.0, or 1.1.0 when slice-aware fields are present
summaryDefined / manual / executed / passed / failed / skipped / uncovered counts, coverage and pass rate
gateresult ("PASS" / "FAIL") and reason
failed_cases[], uncovered_cases[], skipped_cases[]Case lists
consistencyok, reasons, unknown_result_ids, manual_result_ids, outside_eligible_result_ids, invalid_results, count_mismatches, count_mismatch_details
pre_runMode, per-stage status / exit_code / duration_ms, result paths, diagnostics and suggestions
sandboxSandbox mode, isolation, status, diagnostics, optional infos
smoke_precheckSmoke coverage precheck for the active proposal’s changed SMOKE-* cases
report_pathAcceptance report path
verify_mode, attempted_slice_id, eligible_test_ids, pending_test_ids, manifest, checkpoint, test_change_setOnly in slice-aware runs (verify_mode: slice-checkpoint or final)
ModeDescription
noneNo pre-run command configured
pre_run_commandSingle verify.pre_run_command executed
two_phaseregression_command + incremental_command with last-write-wins merge

Each executed pre_run.commands[] entry carries stdout_tail and stderr_tail: the last 80 lines and at most 8 KiB of the command’s output, cut at a UTF-8 character boundary, with ANSI control codes removed. These fields exist only in JSON output. They are not written to acceptance-report.md and the full output is never saved to disk. Skipped entries have no tails.

ReasonDescription
pre_run_failedA pre-run stage (pre_run, regression or incremental) exited non-zero and the ledger has no failed cases. The stale results file is not used to pass the gate.
result_ledger_inconsistentThe result ledger is inconsistent (see consistency)
failed_casesOne or more test cases failed
incomplete_coverageSome defined cases have no result
smoke_runner_missing / smoke_reporter_missingThe smoke coverage precheck blocked: the proposal changed SMOKE-* cases but no runner or reporter is wired up

Priority: if a pre-run stage failed, the reason is failed_cases when there are failed cases, otherwise pre_run_failed. If all pre-run stages passed or none is configured, the order is result_ledger_inconsistent → failed_cases → incomplete_coverage.

In a multi-slice launched change, if TEST_SLICE_MANIFEST.json is missing, invalid or stale, verify does not run any tests. It prints an envelope whose data has reason (for example test-slice-manifest-missing) and slice_verification_state, and exits with 2. For test-slice-manifest-unsupported and test-slice-assignment-ambiguous it exits with 1. The recovery is to re-run openlogos slice plan --file <slices.json>. See Test Slice Manifest.

status and next expose the same slice_verification_state (manifest_status, verify_mode, attempted_slice_id, confirmed_slice_ids, eligible_test_ids, pending_test_ids). When recovery is possible, next returns next_node.id: "plan-slices" with skill: "slice-planner" and dispatch.idempotent: true.

Terminal window
openlogos smoke --format json
openlogos smoke --env staging --format json

Returns post-deployment smoke verification results:

Key fieldDescription
environmentTarget environment (from --env flag, or null)
summarySame structure as verify summary
gateGate 3.8 result and reason
changed_cases, diagnostics, runnersThe active proposal’s changed SMOKE-* IDs, precheck diagnostics, and the runners that were found
required_skipped_casesOnly when non-empty. [{id, detail}] for skips of the proposal’s changed cases, which block the gate (required_cases_skipped).
platform_skipped_casesOnly when non-empty. [{id, detail}] for skips accepted because the result carries reason_code: "platform-unavailable" and a non-empty detail.
sandboxSandbox execution status
report_pathGenerated smoke report path
result_pathSmoke results JSONL path

gate.reason priority: failed_cases → precheck diagnostic code (smoke_runner_missing / smoke_reporter_missing / smoke_cases_uncovered) → required_cases_skipped → incomplete_coverage.

Terminal window
openlogos merge <slug> --format json

openlogos merge applies all deltas in a single call. The write is atomic: either everything lands or nothing does. On success, data contains:

FieldDescription
slugProposal slug
result"merged" (first merge, including no-delta spec-complete), "already-merged" (nothing changed since the recorded merge), or "amended" (an already-merged proposal’s deltas changed and were re-applied incrementally from the recorded merge baseline)
delta_digestDigest of the deltas that were applied
target_count, targetsSpec files written
spec_merged_pathPath of SPEC_MERGED
test_change_setTest IDs added/modified/removed by the proposal
invalidated_markersFor amended: the acceptance markers cleared because the spec changed (VERIFY_PASS, VERIFY_FAIL, DEPLOY_DONE, SMOKE_PASS, SMOKE_FAIL, whichever were present)
amendnull, or for amended: {targets_recomposed, targets_added, targets_restored}

status projects pending amendments at modules[].active_change.spec_amend: merged_delta_digest, current_delta_digest, pending (null when there is no baseline, which means it cannot be determined), blocked_reason (baseline-missing, baseline-unreadable, drift, create-withdraw or prototype-changed) and amend_count. If a merge target was edited by hand after the merge (drift), merge refuses to amend (MERGE_AMEND_DRIFT) and explains what to check. Errors use the error envelope below.

Terminal window
openlogos slice plan --file <slices.json> --format json

On success, command is "slice plan" and data contains slug, slice_count, slice_ids, task_fingerprint, spec_fingerprint and manifest_path. On failure, the error envelope goes to stderr with error.code (SLICE_PLAN_*). When the validator rejected the input, a top-level violations array lists each item with code, path, message and fix_hint. See openlogos slice.

Terminal window
openlogos module list --format json

Returns the module registry:

{
"modules": [
{ "id": "core", "name": "核心功能", "lifecycle": "launched" },
{ "id": "payment", "name": "支付模块", "lifecycle": "initial" }
]
}

When a command fails, JSON mode outputs an error envelope to stderr:

{
"command": "<command-name>",
"version": "<cli-version>",
"timestamp": "<ISO-8601>",
"error": {
"code": "PROJECT_NOT_INITIALIZED",
"message": "logos/logos.config.json not found."
}
}
Error codeDescription
PROJECT_NOT_INITIALIZEDNot in an OpenLogos project
NO_TEST_RESULTSTest results JSONL file not found
NO_TEST_CASESNo test case spec files found
NO_SMOKE_RESULTSSmoke results JSONL file not found
NO_SMOKE_CASESNo smoke case spec files found
Terminal window
# Check gate result in scripts
openlogos verify --format json | jq '.data.gate.result'
# Get current phase
openlogos status --format json | jq '.data.current_phase'
# List module lifecycles
openlogos module list --format json | jq '.data.modules[] | {id, lifecycle}'
# Conditional check
if openlogos verify --format json 2>/dev/null | jq -e '.data.gate.result == "PASS"' > /dev/null; then
echo "All tests passed!"
fi