← All documentation

Command-line reference

This is the complete ptc command and process contract.

The runtime-included ptc frontend and the source-checkout mix ptc frontend share the command engine and runtime path, but frontend-owned switches may differ. Use ptc help COMMAND and mix ptc help COMMAND as the authoritative grammar for each frontend.

Choose a command

CommandPurpose
ptc init DIRECTORYPublish a validated minimal application without replacing an existing target
ptc init DIRECTORY --example NAMEPublish one embedded example tree instead of the scaffold
ptc docs [PAGE] or --search TERMList embedded documentation, print one page, or find a term in prose pages
ptc help [COMMAND]Print the root command list, or the exact switches one command accepts
ptc validate MANIFEST or PROJECTLoad and compile without executing the workflow, and read the input files the declarations name
ptc run MANIFEST or PROJECTExecute the application entry
ptc prune PROJECT.jsonRemove old project run artifacts by age and size
ptc run MANIFEST --env-file FILELoad environment-backed credentials from this exact file
ptc doctor [MANIFEST or PROJECT]Report application and provider readiness
ptc models PROJECT.json or --host-config HOST.jsonList public installed model-alias declarations, each with the safe selector it configured
ptc catalog PROVIDER --host-config HOST.jsonAcquire one installed non-OAuth MCP provider and return its bounded tool definitions without calling a tool
ptc transcript RUN_ID ...Publish one correlated private model transcript
ptc replOpen a direct, manifest-backed, or analysis session
ptc viewer PROJECT.jsonBrowse a project's captured traces in a local web UI
ptc viewer PROJECT.json --env-file FILEUse one exact dotenv file for Viewer-started workflows and missions
ptc gateway GATEWAY.jsonServe configured tools over loopback MCP
ptc gateway GATEWAY.json --print-pinsDiscover every tool's startup pins without serving
ptc materialize MANIFEST or PROJECTExport installed component source, or publish a gated candidate

Help is generated from the same declarations as the strict parser, so use the help command for the frontend you invoke as its canonical command and option reference.

Prune project artifacts

ptc prune PROJECT.json scans the project's artifact root. Supply --max-age-days N, --max-bytes N, or both; neither has a default. Age removes runs whose newest artifact is older than N days. Size then removes the oldest remaining runs until the artifact total is at most N bytes. Each removal includes that run's trace, private trace, inspection, result, and envelope files. Files with unknown names are ignored.

Create <artifact-root>/keep/ with mode 0700 if it does not exist, then create an empty <artifact-root>/keep/<run_ref> file to retain a run past both limits. The keep/ directory must be owned by the current user and have mode 0700, like the other artifact directories. A kept run still counts toward the byte total. The output's kept_exceeds_max_bytes flag reports when kept runs alone exceed the size limit. dangling_keep_markers lists markers without a matching run; pruning leaves them in place.

Use --dry-run to preview the same selection without deleting files. The command writes one JSON object containing deleted, bytes_freed, kept, skipped (each with a run_ref and reason), remaining_bytes, dangling_keep_markers, and kept_exceeds_max_bytes. Runs with temporary publication siblings or files modified in the last ten minutes are skipped with reason in_progress. bytes_freed and remaining_bytes in a dry run describe the projected result. A Viewer or REPL session already open may hold a snapshot of a pruned run; reading it then can fail as unstable or unreadable. An interrupted deletion is recovered by the next real prune; --dry-run returns an error while recovery is pending so it changes no files.

Pruning does not remove append-lock files.

A provider-bearing manifest needs a host configuration. A project document can remember that path and its environment file. Before running it, active provider checks can make real requests and may incur cost:

ptc doctor ptc-project.json --connect

Readiness is derived from provider check rows: no rows report readiness: "not_applicable", all passing rows report readiness: "ready", and any skipped row reports readiness: "unverified". Missing provider commands, unreadable replay fixtures, and other attributable local failures produce failed check rows, readiness: "failed", and a nonzero exit without activating a provider. Successful active checks report ready; an attributable active failure also reports failed and exits nonzero. A manifest or package rejected during application validation is likewise reported as a failed application check instead of an internal command error. Complete readiness reports, including readiness: "failed", are written to stdout. Failed reports retain their nonzero exit status; failures that cannot produce a complete report are written to stderr. Runtime logger output, including TLS handshake alerts, is written to stderr so stdout remains one JSON document. --show-model-selectors adds only safe selectors.

Every readiness report carries usage, on the LLM rows a run reports. Each probed model alias contributes one call with the tokens and cost the provider attributed to it, so a CI step can account for what the check spent:

{"usage": {"llm_usage_state": "available",
           "llm_usage": [{"alias": "llm", "installation_revision": "v1",
                          "calls": 1, "successful_calls": 1, "usage_calls": 1,
                          "missing_usage_calls": 0,
                          "usage_overflow": false,
                          "usage": {"input": 8, "output": 1,
                                    "total_cost": {"currency": "USD", "microunits": 3}}}]}}

The probe asks for one output token, so this is about attribution rather than magnitude. A command that activated no provider reports an empty list, because it spent nothing. A failure that did activate one reports "llm_usage_state": "unavailable" and a null list rather than claiming zero: the request may have been billed with no result left to account for it. As in a run, total_cost is omitted when any call could not be priced, and missing_usage_calls counts calls whose usage may exist but was not observed, including an in-flight request stopped by the run clock. It is not limited to successful completions.

Read the embedded documentation

Every installation carries the language specification, references, and JSON Schemas that describe its own version. ptc docs lists them, ptc docs PAGE prints one page verbatim, and ptc docs --search TERM finds a case-insensitive substring in the prose pages:

ptc docs
ptc docs agent-guide
ptc docs --search result_schema
ptc docs schema-manifest

Pages are embedded when the executable is built, so they need no network access and cannot describe a different version. Search results identify the page, source line, and matching text, with the most relevant pages first. Each page contributes up to three lines, and the result covers up to ten pages. Schema pages are excluded from search. When a term starts with --, pass it in the joined form, such as ptc docs --search=--host-config.

An unrecognized page name is rejected as invalid arguments and points to --search. docs publishes no envelope and reads no application, host configuration, or project document.

Coding agents and LLMs driving the executable should start at Drive ptc as an agent, served as ptc docs agent-guide.

Run a manifest

For normal local use, keep stable paths in a project document:

ptc run ptc-project.json
ptc viewer ptc-project.json

The project form creates its fixed owner-only artifact layout as needed. See Project configuration. Direct manifest invocation remains the explicit low-level form below.

To inspect one mission with the same project paths but without starting the workflow, name it at invocation time:

ptc repl --project ptc-project.json --mission review

The project document remains the only path/configuration file normally supplied to a command; mission declarations continue to live only in ptc.json.

The trace directory must already exist:

mkdir -p traces
ptc run ptc.json
ptc run ptc.json --trace-dir traces
ptc run ptc.json \
  --env-file .env \
  --host-config ptc-host.json \
  --trace-dir traces \
  --inspect traces/run.ptcins \
  --envelope results/command.json

Useful run switches are:

  • --progress writes best-effort live status only to stderr; stdout remains the workflow result. On an interactive stderr it updates one ASCII line. When stderr is redirected or its width is unavailable, it writes bounded newline-delimited milestones and at most one unchanged heartbeat every ten seconds. It remains opt-in and works alongside PTC_VIEWER_URL.
  • --input INPUT.json replaces the manifest input with another normal object. Resolve the path as an application-relative document first; otherwise treat it as absolute or relative to the process working directory (same rule as --component-override-descriptor host paths).
  • --private-input INPUT.json does the same and classifies the run as private.
  • --output VALUE.json publishes a normal result value without replacing an existing file.
  • --private-output VALUE.json publishes a private result at owner-only mode and keeps it off stdout.
  • --trace-dir DIR writes <run_ref>.jsonl or <run_ref>.private.jsonl according to the run's artifact class.
  • --inspect FILE writes sensitive execution evidence to an owner-only .ptcins file.
  • --envelope FILE atomically publishes a convenience copy of the stable V5 command envelope. When a project enables artifacts.envelope, the project's .ptc/envelopes/<run_ref>.json ledger entry is written independently for that run. The explicit path is reserved before artifact admission, so concurrent commands naming the same file are rejected before provider activity. A missing caller-owned parent is rejected at admission; project-managed artifact directories are created before the reservation. Every failed envelope copy is reported on stderr with its destination, even when another copy succeeds. run, validate, doctor, models, init, and materialize all accept the flag; the document it publishes carries status, run reference, result or classified error, artifact state, and a closed warnings array. For run, an uncataloged installed model appears there as model_uncataloged with its provider alias and an adapter-attested public selector; the same warning is retained in run-started metadata. A failed plain doctor command also publishes locally derived model_uncataloged warnings for every affected provider check; successful non-run commands keep the array empty. Parse the envelope rather than scraping stdout, which is a human presentation channel that may also carry application output.

The command envelope reports the run reference and artifact class, not artifact paths. Output, trace, inspection, and envelope destinations must be distinct. All publications are no-replace and recheck their destination at commit time.

Reservations named .*.ptc-reservation record the owning process. When you reuse a destination after an interrupted run on the same host, a reservation whose owner is gone is reclaimed automatically if the destination is absent. Older reservations without an owner marker are reclaimed only after 60 seconds. Live owners, uncertain owner status, and existing destinations are always preserved. Cross-host recovery on network filesystems is not supported. A destination/destination_exists terminal error identifies the colliding switch and resolved path and gives a remedy; the command envelope remains path-free.

Atomic publication may reserve owner-only sibling paths named .ptc-private-* or .ptc-private-result-*. They normally disappear at commit or cleanup, but an abruptly terminated process can leave one behind. Because a completed reservation can contain private prompts, responses, source, or a result, ignore both patterns as well as the configured artifact root. New projects created by ptc init include all three patterns in .gitignore.

ptc init requires a DIRECTORY that does not already exist. It assembles the complete scaffold or selected example tree and publishes it atomically without replacing anything. To add PtcRunner to an existing repository, initialize a new sibling or subdirectory, then deliberately copy or move the generated files the repository wants.

ptc init DIRECTORY --example NAME publishes one of the walkthrough projects this executable embeds instead of the scaffold, under the same no-replace commit. Run ptc help init to see the embedded names. The trees are byte-identical to the repository's, so the commands the guides print work from wherever the copy was created rather than from one checkout directory.

For runs that produce a validated terminal event batch, execution.usage includes the required authoritative llm_budget, aggregate observational llm_spend, and llm_usage grouped by alias and installation revision, llm_usage_by_model grouped by an attested public resolved model, and unattributed_model_calls. llm_spend is byte-equivalent to the value in the canonical run-stopped usage: empty and incomplete contain only state, unpriced also contains non-negative input and output, available adds a fixed-point USD total_cost, and overflow contains only state. Only available can report a measured zero cost; the other states never substitute zero for absent pricing. Rows report call counts, usage-presence counts, and summed token and total_cost values plus required usage_overflow. Values saturate at 9_007_199_254_740_991; a true row flag means at least one value is only a lower bound, while any aggregate overflow makes llm_spend exactly {"state":"overflow"}. Terminal accounting pairs each llm-request capability-started with its capability-stopped by capability_id. An unmatched start is one observed call with unknown usage: calls increments, successful_calls does not, and missing_usage_calls increments. A matched error that may have dispatched likewise increments missing_usage_calls when no usage was observed and makes llm_spend incomplete. When that error retains valid provider-reported usage, the call instead increments usage_calls, contributes its tokens and cost, and can make llm_spend available even though successful_calls remains zero. Trusted not_dispatched errors are excluded because no provider request could have been billed. A row includes total_cost only when every call that could carry usage has valid priced usage; an unmatched or unpriced call leaves the aggregate cost unknown and omitted, not reported as zero, while measured input/output totals are retained. llm_usage_state: "available" means the terminal batch could be reconstructed, not that every call supplied usage. llm_usage_state: "unavailable" pairs all three aggregate fields with null when terminal evidence cannot be validated, including dropped capability-started or capability-stopped events, while preserving other known usage. Non-empty events_dropped for other event types means an available detailed summary covers retained evidence and may not be complete. llm_usage_state describes reconstruction of those detailed rows; it does not replace the independently sealed five-state llm_spend value.

llm_budget always has the exact outer keys total_tokens and cost; either is null when its optional host limit is disabled. An enabled token ledger reports state, limit, reserved, charged, remaining, and refused. The cost ledger uses the corresponding _microusd field names plus fixed currency: "USD". Terminal reserved values are zero after automatic cleanup. incomplete means an acknowledged call was conservatively full-charged without exact usage; overrun clamps remaining to zero and refuses future calls. Unlike the event-derived detail rows, this projection is sealed directly from RunState and remains authoritative when events are dropped.

Artifact publication currently requires a Unix host with POSIX-compatible mkdir and id; trace append also needs sh and either lockf or flock. Private artifacts and newly created traces require trusted ancestry, safe ownership, and restrictive modes. Preflight refuses unsafe or unwritable destinations before provider acquisition, while descriptor-based publication checks close later filesystem races.

--inspect is an explicit host development authority. It may contain prompts, model responses, generated source, capability arguments and results, MCP payloads, prints, and detailed failures. Do not publish it with normal traces.

Evaluate replacement component source

Use --component-override-descriptor to evaluate one already-selected component without installing it. A trusted build step creates the owner-only candidate and descriptor from model-authored source. The replay evaluation guide owns the workflow for holding model responses fixed and comparing a baseline with that candidate. The component reference defines every descriptor field. ptc materialize (and mix ptc.materialize from a source checkout) exports installed source with --source-out and publishes a gated candidate with --source/--out, as shown in Inspect and customize components.

Both ptc validate and ptc run accept the descriptor switch. Validation compiles and checks the replacement without provider acquisition; running uses the same verified candidate in the immutable workflow or mission bundle.

Keep the descriptor in any directory you own, under the application or outside it, and under any directory name. A name outside the portable logical-name grammar — a leading dot, an uppercase segment — restricts neither ptc materialize --out nor --component-override-descriptor.

Read results and failures

A successful normal run prints the compact JSON result value. A private run does not print its value. The V5 envelope records the result class, artifact states, bounded usage, retained-memory counts, and the closed diagnostic when one exists.

Capability failures normally enter PTC-Lisp as recoverable envelopes so the workflow can correct, retry, degrade, or fail. Parser, compiler, timeout, heap, source, result, quota, provider, and event failures retain bounded Kernel classifications.

One-shot public diagnostics come from a closed catalog. They never render an arbitrary exception, rejected value, provider response, credential, or private payload. A provider subject appears as provider/<alias>/<operation> with its workflow or mission occurrence when known.

Host, project, and application schema diagnostics distinguish a closed set of violated rules and carry only schema-authorized JSON Pointers. A missing required field may name that schema-declared field; an unknown caller-authored key is omitted and the pointer stops at its declared parent. Caller-selected map members such as installation aliases and mission names render as *.

validate also reads the files a declaration owns rather than the environment it will run in. A replay installation names a fixture file, so validate parses it under the installed ceilings and reports the rule a rejected file broke — with the line number for a line-level rejection. It still acquires nothing: an installed model's adapter and an MCP server's executable are environment dependencies and belong to doctor.

On success, the validate result includes mission_grants: for each named mission, the sorted parseable data/<name> grants, every public export ref, and selected mission provider names. This is the static grant declaration; validate does not acquire providers. It can nevertheless prove that a mission with no selected provider cannot receive a non-implicit capability, so it rejects that case during bundle validation. Capability tool names remain unresolved for missions selecting one or more providers, and validation does not infer them from provider aliases or provider dependency services. kernel/mission-inventory lists model-visible capabilities once a run or REPL session has built the frozen inventory.

Environment files fail before provider acquisition with a cause-specific code: environment_file_not_found, environment_file_not_regular, environment_file_unreadable, environment_file_too_large, or environment_file_invalid_utf8. The code identifies whether to create the named --env-file/project file, change its permissions, or repair its bytes; the public envelope still does not publish a host filesystem path.

When an agent turns a provider failure into workflow failure, the command retains one bounded class when the adapter can prove it: llm_authentication_failed, llm_payment_required, llm_rate_limited, llm_model_not_found, llm_tool_calling_unsupported, llm_request_invalid, llm_access_denied, llm_usage_unavailable, llm_timeout, llm_provider_unavailable, or the non-retryable fallback llm_provider_failed. No response body is retained. The failing model alias remains attributable through usage/provider evidence; run ptc doctor PROJECT --connect for a minimal provider check and use private inspection only when authorized detail is necessary.

Component compile failures with a provable location print the logical component name and the envelope's half-open byte range, for example at main.clj bytes [45,58). The same canonical offsets remain available in error.span when --envelope is requested. An unknown namespace is a separate closed diagnostic: the compiler carries the rejected namespace and canonical sorted namespace list as structured detail, and the command boundary rebuilds the public list and JSON hint after validating that detail. It never forwards the compiler-rendered string. For a shipped namespace such as kernel/, select its library and add the component dependency as described in Select a shipped prelude.

Use the standalone process contract

For machine integration, name an envelope file instead of parsing stdout:

ptc run ptc.json --envelope command-envelope.json

The standalone streams are human presentation channels and may also contain output from applications or children. The envelope is an atomic, no-replace file whose JSON Schema this executable serves as ptc docs schema-envelope (priv/schemas/ptc-command-envelope-v5.schema.json in the repository). Its status and exit-code relationship is sealed by the same command contract.

When a run fails with a known pre-sink cause, stdout also receives the V5 envelope as one JSON line, and stderr retains the human diagnostic. Project runs attempt to store that same envelope in the default ledger.

Causes also survive append-lock contention, named-document read failures, project and host schema worker unavailability, installation and provider admission, environment setup, and doctor service or owner failures. A schema worker's time or memory limit is resource_unavailable; an append lock that expires is lock_timeout. An unknown environment callback failure keeps internal_error with a closed cause. Causes never include filesystem paths, callback payloads, exception names, messages, or stack traces.

An unreadable project document also retains its read cause for models PROJECT and doctor PROJECT --connect, even before its host configuration can be derived. Invalid command switches still take precedence as argument errors.

The optional error.cause in a V5 envelope gives a closed, public reason for failures known before a trace or inspection sink begins writing. It contains no path, exception name, or provider response.

CauseMeaning
lock_timeoutA reservation or admission lock expired.
filesystem_errorA filesystem operation failed.
permissionThe operating system denied access.
unexpected_exceptionA bounded operation raised or returned an unknown failure.
subprocess_failedA subprocess failed before execution evidence began.
invalid_configurationA configuration value could not be used.
resource_unavailableA required local resource was unavailable.
destination_existsAn output destination was already occupied.

After arguments parse, an ordinary or caught shared-engine command outcome publishes one requested envelope. This includes a recognized run, validate, doctor, or models invocation whose named project fails schema validation: project diagnostics terminate before command bootstrap or project references are opened, but after the envelope destination is admitted. Malformed command syntax, conflicting arguments, invalid envelope destinations, and VM/OS termination can produce no envelope. Publication is no-replace, so a destination that already exists is refused during argument admission with arguments/envelope_destination_exists and exit 2, before any provider work: a repeated CI step is told to remove the file rather than paying for a run whose result it cannot receive. If envelope publication fails at every destination, the standalone command exits 74. If any copy succeeds, the command retains its original exit status and reports the failed destinations on stderr. Success exits 0; classified failures use their diagnostic catalog status; caught internal failures use 70.

run, validate, doctor, models, init, materialize, transcript, and version accept --envelope. repl, viewer, docs, and help do not. A private run envelope omits the result value. A normal run envelope also omits it when the settled result artifact state is not_requested; the successful value still goes to stdout. Installation, packaging, and container commands live in the installation documentation, not in this process-contract reference.

Branch on the exit status

An exit status is a class, not an identity: several diagnostics share one. runtime_limit_exceeded, run_timeout, turn_limit_exceeded, capability_quota_exceeded, and model_output_truncated all exit 6. Branch on the status to decide whether to retry, and read error.code from the envelope when the branch needs to know which failure it was.

A recoverable capability error does not change the exit status. Exhausting workflow_capability_calls_per_name returns {"status":"error","kind":"limit_exceeded","reason":"capability_quota","retryable?":false,"model":"primary","details":{"limit":"workflow_capability_calls_per_name","name":"llm-request","limit_value":2}} as a JSON boundary value; it is not an exact display of the value's PTC-Lisp types. The equivalent value inside the workflow uses keyword keys and keyword-valued :status, :kind, and :reason fields:

{:status :error
 :kind :limit_exceeded
 :reason :capability_quota
 :retryable? false
 :model "primary"
 :details {:limit :workflow_capability_calls_per_name
           :name "llm-request"
           :limit_value 2}}

An aggregate cost-budget refusal has the same field types and this copyable PTC-Lisp shape:

{:status :error
 :kind :limit_exceeded
 :reason :llm_cost_microusd
 :retryable? false
 :details {:limit :llm_cost_microusd
           :limit_value 1
           :remaining 1
           :requested 8585}}

PTC-Lisp equality is type-sensitive even though JSON projection renders a keyword value as its name string. Therefore (= "limit_exceeded" (get response :kind)) is false, while (= :limit_exceeded (get response :kind)) is true. Preserve the authenticated envelope when ending the run so the command retains its limit classification:

(let [response (tool/llm-request request)]
  (if (= :limit_exceeded (get response :kind))
    (fail response)
    response))

A workflow that reads past a recoverable error can still return and the command exits 0. Refusing an aggregate llm_total_tokens or llm_cost_microusd reservation is the same class of recoverable value, with reason naming the budget. Aborting that exact envelope reports execution/runtime_limit_exceeded (exit 6) and names the refused reservation; it is not workflow_failed. execution.usage.capability_refusals counts those errors from environment capability callbacks and the implicit runtime routes the Kernel grants with them (workflow/limit_exceeded/capability_quota, workflow/limit_exceeded/llm_total_tokens, workflow/limit_exceeded/llm_cost_microusd). Runner-added routes such as kernel-eval are not counted. At most 2 distinct classes are named; further classes increment $overflow. Assert capability_refusals is {} when a CI job requires that every counted capability call succeeded, or have the workflow fail, when a quota must end the run.

execution.usage.capability_denials is the sibling counter for a call the environment rejected before any callback ran: a name the workflow bundle or the mission grant does not carry. Refusals cannot count those, because there is no installed callback to observe. Its keys are the runtime's own rejection classes (unknown_tool), never the requested name — in a mission that name is written by the model. The unit is the evaluation the denial stopped rather than the call: a denial ends its evaluation, and one program's undefined names are reported together, so a workflow entry, a REPL form, or one mission turn each count at most once. At most 2 distinct classes are named; further classes increment $overflow. Assert capability_denials is {} when a CI job requires that an authority boundary held; the run still exits 0 when a later turn recovers, and the counter is what distinguishes a denial from a workflow that merely ran out of turns. The denied name stays in the private inspection record. A turn-limited or failed run also names the class in execution.last_evaluation_error as unknown_tool.

The tables below are generated from the diagnostic catalog the command dispatches on, so they list every status a command can exit with and every diagnostic behind it. A run ended by a signal has no status of its own. The shell reports 128 plus the signal number (130 for Ctrl-C and 143 for SIGTERM), and the run publishes nothing, not even a run id. artifacts.inspection: true is the only setting that leaves a partial private record, which no shipped command reads.

StatusMeaningPhases
0the command succeeded—
2the arguments were rejected before any document was readarguments
3a declaration document was unavailable, invalid, or rejectedproject, host, application, bundle, provider_declaration
4a selected provider could not be checked or acquiredlocal_preflight, active_preflight, provider_acquisition
5the workflow ran and failedexecution
6the run exceeded a limit or its durationexecution
7the run produced no usable artifact: a destination, result, or publication failuredestination, execution, result_cleanup, publication
70the command failed internallyinternal
74the requested envelope could not be published, so no envelope describes this failure—
78gateway startup or pin discovery was refusedgateway

Every classified diagnostic and the status it exits with:

From shipped CLI input, authorization_target_unknown and authorization_not_applicable are reachable only through source-checkout mix ptc run --authorize-mcp; runtime-included ptc rejects that switch. Embedding runtimes can supply authorization targets directly.

StatusPhaseCodeRetryableMessage
2argumentsconflicting_argumentsnochoose only one option from the conflicting argument group
2argumentsdocs_page_unknownnono documentation page is served under that name
2argumentsenvelope_destination_existsnothe envelope destination already exists
2argumentsexample_unknownnono example is embedded under that name
2argumentsinvalid_argumentsnouse the documented arguments for this command
2argumentsinvalid_commandnouse one of the supported commands
2argumentsproject_host_undeclarednothe project document declares no host block; add one to use this command
3applicationapplication_not_foundnothe application manifest does not exist
3applicationapplication_unavailablenothe application is unavailable
3applicationcontract_invalidnoan application value contract is invalid
3applicationcontract_projection_limit_exceedednoapplication contract prompt projections exceed their bounded admission limit
3applicationdeclared_read_effect_invalidnoexport example/read declares effect read but resolves to write; change the declaration or use only read capabilities
3applicationdocument_limit_exceedednothe application document closure exceeds its limit
3applicationduplicate_mission_providernoa mission selects the same provider more than once; list each provider at most once
3applicationduplicate_propertynoan application document contains a duplicate property
3applicationevent_identity_conflictnothe command event identity conflicts with the application
3applicationinput_contract_failednothe selected input does not satisfy the input contract
3applicationinput_invalidnothe selected input is not an admissible JSON object
3applicationinstalled_limit_exceedednoan application limit exceeds the installed ceiling; lower it or raise the host-configured ceiling
3applicationinvalid_jsonnoan application document is not valid JSON
3applicationlimit_capacity_invalidnoevent_payload_bytes effective limit 9092 is below the required 12000 bytes for this application's resolved terminal usage; raise limits.event_payload_bytes, and its installed host ceiling if it is lower, or declare fewer capabilities or missions
3applicationlimit_configuration_invalidnonormal_event_bytes effective limit 4000000 is below the required 12003450 bytes for event_payload_bytes 4000000; raise limits.normal_event_bytes, and its installed host ceiling if it is lower, or lower limits.event_payload_bytes
3applicationlimit_unavailablenoan optional application limit is unavailable because the host has not enabled it
3applicationoverride_invalidnothe component override is invalid
3applicationreference_missingnoa referenced document is unavailable; for --input/--private-input try an application-relative name or an absolute/working-directory path
3applicationrequired_property_missingnothe application manifest is missing a required property
3applicationschema_validation_unavailableyesapplication schema validation timed out or exceeded its resource bound; retry the command
3applicationschema_violationnothe application manifest does not satisfy its schema
3applicationunknown_mission_providernoa mission selects a provider that is not declared in providers.mission; declare it there before selecting it in the mission
3bundlebundle_invalidnothe component bundle is invalid
3bundlebundle_limit_exceedednothe component bundle exceeds a compile limit
3bundlecompile_failednothe component bundle could not be compiled
3bundleduplicate_definitionnothe component bundle defines the same name more than once
3bundleentry_invalidnothe workflow entry is not a public bundle export
3bundlemission_capability_ungrantednoa mission with no providers requires a capability not supplied implicitly
3bundlemission_undeclarednothe workflow entry evaluates into a mission and the manifest declares none
3bundlesyntax_invalidnothe component source is not valid PTC-Lisp
3bundleundefined_variablenothe component source contains an undefined variable reference
3bundleunknown_namespacenothe component source references an unavailable namespace
3hosthost_invalidnothe host configuration is invalid
3hosthost_schema_invalidnothe host configuration does not satisfy its schema
3hosthost_unavailablenothe host configuration is unavailable
3hostinstallation_endpoint_credentials_require_httpsnoconfigured MCP credentials require an https endpoint
3hostinstallation_endpoint_insecure_loopback_forbiddennoallow_insecure_loopback is not permitted on an https endpoint; remove it
3hostinstallation_endpoint_insecure_loopback_requirednoa plain-http MCP endpoint requires allow_insecure_loopback
3hostinstallation_endpoint_invalidnoan installed MCP endpoint is not admissible; streamable_http requires an https URL, or allow_insecure_loopback with a credential-free plain-http loopback address
3hostinstallation_endpoint_literal_loopback_requirednoallow_insecure_loopback requires a literal 127.0.0.1 or [::1] address
3hostinstallation_revision_missingnoan installed provider is missing its behavior revision
3hostinstalled_limit_invalidnoan installed limit is invalid
3hostschema_validation_unavailableyeshost schema validation timed out or exceeded its resource bound; retry the command
3projectproject_schema_invalidnothe project configuration does not satisfy its schema
3projectschema_validation_unavailableyesproject schema validation timed out or exceeded its resource bound; retry the command
3provider_declarationdata_policy_deniednothe selected providers do not admit the effective data class
3provider_declarationdependency_invalidnothe selected provider dependency graph is invalid
3provider_declarationplacement_deniednothe provider is not allowed in this destination
3provider_declarationprovider_unknownnothe selected provider is not installed
3provider_declarationselection_invalidnothe provider selection is invalid
3provider_declarationselection_unverifiablenothe provider selection cannot be verified declaratively
4active_preflightauthentication_rejectednoprovider authentication was rejected
4active_preflightauthorization_rejectednoexplicit provider authorization was rejected
4active_preflightauthorization_requirednoprovider authorization is required; runtime-included ptc cannot initiate authorization; source-checkout mix ptc run ... --authorize-mcp NAME can initiate it, and embedding hosts may provide authorization
4active_preflightauthorization_unavailableyesthe authorization service is temporarily unavailable
4active_preflightconnectivity_outcome_unknownnothe connectivity outcome could not be committed safely
4active_preflightconnectivity_protocol_errornothe provider returned an invalid connectivity response
4active_preflightconnectivity_rate_limitedyesthe provider connectivity operation is rate limited
4active_preflightconnectivity_rejectednothe provider rejected the connectivity operation
4active_preflightconnectivity_timeoutnothe connectivity operation exceeded its budget
4active_preflightconnectivity_unavailableyesthe provider connectivity operation is temporarily unavailable
4active_preflightconnectivity_unsupportednothe provider does not implement the declared connectivity check
4active_preflightcredential_unavailablenoa required provider credential is unavailable
4active_preflightprovider_application_unavailablenoa required provider application is unavailable
4active_preflightselection_rejectednothe provider rejected the normalized selection
4active_preflightselection_validation_failednoactive provider selection validation failed
4active_preflightselection_validation_timeoutnoactive provider selection validation timed out
4local_preflightadapter_unavailablenoa required provider adapter is unavailable
4local_preflightauthorization_not_applicableno--authorize-mcp applies only to an installation that declares OAuth
4local_preflightauthorization_target_unknownno--authorize-mcp must name an installed provider the application selects
4local_preflightcommand_not_foundnoa required provider command could not be found
4local_preflightenvironment_file_invalidnothe named environment file contains an invalid assignment
4local_preflightenvironment_file_invalid_utf8nothe named environment file is not valid UTF-8
4local_preflightenvironment_file_not_foundnothe named environment file does not exist
4local_preflightenvironment_file_not_regularnothe named environment file is not a regular file
4local_preflightenvironment_file_too_largenothe named environment file exceeds the 1 MB limit
4local_preflightenvironment_file_unreadablenothe named environment file cannot be read safely
4local_preflightenvironment_unavailablenoa required local environment is unavailable
4local_preflightexecutable_unavailablenoa required provider executable is unusable
4local_preflightfixtures_unreadablenoprovider fixtures could not be read
4local_preflightlauncher_unavailablenoa required provider launcher is unavailable
4local_preflightlocal_check_timeoutnoa local provider check timed out
4local_preflightmodel_contract_unsupportednothe selected model cannot honor its installed model contract
4local_preflightprovider_admission_unavailablenohosted provider admission is unavailable
4provider_acquisitioncapability_requirement_missingnoa component requires a capability that the selected providers did not supply
4provider_acquisitionprovider_acquisition_timeoutyesthe selected provider exceeded its acquisition timeout budget
4provider_acquisitionprovider_endpoint_connection_refusedyesthe installed endpoint refused the connection
4provider_acquisitionprovider_endpoint_name_unresolvednothe installed endpoint hostname could not be resolved
4provider_acquisitionprovider_endpoint_tls_failednothe installed endpoint did not complete a TLS handshake
4provider_acquisitionprovider_policy_changednothe selected provider policy changed during acquisition
4provider_acquisitionprovider_protocol_errornothe selected provider returned an invalid acquisition response
4provider_acquisitionprovider_protocol_version_unsupportednothe endpoint did not advertise support for MCP protocol 2026-07-28
4provider_acquisitionprovider_tool_missingnothe installed endpoint does not expose a declared tool
4provider_acquisitionprovider_unavailablenothe selected provider could not be acquired
5executionevaluation_failednothe evaluation failed
5executionexplicit_failurenothe workflow signalled an explicit failure
5executioninvalid_agent_confignoan agent configuration option is outside its supported range
5executionllm_access_deniednothe LLM provider denied access to the configured model
5executionllm_authentication_failednothe LLM provider rejected authentication; check the installed credential
5executionllm_model_not_foundnothe LLM provider could not find the configured model
5executionllm_payment_requirednothe LLM provider rejected the request for billing or credit reasons
5executionllm_provider_failednothe LLM provider request failed
5executionllm_provider_unavailableyesthe LLM provider is unavailable
5executionllm_rate_limitedyesthe LLM provider rate limited the request
5executionllm_request_invalidnothe LLM provider rejected the configured request
5executionllm_timeoutyesthe LLM provider request timed out
5executionllm_tool_calling_unsupportednothe configured model does not support tool calling
5executionllm_usage_unavailablenothe LLM provider did not return the promised usage or cost metadata
5executionmission_failednoa subordinate mission failed
5executionphase_return_contract_failednothe standalone agent return does not satisfy its named contract
5executionprovider_failednoa provider failed during execution
5executionreplay_fixture_missingnono replay fixture matches the workflow request
5executionworkflow_failednothe workflow failed
6executioncapability_quota_exceedednoa capability quota was exceeded
6executionmodel_output_truncatednomodel output was truncated before producing a usable agent action
6executionrun_timeoutnothe run duration limit was exceeded
6executionruntime_limit_exceedednoa runtime limit was exceeded
6executionturn_limit_exceedednothe agent turn limit was exceeded
7destinationdestination_existsnoan artifact destination already exists
7destinationenvelope_destination_unavailablenothe envelope destination is unavailable
7destinationinspection_destination_unavailablenothe inspection destination is unavailable
7destinationinspection_destination_unsafenothe inspection destination is unsafe
7destinationinspection_directory_missingno--inspect must name a file in an existing directory
7destinationinvalid_destinationnoan artifact destination is invalid
7destinationinvalid_inspection_destinationno--inspect must name a valid destination ending in .ptcins
7destinationinvalid_result_destinationnothe result destination is invalid
7destinationinvalid_trace_destinationnothe trace destination is invalid
7destinationprivate_destination_requirednothe run requires an authorized private destination
7destinationprune_delete_failednoa run artifact could not be deleted safely
7destinationprune_unavailablenothe project artifact directory is unavailable
7destinationprune_unsafe_directorynothe project artifact directory is unsafe
7destinationrecovery_reservation_failednothe private result recovery reservation failed
7destinationresult_destination_unavailablenothe result destination is unavailable
7destinationresult_destination_unsafenothe result destination is unsafe
7destinationresult_directory_missingno--output and --private-output must name a file in an existing directory
7destinationtrace_destination_unavailablenothe trace destination is unavailable
7destinationtrace_destination_unsafenothe trace destination is unsafe
7destinationtrace_directory_missingno--trace-dir must be an existing normal directory
7executionevent_capture_limit_exceedednothe trace event capture limit was exceeded
7executionevent_sink_unavailablenothe trace event sink is unavailable
7executioninspection_capture_limit_exceedednothe private inspection capture limit was exceeded
7executioninspection_sink_unavailablenothe private inspection sink is unavailable
7publicationcandidate_cleanup_failednoa refused candidate could not be discarded
7publicationcandidate_destination_existsnoptc materialize --out publishes only to a new directory; choose a path that does not already exist
7publicationcandidate_destination_parent_missingnothe --out parent directory does not exist; ptc materialize creates the candidate directory but never its parent
7publicationcandidate_destination_parent_unsafenothe --out parent directory is writable by other users or owned by another user
7publicationcandidate_publication_failednocandidate publication failed
7publicationcandidate_refusednothe candidate was refused by the promotion gate
7publicationcandidate_source_too_largenothe candidate source exceeds the 1 MiB publication bound
7publicationdescriptor_too_largenothe candidate descriptor exceeds its publication bound
7publicationdestination_collisionnoan artifact destination appeared before publication
7publicationinitialization_failednoproject initialization failed
7publicationinitialization_parent_missingnothe initialization target's parent directory does not exist
7publicationinitialization_parent_unusablenothe initialization target's parent directory is unusable
7publicationinitialization_target_existsnoptc init publishes only to a new directory; choose a target that does not already exist
7publicationinspection_publication_failednoinspection publication failed
7publicationinvalid_candidate_destinationnothe candidate destination is invalid
7publicationrecovery_cleanup_failednoprivate result recovery cleanup failed
7publicationresult_artifact_invalidnothe result artifact is not valid JSON
7publicationresult_artifact_too_largenothe result artifact exceeds the 1 MiB publication bound
7publicationresult_pointer_invalidnothe result pointer does not resolve to a string
7publicationresult_publication_failednoresult publication failed
7publicationselected_component_missingnothe selected component is not present in the chosen environment
7publicationsource_out_cleanup_failednosource export could not discard its staging directory
7publicationsource_out_destination_existsnoptc materialize --source-out publishes only to a new file; choose a path that does not already exist
7publicationsource_out_failednosource export failed
7publicationsource_out_parent_missingnothe --source-out parent directory does not exist; ptc materialize writes the file but never creates its parent
7publicationsource_out_parent_unsafenothe --source-out parent directory is writable by other users or owned by another user
7publicationsource_out_parent_unusablenothe --source-out parent directory is unusable
7publicationtrace_publication_failednotrace publication failed
7publicationunreadable_candidate_sourcenothe candidate source file could not be read
7publicationunreadable_result_artifactnothe result artifact could not be read
7result_cleanupprovider_cleanup_failednoprovider cleanup failed
7result_cleanupprovider_cleanup_timeoutnoprovider cleanup timed out
7result_cleanupresult_contract_failednothe workflow result does not satisfy its contract
7result_cleanupresult_invalidnothe workflow result is invalid
7result_cleanupresult_limit_exceedednothe workflow result exceeds its limit
70internalinternal_errornothe command failed internally

Profile frontend diagnostics

Profile commands exit 1 for frontend setup, source capture, selection, or evaluation refusals. Selection, immutable source-capture, and classified evaluation refusals carry a stable code from the table below in the terminal JSONL command-error record; stderr uses the same identity as repl/CODE. Other frontend setup and persistence failures retain repl/command_failed. Shared argument-parser refusals still use their existing arguments/CODE diagnostic and exit 2. Catalog messages never include a resource path, selected run reference, or private payload. Any internal reason classified at this boundary but outside the closed vocabulary becomes profile_setup_failed.

The table is generated from the catalog the REPL frontend dispatches on:

CodeRefusalPublic message
ambiguous_selected_traceBoth sanitized and private trace candidates exist.selected run has ambiguous trace candidates
catalog_limit_exceededCatalog entry, file, retained-memory, heap, or listing bounds were exceeded.private run catalog exceeded its capture limits
duplicate_inspection_runMore than one selected artifact claims the same inspection run identity.selected inspection set contains a duplicate run identity
duplicate_selected_runThe same run reference was supplied more than once.selected run set contains a duplicate reference
inspection_correlation_missingThe selected trace and sealed evidence do not prove one correlation.selected trace and inspection artifacts do not correlate
invalid_run_referenceA --run value is not a canonical PTC command run reference.selected run reference is invalid
malformed_sourceSelected metadata or sealed evidence failed structural validation.analysis source is malformed
profile_evaluation_failedA profile form failed for a reason outside the source-capture vocabulary.profile evaluation failed
profile_setup_failedAn unclassified internal setup failure was closed at the frontend boundary.private analysis profile setup failed
result_limit_exceededA catalog page or selected-analysis result could not fit its result bound.profile evaluation result exceeded its byte limit
selected_inspection_missingNo sealed inspection artifact exists for a selected run.selected inspection artifact is missing
selected_inspection_not_regularThe exact selected inspection candidate is not a regular file.selected inspection artifact is not a regular file
selected_run_mismatchEmbedded run or trace identity disagrees with the selected candidate.selected source identity does not match its run reference
selected_set_limit_exceededMore than sixteen run references were selected for one session.selected run set exceeds the 16-run limit
selected_trace_missingNo trace candidate exists for a selected run.selected trace is missing
selected_trace_not_regularThe exact selected trace candidate is not a regular file.selected trace is not a regular file
source_changedA selected source or cursor identity changed while it was being verified.analysis source changed during immutable capture
source_limit_exceededAggregate bytes, per-artifact records, index entries, or heap were exceeded.selected analysis source exceeded its admission limits
source_retained_limit_exceededThe immutable trace projection or inspection index retained too much memory.selected analysis source exceeded its retained-memory limit; for whole-directory private-run-analysis-v2 capture, select exact runs with --run RUN_ID
source_unavailableA source root became unavailable or its bounded capture deadline elapsed.analysis source is unavailable or capture timed out
unsupported_schemaA selected trace or inspection artifact uses an unsupported version.analysis source uses an unsupported schema version

Diagnose a failed run

The command reports a closed phase/code pair. If a workflow deliberately calls fail, its value is not copied into the command diagnostic. The Kernel API and canonical run-stopped event retain only a safe taxonomy:

  • a map whose kind is recognized retains that readable kind;
  • an authenticated agent.core turn-limit failure additionally retains the fixed agent_turns name, the effective integer ceiling from 1 through 128, and the closed reason the loop stopped (turn_limit_exceeded, intermediate_result, evaluation_error, or protocol_error);
  • an unknown map kind retains only a one-way fingerprint;
  • a string or other non-map retains no detail.

Prefer a framework classification such as:

(fail {:kind "assertion-failed" :detail "private explanation"})

The public evidence retains assertion-failed, not the private explanation. The Runner adds the fixed agent_turns fields only after the shipped agent's private runtime route has authenticated the exhaustion failure.

For exact authorized detail, capture inspection evidence:

ptc run ptc.json \
  --trace-dir traces \
  --inspect private/run.ptcins

Each inspection artifact includes the frozen component sources. It adds execution prints and any provider-backed private activity that occurred. A failure can add detailed execution-error evidence. A raised capability callback additionally records its bounded exception class, message, and formatted stacktrace while the trace retains only the closed provider_error / exception category. Failed mission evaluations retain their bounded evaluator diagnostic and available source offset under the exact generated-source evaluation identity, even when the workflow handles the failure and returns normally. Exception text and stacktrace paths can contain sensitive data and are not reliably redactable; read the artifact only through an authorized private sink.

To debug compilation with the manifest bundle, use manifest mode:

ptc repl -m ptc.json

-l dynamically evaluates setup code and does not accept component-only forms such as ns or defn-.

Use workflow REPL sessions

Start a scratch session or attach the manifest's frozen workflow environment:

ptc repl
ptc repl -e '(def x 40)' -e '(+ x 2)' -e '(+ *1 1)'
ptc repl --manifest ptc.json --host-config ptc-host.json
ptc repl --manifest ptc.json --trace traces/repl.jsonl

Successful definitions and three-value history persist for one session; failed forms preserve prior state. Manifest providers are acquired once and reused. Private manifest sessions require an attached terminal and --private-terminal. The Kernel REPL guide covers all modes, input forms, privacy gates, JSON Lines, bounds, and cleanup.

An interactive session on a terminal runs under the Erlang line editor, so the usual editing keys work — Ctrl+A/Ctrl+E, word motion, Ctrl+K/Ctrl+U and yank, arrow-key history, and Ctrl+R reverse search. Two keys behave differently there than in a plain terminal reader:

  • :quit leaves the session. Ctrl+D deletes the character under the cursor instead of ending input, because the editor binds it that way and offers no end-of-input binding to rebind it to.
  • Ctrl+C opens the BEAM break menu, as it does in iex. Press c to return to the prompt or a to abort the command.

A direct session keeps its submitted lines between runs, under ptc/repl-history in the user cache directory, so the previous session's expressions are one arrow key away. Disk history is enabled only when that directory is owned by the current user and its mode is verified as 0700 after setting permissions. If creation, permission setting, or verification fails, the session keeps only in-memory history. A manifest session can carry a private event policy, so it edits and recalls within the session but writes nothing to disk.

Analysis profile sessions (--profile) read under the source limit that mode enforces, which the line editor cannot drive. They keep the plain reader, and with it Ctrl+D as end of input; :quit works there too. Every non-interactive form — -e, a script argument, -, or redirected input — reads exactly as before.

Query traces

Traces contain bounded operational events, not prompts, model responses, capability payloads, or generated source. Query one immutable directory capture through the fixed public profile:

ptc repl \
  --profile run-analysis-v1 \
  --resource traces=traces \
  -e '(analysis/runs {})' \
  -e '(analysis/open "run-id")'

Public analysis supports runs, open, read, and counters; the public activity collection contains trace events. open advertises the private collections but they require a correlated inspection snapshot and private authority. analysis/runs defaults to a compact projection containing run ID, status, duration, LLM calls, evaluations, terminal reason, and completeness flags. call_counts_complete applies to llm_calls, decision_calls, and the workflow and mission capability counts in the full view: true means terminal usage supplied run totals; false means the values count retained events and may be low after retention loss. complete only reports whether the run has a terminal event. Pass {"view" "full"} when selecting by the complete metadata record:

(analysis/runs {"status" "error"})
(analysis/runs {"status" "error" "view" "full"})

When a project already declares its artifact root, reuse it instead of repeating resource paths:

ptc repl --project ptc-project.json \
  --profile run-analysis-v1 \
  -e '(analysis/runs {})'

The public profile derives traces; both private profiles derive traces and inspection when those artifact classes are enabled. An explicit --resource NAME=DIR overrides only that derived resource. Use private-run-catalog-v1 with (analysis/catalog QUERY) for bounded safe metadata discovery before opening private evidence; the complete query contract is in the Kernel REPL guide.

The complete discovery-to-selection command flow is two invocations. Replace the example references in the second command with run_id values from admissible rows in the first command's JSONL evaluation record:

ptc repl --profile private-run-catalog-v1 \
  --resource traces=.ptc/traces \
  --resource inspection=.ptc/inspection \
  --private-unattended --format jsonl \
  -e '(analysis/catalog {"state" "admissible" "limit" 20})'

ptc repl --profile private-run-analysis-v2 \
  --run cmd-00000000000000000000000001 \
  --run cmd-00000000000000000000000002 \
  --resource traces=.ptc/traces \
  --resource inspection=.ptc/inspection \
  --private-unattended --format jsonl \
  -e '(analysis/runs {})'

Each selected session accepts one through sixteen distinct canonical command run references. It re-verifies the exact candidates and carries no catalog cursor, digest, or dynamic source-acquisition authority. Start another command with a later batch rather than trying to add runs to an open session.

The TraceLog and run-analysis reference defines event schemas, sanitization, filtering, pagination, and source classes. The Kernel REPL guide covers longer investigations.

Inspect a private model conversation

Capture canonical and inspection artifacts in separate trusted locations. The credential-free Kernel inspection lab creates a correlated pair without a live model.

For one transcript, avoid a REPL:

mkdir -p tmp/transcript
ptc transcript RUN_ID \
  --traces tmp/traces \
  --inspection tmp/inspection \
  --private-unattended \
  --private-output tmp/transcript/conversation.private.json \
  --envelope tmp/transcript/command-envelope.json

When the producer uses an explicitly named inspection file, pass that exact file back to the reader instead of renaming it to the run reference. Keep the trace, inspection, and transcript output in separate sibling directories:

mkdir -p tmp/traces tmp/inspection tmp/transcript
ptc run application.json \
  --trace-dir tmp/traces \
  --inspect tmp/inspection/compile.ptcins \
  --envelope tmp/envelope.json
ptc transcript RUN_ID \
  --traces tmp/traces \
  --inspection-file tmp/inspection/compile.ptcins \
  --private-unattended \
  --private-output tmp/transcript/conversation.private.json

Read RUN_ID from the command envelope. Use exactly one inspection selector: --inspection DIRECTORY selects DIRECTORY/RUN_ID.ptcins, while --inspection-file FILE.ptcins opens only that explicit file.

On success the command prints one JSON line containing command, the selected run under run_ref, the absolute output path, and the number of published turns. The optional envelope receives the same result in the V5 command contract. The frontend-owned transcript command publishes this success envelope only after its private document is complete; a transcript refusal retains its existing transcript/ diagnostic and publishes neither file.

Transcript document schema version 2 scopes conversation.complete? with the top-level complete_scope: "model_conversation". Its top-level not_included list names evidence outside that scope: prelude_sources, capability_schemas, and the run result. The list comes from the turns collection catalog entry; the transcript does not include those surfaces.

Each selected model turn carries the provider-neutral request_hash used by llm_replay fixtures. The underlying owner-only inspection artifact carries the same value as payload.request_hash on the llm-request capability-input record. A private replay miss keeps that hash out of its public message while retaining the replay_fixture_missing code; copy the transcript field into the fixture and rerun.

The command reserves an owner-only destination before capture. RUN_ID must be a canonical PTC command run reference (cmd- followed by 26 Crockford characters). Capture then opens only these exact candidates, without listing either directory:

TRACE_DIRECTORY/RUN_ID.jsonl
TRACE_DIRECTORY/RUN_ID.private.jsonl
INSPECTION_DIRECTORY/RUN_ID.ptcins

Exactly one of the two trace candidates must exist as a regular file; both present is an ambiguous selected source. The inspection candidate, whether selected by directory or explicit path, must exist as a regular file. Filenames are routing hints: embedded run and trace identities remain authoritative, and unrelated directory members are not listed, opened, sized, decoded, or counted toward directory or aggregate source limits. The selected files still keep their individual source, record, retained-memory, heap, deadline, and result ceilings. Whole-directory snapshots used by private-run-analysis-v2 stay a distinct source variant: they admit only filename-bound one-run files, isolate a stable damaged connected component, and keep disjoint healthy runs queryable. Namespace or selected-file mutation still rejects the whole capture.

The parent of --private-output must already exist and be reached without a symbolic link — on macOS /tmp is a symlink, so /tmp/out.json is refused. Trace, inspection, and output directories must be pairwise physically separate: no directory may equal, contain, or be contained by either of the others. A file in the current directory fails when that directory contains --traces; create a sibling directory instead, as above. A rejection names the two conflicting switches and their physical relationship, and discloses no path. A noncanonical or traversal-shaped RUN_ID, a missing or non-regular selected candidate, a selected identity or correlation mismatch, and ambiguous, incomplete, changed, unsupported, or oversized selected evidence fail without a partial output.

Use private-run-analysis-v2 when you need several correlated questions or custom PTC-Lisp analysis. Its results can include exact messages, generated source, effective components, capability payloads, prints, diagnostics, and terminal values. The attached-terminal and unattended switches are accident guards, not access control; treat every downstream sink as private.

To walk the same capture from an ordinary application rather than a session, Debug a failed run installs it as a snapshot provider and follows typed evidence links with the shipped debug.nav prelude.

Browse traces in the Viewer

ptc viewer PROJECT.json [--port PORT] [--listen ADDRESS] [--env-file FILE]

The Viewer opens captured traces and watches or launches live runs for one project. See the Viewer reference for its display, startup, exposure, authentication, and live-reporting contract.

Environment file syntax and scope

--env-file reads one snapshot per command and restores its declared variables after the command exits. Blank lines and # comment lines are ignored. Each other line must be KEY=VALUE, with keys matching [A-Za-z_][A-Za-z0-9_]*. Whitespace around keys and values is trimmed. A quoted value must have matching single or double quotes; exactly one pair is removed. There is no export prefix, interpolation, escaping, or inline comment syntax. Malformed assignments and NUL bytes reject the entire file before any environment changes.

File values override inherited values. Overrides of PATH, HOME, LD_*, or DYLD_* are permitted with a warning on standard error because they can affect child processes. The warning never includes values. Only use trusted files.

Test a workflow

Use deterministic fixtures for normal tests. Assert the business value and semantic error classification, not timestamps, run references, or remaining milliseconds.

One shell-level check can use the stable envelope:

ptc init kernel-tutorial --example kernel-tutorial
artifact_dir="$(mktemp -d)"
envelope="$artifact_dir/command-envelope.json"
result="$artifact_dir/result.json"
ptc run kernel-tutorial/01-orders/ptc.json \
  --output "$result" \
  --envelope "$envelope"
actual="$(jq -c '.result.value' "$envelope")"
test "$actual" = \
  '{"order_count":3,"paid_count":2,"paid_total":335.75,"pending_ids":["A-101"]}'

Test scripted model responses before a small live-provider boundary. The replay evaluation guide shows the deterministic path; the Quickstart keeps one deliberately small live check.

Next steps

Gateway serving and pin discovery

ptc gateway GATEWAY.json [--env-file FILE] serves the configured loopback MCP endpoint. ptc gateway GATEWAY.json --print-pins [--env-file FILE] prints one JSON object keyed by tool name with the three startup pin fields, without executing workflows or starting a listener. Missing or stale pins are accepted only for discovery. See Gateway configuration for credential, validation, cleanup, and artifact behavior.

Gateway success exits 0; startup or discovery refusal exits 78 with one closed JSON error on stderr and no stdout. An uncertain serving shutdown exits 70. Serving emits no stdout; discovery emits output only after all tools succeed.