--json. Under that flag the command prints exactly one JSON object on
stdout, prints nothing else on stdout, turns colour off and sends every log line to stderr. An error
prints an object too, so a caller that reads stdout always has something to parse.
There is no wrapper. The object is flat: the reserved keys and the command’s own fields sit side by
side at the top level. There is no command key, no exit_code key and no nesting under the
command’s name. A reader that wants a field reads it, and a reader that wants to know which fields a
command has reads decktalk schema NAME.
The four reserved keys
Every result carries all four.The two declared fields
Two more fields are declared once and carried by the commands that earn them, so a reader learns from the schema which commands do those things rather than meeting a null on the ones that do not.init, check, storyboard, narrate, cue, record, score, assemble, build,
clip, config set and config unset declare written. doctor, install, status, words,
serve, verify, config list, config get and config explain do not, because they write
nothing.
The error result carries neither field. A command refused before its run opened has no run to name.
A command whose run opened and then failed has one, and its events file still says why: run.done
carries the error. A narrate stopped or refused once its takes were under way, and a score or
build stopped or refused after it paid for something, print their own result instead, with ok
false, error filled and cost saying what the run spent, so a run stopped by Ctrl-C still reports
the takes and sounds it paid for.
The one exemption
decktalk schema prints the contract document itself with no reserved keys around it. A JSON Schema
document inside a wrapper is not that document, and a key called schema set to 1 inside a
document about schemas is unreadable. Every other command under --json prints one flat object.
A result that read a project
decktalk status --json on a project that has been written and not yet built.
next is the whole command to run next, so an agent that has just read the project already knows
what to run. It is null once the film on disk is the one the last build measured. It is also null
when the takes directory holds none of the takes the project played, because a build would buy them
again. Status then prints a warning line that says to point [narration] takes_dir at the folder
that holds them. runs lists every
run of this project whose events file is still open. unplayed lists the takes on disk that no
section plays, and is null when no voice is named, because then no take can be matched to a section.
take_state is what the disk holds for each spoken section: voiced, unchecked, stale,
placeholder or missing, with take_reason saying why in words. narrate, check and watch read the
same rule.
A result that judged the project
decktalk check --no-pages --json judges the written files with no browser at all and says which
judgements it therefore could not reach. It makes no film and prices what a voiced build would
cost, so it is the one call an agent makes to learn the price of a run.
pages_opened and frames_compared say how far the judgement reached. Both are true on a plain decktalk check,
--no-frames drops the freeze comparison alone, and --no-pages drops both and names on stderr
every code it could not reach. dollars is the price at the rate the voice publishes, and
ceiling_dollars is the most the run could cost. --max-cost is compared against ceiling_dollars
and never against dollars, because a provider charges one request at a time.
To add the take.charged and sound.charged lines yourself, add each dollars as the decimal it is
written as, settle the sum to the nearest billionth of a dollar, then round up to the cent: that is the
stage’s charged dollars. A take.charged or sound.charged line with possibly_charged true is a request whose reply
broke after it was sent, which the provider may have billed: it counts toward ceiling_dollars alone.
A result that judged and failed
The same call on a project whose script still holds an unfilled blank and whose cue waits for a phrase the script does not speak. It exits 1, anderror is null, because both of these are
judgements about the project rather than a reason the command could not run.
A build that stopped
A build stops after the stage whose findings reach the threshold, and the stages after it areskipped. It is still a result and not an error, so it exits 1, error is null, and stopped_at
names the stage. This is decktalk build --no-spend --json on a starter whose cue waits for a phrase
the script does not speak, cut to the keys a build adds.
spend is true when the caller let the run buy what is missing, which --spend does. A run that
may not plays every take on disk and reports each section whose take is missing as
TAKE_MISSING, a warning, so a draft build still
runs through under the default threshold.
stopped_at is null on a build that ran through, whatever it found. --allow CODE carries a build
past a code, and --fail-on moves the threshold, so both decide what stops a build exactly as they
decide what fails it.
A result that could not run
decktalk words --json before anything has been narrated. findings is empty and error is
filled, because nothing was judged. This result carries neither run nor written, because an
error result carries only the four reserved keys. The run it opened still has its events file, and
that file’s run.done carries the same error.
USAGE and exit 2. Pass --json before the
command name to get the object rather than the text form, because a flag after an unknown flag is
never parsed.
A finding
A finding is one judgement, in the same shape in every result, in the event stream and in the library.decktalk schema finding prints the model, and decktalk schema prints every code with
its sentence, its severity and the settings keys that move it.
severity and docs are the code’s own and are written into the object rather than left for a
reader to look up, so one line of JSON carries everything a decision needs.
location carries five keys. where is required and never null, because the object a finding
judges is what tells a reader which side of the spend it came from.
The two fixes
Afix is one of two kinds, and kind is the discriminator. Every fix carries a title, which
is one sentence saying what applying it does, and an applicability, which decides whether it may
be applied without asking.
kind: "edit" changes files. Its edits array is applied together or not at all. Each edit
names one file and exactly one locator, so nothing has to be guessed: pointer is a JSON pointer
for a JSON file, key is a dotted TOML key for a TOML file, such as a settings key in decktalk.toml, and line is a line number for any
other text file. old is what is there now, or null when the edit only adds, and new is what goes
there instead, which is the empty string when the edit removes.
This one comes from a CUE_UNLISTED finding that decktalk check --json raised on a project whose
deck declares two moments cues.json does not list.
kind: "command" runs a command. Its command array is the whole argv, one argument per
element, so a caller hands it straight to a subprocess with no shell. This one comes from
decktalk doctor --json on a machine whose cache holds none of the tools, which raises one such
fix for each of Chromium, ffmpeg and ffprobe.
--fix applies every safe fix a command found, --no-fix applies none, and an unsafe or display
fix is printed either way and never applied. It is on doctor, check and build. In the library
the same call is Project.apply(finding), which takes unsafe=True to reach an unsafe fix and
never reaches a display one.
Which findings fail a run
--fail-on names the least severe finding that fails a run, and --allow names an exception.
Both are on
doctor, check, storyboard, narrate, cue, record, score, assemble,
verify and build, which are the commands that can judge something.
Errors and exit codes
An error means DeckTalk could not run or did not finish. A result that carries anerror beside its
own fields is a run that stopped after it made something, and its cost and findings say what it
made. The error object carries code,
message, hint, location and docs. hint is the smallest next action, or null: the whole command that clears a refusal, or how to report a bug.
location is the same five-key object a finding carries, or null. message never carries the value
of a secret. Error codes lists every code with its exit, and each code
has a page of its own, such as NOT_BUILT.
Exit codes is the one table of what each exit means.
The rule an agent follows is one line. A non-zero exit with error null is a finding, so read
findings, change the project and run the command again. A non-null error is an error, so stop
and tell the person. Write the branch as error == null rather than as an exit number, because a
broken machine can exit non-zero for reasons that are not the project’s fault.
The event stream
A result arrives when the call is over. The event stream says what is happening while it runs.--events prints one JSON line per event on stderr as it happens, and every run of a project also
appends the same lines to build/events/<run>.jsonl, named by the run id the result carries. A run
with no project behind it, such as decktalk doctor outside one, reports events_file as null and
appends nothing.
--events every line of stderr is one JSON object. The sentences DeckTalk would otherwise
print are run.log lines among them, and the first line of a run is run.start, which names the file
the events are appended to. A refusal is written as one line holding the whole error result,
{"schema": 1, "ok": false, "findings": [], "error": {...}}. A refusal before the run opens is that
line alone.
The discriminator key is event, and every line carries event, time, seq and run. seq
counts from zero inside its run, so a reader can order lines that arrived out of order. Sections
that run at once, such as the takes narrate makes on its workers, write their lines in whatever
order the workers reach them, so the line after one section’s start may be another section’s. Each
section’s own lines still read in order, from its section.start to its section.done, and
stage.progress counts up one at a time, so a reader follows one section by its section field
rather than by the lines around it.
turn is the turn of its section a line is about, counted from one. narrate names it on its
section lines and on every take.charged, and a stage that does not work by turns leaves it null.
voice is the voice’s name in decktalk.toml, and provider the speech provider that voice names.
outcome is ran, kept, skipped, stopped or failed, and it is on run.done, stage.done
and section.done. kept is a stage the run planned and did not repeat, because nothing it reads had
changed since it last ran, or a section whose take narrate found on disk or whose recording record
found still standing. skipped is a stage the run did not plan or did not reach, and stopped is a
run the caller cancelled or interrupted. One
field replaces four event names, so a caller reads one field rather than branching four ways to learn the
same fact. unit on a progress line is take, section, score_item, pass or probe, because narrate
counts takes, record sections, score its score items, assemble encoding passes and verify probes, and level on a log line is debug, info, warning or error.
run.done carries error in the shape a result’s error takes when the run stopped or failed, and
null when it finished, so the events file of a failed run says why. A run.log line carries source,
the module that wrote it such as media.ffmpeg, or null for a stage’s own sentence, and the stage
and section it was written in. Its data is a flat map of the measured values behind the
sentence, so a reader filters on a value rather than parsing English.
A finding.raised line carries the judgement in the same shape the result will carry, so an agent that
watches the stream can react before the call returns.
A cost.priced line carries cost, the object a result carries, whose state is estimate before
the run buys anything and charged after.
How the price is worked out gives each of its fields.
build/events/ keeps the most recent events.keep_runs runs and deletes the oldest, and
that key defaults to 20. Run decktalk config explain events.keep_runs for its range. One
file stops taking progress, fetch, debug and info lines once it reaches events.max_bytes,
8 MiB by default, and keeps every line about the run, its stages and sections, its findings and its
costs. run.done says in dropped how many lines were left out, so a reader can tell a bounded
file from a quiet run.
Watching from Python
Nothing in the library prints, so the stream is how a library caller learns what is happening.Machine.events is the whole stream and Project.events is its filtered view.
events.subscribe(listener, runs=None) returns a Subscription, and the listener receives the same
objects --events prints.
Related
- CLI for every command, flag, error code and finding code.
- Reference card for the whole contract on one page.
- Python API for the library that returns these same objects.
- Configuration for every settings key an edit fix can name in its
key.