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
| Command | Purpose |
|---|---|
ptc init DIRECTORY | Publish a validated minimal application without replacing an existing target |
ptc init DIRECTORY --example NAME | Publish one embedded example tree instead of the scaffold |
ptc docs [PAGE] or --search TERM | List 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 PROJECT | Load and compile without executing the workflow, and read the input files the declarations name |
ptc run MANIFEST or PROJECT | Execute the application entry |
ptc prune PROJECT.json | Remove old project run artifacts by age and size |
ptc run MANIFEST --env-file FILE | Load 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.json | List public installed model-alias declarations, each with the safe selector it configured |
ptc catalog PROVIDER --host-config HOST.json | Acquire 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 repl | Open a direct, manifest-backed, or analysis session |
ptc viewer PROJECT.json | Browse a project's captured traces in a local web UI |
ptc viewer PROJECT.json --env-file FILE | Use one exact dotenv file for Viewer-started workflows and missions |
ptc gateway GATEWAY.json | Serve configured tools over loopback MCP |
ptc gateway GATEWAY.json --print-pins | Discover every tool's startup pins without serving |
ptc materialize MANIFEST or PROJECT | Export 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 --connectReadiness 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-manifestPages 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.jsonThe 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 reviewThe 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.jsonUseful run switches are:
--progresswrites 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 alongsidePTC_VIEWER_URL.--input INPUT.jsonreplaces 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-descriptorhost paths).--private-input INPUT.jsondoes the same and classifies the run as private.--output VALUE.jsonpublishes a normal result value without replacing an existing file.--private-output VALUE.jsonpublishes a private result at owner-only mode and keeps it off stdout.--trace-dir DIRwrites<run_ref>.jsonlor<run_ref>.private.jsonlaccording to the run's artifact class.--inspect FILEwrites sensitive execution evidence to an owner-only.ptcinsfile.--envelope FILEatomically publishes a convenience copy of the stable V5 command envelope. When a project enablesartifacts.envelope, the project's.ptc/envelopes/<run_ref>.jsonledger 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, andmaterializeall accept the flag; the document it publishes carries status, run reference, result or classified error, artifact state, and a closedwarningsarray. Forrun, an uncataloged installed model appears there asmodel_uncatalogedwith its provider alias and an adapter-attested public selector; the same warning is retained inrun-startedmetadata. A failed plaindoctorcommand also publishes locally derivedmodel_uncatalogedwarnings 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.jsonThe 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.
| Cause | Meaning |
|---|---|
lock_timeout | A reservation or admission lock expired. |
filesystem_error | A filesystem operation failed. |
permission | The operating system denied access. |
unexpected_exception | A bounded operation raised or returned an unknown failure. |
subprocess_failed | A subprocess failed before execution evidence began. |
invalid_configuration | A configuration value could not be used. |
resource_unavailable | A required local resource was unavailable. |
destination_exists | An 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.
| Status | Meaning | Phases |
|---|---|---|
| 0 | the command succeeded | — |
| 2 | the arguments were rejected before any document was read | arguments |
| 3 | a declaration document was unavailable, invalid, or rejected | project, host, application, bundle, provider_declaration |
| 4 | a selected provider could not be checked or acquired | local_preflight, active_preflight, provider_acquisition |
| 5 | the workflow ran and failed | execution |
| 6 | the run exceeded a limit or its duration | execution |
| 7 | the run produced no usable artifact: a destination, result, or publication failure | destination, execution, result_cleanup, publication |
| 70 | the command failed internally | internal |
| 74 | the requested envelope could not be published, so no envelope describes this failure | — |
| 78 | gateway startup or pin discovery was refused | gateway |
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.
| Status | Phase | Code | Retryable | Message |
|---|---|---|---|---|
| 2 | arguments | conflicting_arguments | no | choose only one option from the conflicting argument group |
| 2 | arguments | docs_page_unknown | no | no documentation page is served under that name |
| 2 | arguments | envelope_destination_exists | no | the envelope destination already exists |
| 2 | arguments | example_unknown | no | no example is embedded under that name |
| 2 | arguments | invalid_arguments | no | use the documented arguments for this command |
| 2 | arguments | invalid_command | no | use one of the supported commands |
| 2 | arguments | project_host_undeclared | no | the project document declares no host block; add one to use this command |
| 3 | application | application_not_found | no | the application manifest does not exist |
| 3 | application | application_unavailable | no | the application is unavailable |
| 3 | application | contract_invalid | no | an application value contract is invalid |
| 3 | application | contract_projection_limit_exceeded | no | application contract prompt projections exceed their bounded admission limit |
| 3 | application | declared_read_effect_invalid | no | export example/read declares effect read but resolves to write; change the declaration or use only read capabilities |
| 3 | application | document_limit_exceeded | no | the application document closure exceeds its limit |
| 3 | application | duplicate_mission_provider | no | a mission selects the same provider more than once; list each provider at most once |
| 3 | application | duplicate_property | no | an application document contains a duplicate property |
| 3 | application | event_identity_conflict | no | the command event identity conflicts with the application |
| 3 | application | input_contract_failed | no | the selected input does not satisfy the input contract |
| 3 | application | input_invalid | no | the selected input is not an admissible JSON object |
| 3 | application | installed_limit_exceeded | no | an application limit exceeds the installed ceiling; lower it or raise the host-configured ceiling |
| 3 | application | invalid_json | no | an application document is not valid JSON |
| 3 | application | limit_capacity_invalid | no | event_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 |
| 3 | application | limit_configuration_invalid | no | normal_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 |
| 3 | application | limit_unavailable | no | an optional application limit is unavailable because the host has not enabled it |
| 3 | application | override_invalid | no | the component override is invalid |
| 3 | application | reference_missing | no | a referenced document is unavailable; for --input/--private-input try an application-relative name or an absolute/working-directory path |
| 3 | application | required_property_missing | no | the application manifest is missing a required property |
| 3 | application | schema_validation_unavailable | yes | application schema validation timed out or exceeded its resource bound; retry the command |
| 3 | application | schema_violation | no | the application manifest does not satisfy its schema |
| 3 | application | unknown_mission_provider | no | a mission selects a provider that is not declared in providers.mission; declare it there before selecting it in the mission |
| 3 | bundle | bundle_invalid | no | the component bundle is invalid |
| 3 | bundle | bundle_limit_exceeded | no | the component bundle exceeds a compile limit |
| 3 | bundle | compile_failed | no | the component bundle could not be compiled |
| 3 | bundle | duplicate_definition | no | the component bundle defines the same name more than once |
| 3 | bundle | entry_invalid | no | the workflow entry is not a public bundle export |
| 3 | bundle | mission_capability_ungranted | no | a mission with no providers requires a capability not supplied implicitly |
| 3 | bundle | mission_undeclared | no | the workflow entry evaluates into a mission and the manifest declares none |
| 3 | bundle | syntax_invalid | no | the component source is not valid PTC-Lisp |
| 3 | bundle | undefined_variable | no | the component source contains an undefined variable reference |
| 3 | bundle | unknown_namespace | no | the component source references an unavailable namespace |
| 3 | host | host_invalid | no | the host configuration is invalid |
| 3 | host | host_schema_invalid | no | the host configuration does not satisfy its schema |
| 3 | host | host_unavailable | no | the host configuration is unavailable |
| 3 | host | installation_endpoint_credentials_require_https | no | configured MCP credentials require an https endpoint |
| 3 | host | installation_endpoint_insecure_loopback_forbidden | no | allow_insecure_loopback is not permitted on an https endpoint; remove it |
| 3 | host | installation_endpoint_insecure_loopback_required | no | a plain-http MCP endpoint requires allow_insecure_loopback |
| 3 | host | installation_endpoint_invalid | no | an installed MCP endpoint is not admissible; streamable_http requires an https URL, or allow_insecure_loopback with a credential-free plain-http loopback address |
| 3 | host | installation_endpoint_literal_loopback_required | no | allow_insecure_loopback requires a literal 127.0.0.1 or [::1] address |
| 3 | host | installation_revision_missing | no | an installed provider is missing its behavior revision |
| 3 | host | installed_limit_invalid | no | an installed limit is invalid |
| 3 | host | schema_validation_unavailable | yes | host schema validation timed out or exceeded its resource bound; retry the command |
| 3 | project | project_schema_invalid | no | the project configuration does not satisfy its schema |
| 3 | project | schema_validation_unavailable | yes | project schema validation timed out or exceeded its resource bound; retry the command |
| 3 | provider_declaration | data_policy_denied | no | the selected providers do not admit the effective data class |
| 3 | provider_declaration | dependency_invalid | no | the selected provider dependency graph is invalid |
| 3 | provider_declaration | placement_denied | no | the provider is not allowed in this destination |
| 3 | provider_declaration | provider_unknown | no | the selected provider is not installed |
| 3 | provider_declaration | selection_invalid | no | the provider selection is invalid |
| 3 | provider_declaration | selection_unverifiable | no | the provider selection cannot be verified declaratively |
| 4 | active_preflight | authentication_rejected | no | provider authentication was rejected |
| 4 | active_preflight | authorization_rejected | no | explicit provider authorization was rejected |
| 4 | active_preflight | authorization_required | no | provider 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 |
| 4 | active_preflight | authorization_unavailable | yes | the authorization service is temporarily unavailable |
| 4 | active_preflight | connectivity_outcome_unknown | no | the connectivity outcome could not be committed safely |
| 4 | active_preflight | connectivity_protocol_error | no | the provider returned an invalid connectivity response |
| 4 | active_preflight | connectivity_rate_limited | yes | the provider connectivity operation is rate limited |
| 4 | active_preflight | connectivity_rejected | no | the provider rejected the connectivity operation |
| 4 | active_preflight | connectivity_timeout | no | the connectivity operation exceeded its budget |
| 4 | active_preflight | connectivity_unavailable | yes | the provider connectivity operation is temporarily unavailable |
| 4 | active_preflight | connectivity_unsupported | no | the provider does not implement the declared connectivity check |
| 4 | active_preflight | credential_unavailable | no | a required provider credential is unavailable |
| 4 | active_preflight | provider_application_unavailable | no | a required provider application is unavailable |
| 4 | active_preflight | selection_rejected | no | the provider rejected the normalized selection |
| 4 | active_preflight | selection_validation_failed | no | active provider selection validation failed |
| 4 | active_preflight | selection_validation_timeout | no | active provider selection validation timed out |
| 4 | local_preflight | adapter_unavailable | no | a required provider adapter is unavailable |
| 4 | local_preflight | authorization_not_applicable | no | --authorize-mcp applies only to an installation that declares OAuth |
| 4 | local_preflight | authorization_target_unknown | no | --authorize-mcp must name an installed provider the application selects |
| 4 | local_preflight | command_not_found | no | a required provider command could not be found |
| 4 | local_preflight | environment_file_invalid | no | the named environment file contains an invalid assignment |
| 4 | local_preflight | environment_file_invalid_utf8 | no | the named environment file is not valid UTF-8 |
| 4 | local_preflight | environment_file_not_found | no | the named environment file does not exist |
| 4 | local_preflight | environment_file_not_regular | no | the named environment file is not a regular file |
| 4 | local_preflight | environment_file_too_large | no | the named environment file exceeds the 1 MB limit |
| 4 | local_preflight | environment_file_unreadable | no | the named environment file cannot be read safely |
| 4 | local_preflight | environment_unavailable | no | a required local environment is unavailable |
| 4 | local_preflight | executable_unavailable | no | a required provider executable is unusable |
| 4 | local_preflight | fixtures_unreadable | no | provider fixtures could not be read |
| 4 | local_preflight | launcher_unavailable | no | a required provider launcher is unavailable |
| 4 | local_preflight | local_check_timeout | no | a local provider check timed out |
| 4 | local_preflight | model_contract_unsupported | no | the selected model cannot honor its installed model contract |
| 4 | local_preflight | provider_admission_unavailable | no | hosted provider admission is unavailable |
| 4 | provider_acquisition | capability_requirement_missing | no | a component requires a capability that the selected providers did not supply |
| 4 | provider_acquisition | provider_acquisition_timeout | yes | the selected provider exceeded its acquisition timeout budget |
| 4 | provider_acquisition | provider_endpoint_connection_refused | yes | the installed endpoint refused the connection |
| 4 | provider_acquisition | provider_endpoint_name_unresolved | no | the installed endpoint hostname could not be resolved |
| 4 | provider_acquisition | provider_endpoint_tls_failed | no | the installed endpoint did not complete a TLS handshake |
| 4 | provider_acquisition | provider_policy_changed | no | the selected provider policy changed during acquisition |
| 4 | provider_acquisition | provider_protocol_error | no | the selected provider returned an invalid acquisition response |
| 4 | provider_acquisition | provider_protocol_version_unsupported | no | the endpoint did not advertise support for MCP protocol 2026-07-28 |
| 4 | provider_acquisition | provider_tool_missing | no | the installed endpoint does not expose a declared tool |
| 4 | provider_acquisition | provider_unavailable | no | the selected provider could not be acquired |
| 5 | execution | evaluation_failed | no | the evaluation failed |
| 5 | execution | explicit_failure | no | the workflow signalled an explicit failure |
| 5 | execution | invalid_agent_config | no | an agent configuration option is outside its supported range |
| 5 | execution | llm_access_denied | no | the LLM provider denied access to the configured model |
| 5 | execution | llm_authentication_failed | no | the LLM provider rejected authentication; check the installed credential |
| 5 | execution | llm_model_not_found | no | the LLM provider could not find the configured model |
| 5 | execution | llm_payment_required | no | the LLM provider rejected the request for billing or credit reasons |
| 5 | execution | llm_provider_failed | no | the LLM provider request failed |
| 5 | execution | llm_provider_unavailable | yes | the LLM provider is unavailable |
| 5 | execution | llm_rate_limited | yes | the LLM provider rate limited the request |
| 5 | execution | llm_request_invalid | no | the LLM provider rejected the configured request |
| 5 | execution | llm_timeout | yes | the LLM provider request timed out |
| 5 | execution | llm_tool_calling_unsupported | no | the configured model does not support tool calling |
| 5 | execution | llm_usage_unavailable | no | the LLM provider did not return the promised usage or cost metadata |
| 5 | execution | mission_failed | no | a subordinate mission failed |
| 5 | execution | phase_return_contract_failed | no | the standalone agent return does not satisfy its named contract |
| 5 | execution | provider_failed | no | a provider failed during execution |
| 5 | execution | replay_fixture_missing | no | no replay fixture matches the workflow request |
| 5 | execution | workflow_failed | no | the workflow failed |
| 6 | execution | capability_quota_exceeded | no | a capability quota was exceeded |
| 6 | execution | model_output_truncated | no | model output was truncated before producing a usable agent action |
| 6 | execution | run_timeout | no | the run duration limit was exceeded |
| 6 | execution | runtime_limit_exceeded | no | a runtime limit was exceeded |
| 6 | execution | turn_limit_exceeded | no | the agent turn limit was exceeded |
| 7 | destination | destination_exists | no | an artifact destination already exists |
| 7 | destination | envelope_destination_unavailable | no | the envelope destination is unavailable |
| 7 | destination | inspection_destination_unavailable | no | the inspection destination is unavailable |
| 7 | destination | inspection_destination_unsafe | no | the inspection destination is unsafe |
| 7 | destination | inspection_directory_missing | no | --inspect must name a file in an existing directory |
| 7 | destination | invalid_destination | no | an artifact destination is invalid |
| 7 | destination | invalid_inspection_destination | no | --inspect must name a valid destination ending in .ptcins |
| 7 | destination | invalid_result_destination | no | the result destination is invalid |
| 7 | destination | invalid_trace_destination | no | the trace destination is invalid |
| 7 | destination | private_destination_required | no | the run requires an authorized private destination |
| 7 | destination | prune_delete_failed | no | a run artifact could not be deleted safely |
| 7 | destination | prune_unavailable | no | the project artifact directory is unavailable |
| 7 | destination | prune_unsafe_directory | no | the project artifact directory is unsafe |
| 7 | destination | recovery_reservation_failed | no | the private result recovery reservation failed |
| 7 | destination | result_destination_unavailable | no | the result destination is unavailable |
| 7 | destination | result_destination_unsafe | no | the result destination is unsafe |
| 7 | destination | result_directory_missing | no | --output and --private-output must name a file in an existing directory |
| 7 | destination | trace_destination_unavailable | no | the trace destination is unavailable |
| 7 | destination | trace_destination_unsafe | no | the trace destination is unsafe |
| 7 | destination | trace_directory_missing | no | --trace-dir must be an existing normal directory |
| 7 | execution | event_capture_limit_exceeded | no | the trace event capture limit was exceeded |
| 7 | execution | event_sink_unavailable | no | the trace event sink is unavailable |
| 7 | execution | inspection_capture_limit_exceeded | no | the private inspection capture limit was exceeded |
| 7 | execution | inspection_sink_unavailable | no | the private inspection sink is unavailable |
| 7 | publication | candidate_cleanup_failed | no | a refused candidate could not be discarded |
| 7 | publication | candidate_destination_exists | no | ptc materialize --out publishes only to a new directory; choose a path that does not already exist |
| 7 | publication | candidate_destination_parent_missing | no | the --out parent directory does not exist; ptc materialize creates the candidate directory but never its parent |
| 7 | publication | candidate_destination_parent_unsafe | no | the --out parent directory is writable by other users or owned by another user |
| 7 | publication | candidate_publication_failed | no | candidate publication failed |
| 7 | publication | candidate_refused | no | the candidate was refused by the promotion gate |
| 7 | publication | candidate_source_too_large | no | the candidate source exceeds the 1 MiB publication bound |
| 7 | publication | descriptor_too_large | no | the candidate descriptor exceeds its publication bound |
| 7 | publication | destination_collision | no | an artifact destination appeared before publication |
| 7 | publication | initialization_failed | no | project initialization failed |
| 7 | publication | initialization_parent_missing | no | the initialization target's parent directory does not exist |
| 7 | publication | initialization_parent_unusable | no | the initialization target's parent directory is unusable |
| 7 | publication | initialization_target_exists | no | ptc init publishes only to a new directory; choose a target that does not already exist |
| 7 | publication | inspection_publication_failed | no | inspection publication failed |
| 7 | publication | invalid_candidate_destination | no | the candidate destination is invalid |
| 7 | publication | recovery_cleanup_failed | no | private result recovery cleanup failed |
| 7 | publication | result_artifact_invalid | no | the result artifact is not valid JSON |
| 7 | publication | result_artifact_too_large | no | the result artifact exceeds the 1 MiB publication bound |
| 7 | publication | result_pointer_invalid | no | the result pointer does not resolve to a string |
| 7 | publication | result_publication_failed | no | result publication failed |
| 7 | publication | selected_component_missing | no | the selected component is not present in the chosen environment |
| 7 | publication | source_out_cleanup_failed | no | source export could not discard its staging directory |
| 7 | publication | source_out_destination_exists | no | ptc materialize --source-out publishes only to a new file; choose a path that does not already exist |
| 7 | publication | source_out_failed | no | source export failed |
| 7 | publication | source_out_parent_missing | no | the --source-out parent directory does not exist; ptc materialize writes the file but never creates its parent |
| 7 | publication | source_out_parent_unsafe | no | the --source-out parent directory is writable by other users or owned by another user |
| 7 | publication | source_out_parent_unusable | no | the --source-out parent directory is unusable |
| 7 | publication | trace_publication_failed | no | trace publication failed |
| 7 | publication | unreadable_candidate_source | no | the candidate source file could not be read |
| 7 | publication | unreadable_result_artifact | no | the result artifact could not be read |
| 7 | result_cleanup | provider_cleanup_failed | no | provider cleanup failed |
| 7 | result_cleanup | provider_cleanup_timeout | no | provider cleanup timed out |
| 7 | result_cleanup | result_contract_failed | no | the workflow result does not satisfy its contract |
| 7 | result_cleanup | result_invalid | no | the workflow result is invalid |
| 7 | result_cleanup | result_limit_exceeded | no | the workflow result exceeds its limit |
| 70 | internal | internal_error | no | the 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:
| Code | Refusal | Public message |
|---|---|---|
ambiguous_selected_trace | Both sanitized and private trace candidates exist. | selected run has ambiguous trace candidates |
catalog_limit_exceeded | Catalog entry, file, retained-memory, heap, or listing bounds were exceeded. | private run catalog exceeded its capture limits |
duplicate_inspection_run | More than one selected artifact claims the same inspection run identity. | selected inspection set contains a duplicate run identity |
duplicate_selected_run | The same run reference was supplied more than once. | selected run set contains a duplicate reference |
inspection_correlation_missing | The selected trace and sealed evidence do not prove one correlation. | selected trace and inspection artifacts do not correlate |
invalid_run_reference | A --run value is not a canonical PTC command run reference. | selected run reference is invalid |
malformed_source | Selected metadata or sealed evidence failed structural validation. | analysis source is malformed |
profile_evaluation_failed | A profile form failed for a reason outside the source-capture vocabulary. | profile evaluation failed |
profile_setup_failed | An unclassified internal setup failure was closed at the frontend boundary. | private analysis profile setup failed |
result_limit_exceeded | A catalog page or selected-analysis result could not fit its result bound. | profile evaluation result exceeded its byte limit |
selected_inspection_missing | No sealed inspection artifact exists for a selected run. | selected inspection artifact is missing |
selected_inspection_not_regular | The exact selected inspection candidate is not a regular file. | selected inspection artifact is not a regular file |
selected_run_mismatch | Embedded run or trace identity disagrees with the selected candidate. | selected source identity does not match its run reference |
selected_set_limit_exceeded | More than sixteen run references were selected for one session. | selected run set exceeds the 16-run limit |
selected_trace_missing | No trace candidate exists for a selected run. | selected trace is missing |
selected_trace_not_regular | The exact selected trace candidate is not a regular file. | selected trace is not a regular file |
source_changed | A selected source or cursor identity changed while it was being verified. | analysis source changed during immutable capture |
source_limit_exceeded | Aggregate bytes, per-artifact records, index entries, or heap were exceeded. | selected analysis source exceeded its admission limits |
source_retained_limit_exceeded | The 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_unavailable | A source root became unavailable or its bounded capture deadline elapsed. | analysis source is unavailable or capture timed out |
unsupported_schema | A 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
kindis recognized retains that readable kind; - an authenticated
agent.coreturn-limit failure additionally retains the fixedagent_turnsname, the effective integer ceiling from 1 through 128, and the closed reason the loop stopped (turn_limit_exceeded,intermediate_result,evaluation_error, orprotocol_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.ptcinsEach 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.jsonlSuccessful 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:
:quitleaves the session.Ctrl+Ddeletes 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+Copens the BEAM break menu, as it does iniex. Presscto return to the prompt orato 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.jsonWhen 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.jsonRead 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.ptcinsExactly 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
- Kernel REPL covers session and analysis modes.
- Manifests and capabilities defines the application boundary these commands enforce.
- Host configuration defines provider installation.
- Building agents explains the agent policy producing the runs.
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.