# AGENTS.md Source: https://docs.decktalk.ai/agents/agents-md The short file that tells any coding agent it is standing in a DeckTalk project. `AGENTS.md` is a plain Markdown file that a coding agent loads at the start of every session. It is stewarded by the Agentic AI Foundation, and Codex, Cursor, Copilot, VS Code, Zed, Aider and many others read it. It is the one agent surface that costs context in every session, so it stays short. `decktalk init` writes a ten-line `AGENTS.md` when the project has none. It never overwrites one that is already there, so a project with its own `AGENTS.md` keeps it. ## What DeckTalk writes The generated file says four things and stops. * This folder is a DeckTalk project. * The four input files are `script.md`, `decktalk.toml`, `cues.json` and the deck page. * `decktalk status --json` is the first command to run, because it reports what the four files say, what is built and what is stale. * The skills are in `.agents/skills/`, and they hold the procedures. Procedures do not belong in `AGENTS.md`. A procedure that is loaded in every session is paid for in every session, and the skills exist so that an agent loads a procedure only when it needs one. Keep `AGENTS.md` to orientation, and put the steps in a skill. ## Adding DeckTalk to an AGENTS.md you already have Add the same four lines to your own file under a heading of their own, and leave the rest alone. The file is read as plain Markdown, so a heading and a short list are enough. ## Harnesses that read a different file Claude Code reads `CLAUDE.md` rather than `AGENTS.md`. The usual arrangement is a `CLAUDE.md` whose whole content is a reference to `AGENTS.md`, so one file stays the source. Gemini CLI reads `GEMINI.md` by default and reads `AGENTS.md` when its context file setting names it. `AGENTS.md` also serves a harness that reads no skills at all. For that harness it is the only DeckTalk instruction in the session, which is why it names `decktalk status --json` rather than describing the pipeline. # Install the skills Source: https://docs.decktalk.ai/agents/install Where the skills land, which folders each coding agent reads, and what DeckTalk has actually tested. `decktalk init` installs the six skills into a new project by default. Pass `--no-skills` to leave them out. ```console theme={null} decktalk init my-video ``` ## The folders The skills are written once, into one generic folder, and a second folder points at it. | Path | What it is | | ----------------------------------------- | ----------------------------------------------------------------------------------------------- | | `.agents/skills/decktalk-/SKILL.md` | The skills themselves, in the folder the Agent Skills format names for sharing between clients. | | `.claude/skills` | A link to `.agents/skills`, because Claude Code reads its own folder and not the generic one. | On a machine where a link cannot be made, which is usually Windows without the privilege for one, `init` writes a copy instead of a link. The copy holds the same files, and it has to be written again when the skills change. Do not commit a second copy of a skill under another name. Several harnesses read both folders, and two copies of one skill is the case where a harness has to guess. ## Which harness reads which folder Every harness below loads a `SKILL.md` natively. The folders are the ones each harness documents for a project. | Harness | Project folders it reads | | -------------------------- | --------------------------------------------------------- | | Claude Code | `.claude/skills/` | | OpenAI Codex | `.agents/skills/` | | Cursor | `.agents/skills/`, `.cursor/skills/`, `.claude/skills/` | | GitHub Copilot and VS Code | `.github/skills/`, `.claude/skills/`, `.agents/skills/` | | Gemini CLI | `.gemini/skills/`, `.agents/skills/` | | Windsurf | `.windsurf/skills/`, `.agents/skills/` | | opencode | `.opencode/skills/`, `.claude/skills/`, `.agents/skills/` | | Amp | `.agents/skills/`, `.claude/skills/` | | Goose | `.agents/skills/`, `.goose/skills/`, `.claude/skills/` | The two folders that `init` writes reach every harness in that table. `.agents/skills/` reaches eight of them, and `.claude/skills` reaches Claude Code, which is the one harness that does not read the generic folder. DeckTalk tests its skills on Claude Code alone. The release test is a trigger run over Claude Code and a build made from the skills alone. Every other harness in the table is supported through the generic folder and is untested, because the founder does not run it. A skill that misfires on another harness is worth a GitHub Discussions post, and it is not a case DeckTalk can reproduce. ## Why the skills use no harness-specific fields DeckTalk skills use only the fields the open format defines, which are `name`, `description`, `license`, `compatibility` and `metadata`. Several harnesses add fields of their own, and a field that one harness honours and another ignores gives one skill two behaviours. Some services also reject a skill whose front matter carries a key they do not know. The cost of that choice is that no front-matter field can mark a skill as user-only, so the guards against spending live in the skill text and in the CLI. [Agent skills](/agents/skills) lists them. ## Install the skills without installing DeckTalk The repository carries a `skills` folder at its root that points at the same files, so a generic skill installer can read the skills straight from the repository before DeckTalk is on the machine. The skills drive a CLI, so they are only useful once that CLI exists. # The JSON contract Source: https://docs.decktalk.ai/agents/json The one envelope every command prints under --json, the finding rows inside it, and what each exit code means. Every DeckTalk command takes `--json`. Under that flag a command prints exactly one object on stdout, prints nothing else on stdout, turns colour off and sends every log line to stderr. An error prints the envelope too. This page is the short contract an agent needs. The per-command payloads are in the [CLI reference](/reference/cli). ## The envelope | Field | Type | Meaning | | ----------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------- | | `schema` | integer | The envelope's shape version, which changes only when the shape changes. It is not the product version. | | `version` | string | The installed DeckTalk version. | | `command` | string | The command as typed, with a subcommand joined by one space. | | `ok` | boolean | True when the command found nothing and raised nothing. It ignores `--exit-zero`. | | `exit_code` | integer | The code the process is about to return, so a caller that lost the code can still read it. | | `summary` | object | A small object of counts and totals that leads the output. Its keys are the command's own. | | `findings` | object | `{"certain": n, "uncertain": n, "items": [...]}`. | | `written` | array of strings | Every file the command wrote, as project-relative POSIX paths, in the order written. A read-only command writes an empty array. | | `error` | object or null | Null on success and on a finding. On an error it carries `code`, `message`, `hint` and `path`. | | `` | object | The command's own payload, under the first word of the command name. | ## A finding row Every row of `findings.items[]` has the same shape on every command, so an agent can dispatch on the rows alone without reading the per-command payload. | Key | Type | Meaning | | --------- | --------------- | ------------------------------------------------------------------------------------------------------ | | `code` | string | The verdict or reason code, such as `SPEECH_AT_CUT`. Dispatch on this. | | `label` | string | The human label, such as `SPEECH AT CUT`. Nothing parses it. | | `certain` | boolean | True when the finding is certain. | | `section` | integer or null | The section number. | | `cue` | string or null | The cue id. | | `where` | string or null | The file, the page or the artifact the finding is about, as a project-relative path when it is a file. | | `detail` | string or null | One sentence of detail, with any measured number in it. | A row that judges nothing carries the passing code `NOTE`, so `code` and `label` are strings on every row and a reader never meets a null code. ## An error The `error` object is filled by whatever raised the error, so a slot it does not know is null. | Key | Meaning | | --------- | ----------------------------------------------------------------------------- | | `code` | One of `CONFIG`, `MISSING_INPUT`, `PROVIDER`, `TOOL`, `USAGE` and `INTERNAL`. | | `message` | One sentence. It never carries the value of a secret. | | `hint` | The smallest next action, or null. | | `path` | The project-relative file the error is about, or null. | In text mode the same error prints as `error[CONFIG]: ...` on stderr, so the code is readable without `--json`. ## Exit codes | Code | Meaning | How to tell | | ---- | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | 0 | The command ran and found nothing, or `--exit-zero` was passed. | `ok` is true, or `exit_code` is 0 with findings present. | | 1 | The command ran and found something, which is a certain finding or an uncertain one under `--strict`. | `error` is null and `findings.certain` or `findings.uncertain` is above zero. | | 2 | A usage error from the parser, such as an unknown command, an unknown flag or a bad value. | `error.code` is `USAGE`. | | 3 | DeckTalk itself could not run. | `error` is not null and `error.code` names the error class. | | 130 | The run was interrupted. The process was signalled, and no envelope is promised. | There is no envelope to read. | The rule an agent follows is one line. A non-zero exit with `error` null is a finding, and the agent may fix the project and run the command again. A non-null `error` is an error, and the agent stops and tells the person. Write the trigger as `error == null` rather than as "exits 1", because a broken machine can exit non-zero for reasons that are not the project's fault. ## Watching a build `decktalk build` appends one JSON Lines object per event to `build/progress.jsonl`, relocatable with `--progress`. Each row carries `ts`, `stage`, `stage_index`, `stage_count`, `section`, `event`, `detail` and `pid`, and `event` is one of `start`, `done`, `skip` and `fail`. Poll `decktalk status --json` rather than tailing that file. Its `run` object is null when no build is running, and otherwise carries `pid`, `started`, `stage`, `sections_done`, `sections_total` and `alive`. ## Reading what a project has `status.narration` is the narration clock that `build/narration/takes.json` gives: `exists`, `estimated`, `total_seconds`, and one `sections[]` row per spoken section with `key`, `title`, `start`, `end` and `duration`. `estimated` is true while the takes are placeholders a run without voice wrote. Each `status.sections[]` row carries `key`, `kind`, `source`, `recorded`, `cut` and `stale`. `stale` is null while the recording on disk still matches the project, and otherwise one sentence saying why it does not, which is the same rule `record` uses to decide what to record again. # Agent skills Source: https://docs.decktalk.ai/agents/skills The six packaged skills that teach a coding agent to write, build and repair a DeckTalk video. DeckTalk ships six agent skills. A skill is a folder with a `SKILL.md` file, written in the Agent Skills open format, that a coding agent loads when a task matches its description. The skills turn the CLI into something an agent can drive on its own, and they carry the rules that a person would otherwise learn from these docs. The skills live inside the Python package, so every installation carries the skills that match its own CLI. `decktalk init` writes them into a new project, and [Install the skills](/agents/install) covers the folders they land in. ## The six skills | Skill | It starts when | It ends with | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | | `decktalk-script` | someone asks for a new narrated video, explainer, tutorial, product demo or lesson, adds a section, or changes what the voice says. | `script.md` written to a time budget, with a table of every spoken phrase and the symbols it stands for. | | `decktalk-slide` | a narrated video needs a new slide or scene, when maths or code has to appear on screen, when a picture must be added to an existing section, or when a reveal is too small, overlapping or off frame. | Markup that obeys the page contract, checked against the PNGs that `decktalk screenshots` writes. | | `decktalk-cues` | a script, a page or a reveal changes, when align reports an unresolved or unknown cue, when a picture appears early, late or not at all, or when a reveal is too small for the checker to see. | Every `data-cue` matched to `cues.json` in both directions, with the gaps inside their thresholds. | | `decktalk-build` | someone asks to build, render, export, preview or check a narrated video, an explainer, a tutorial or a lesson, or after any edit to script.md, cues.json, decktalk.toml or a deck page. | A verified build, with the frames shown to you before the work is called done. | | `decktalk-fix` | preflight, align, record, verify or build exits non-zero with error set to null, when a cue is unresolved or unknown, when a reveal lands off cue or shows no change, when a recording stalls or starts black, or when preflight skips every cue. | The smallest edit that clears the finding, and the failing command run again with `--only`. | | `decktalk-revise` | someone says a demo, tutorial, explainer or lesson is out of date, asks for this week's update, or names a release that changed what the video claims. | The fewest edits, a build of the changed sections alone, and a change note with the sections and the dollars. | ## What the skills will not do on their own The skills are written so that an agent cannot spend your money or hide a failure. * A voiced run stops and asks. `decktalk-build` reports the sections a voiced run would voice, their characters, the dollars and the host, and then waits for your approval. * No skill passes `--force`, `--exit-zero` or an `--allow-` flag, and no skill turns `verify` off. * No skill edits the `[voice]` table without asking. * `decktalk-fix` reads `findings.items[]` and fixes the project. On exit code 3 it stops and reports the error to you, because exit code 3 means DeckTalk itself could not run. No portable field in the skill format stops a model from starting a skill on its own, so these guards live in the skill text and in the CLI rather than in front matter. ## The other two agent surfaces A skill is not the only thing to hand an agent. * [The reference card](/reference/card) puts every file, command, rule and exit code on one page. Hand it to an agent that has no skills loaded. * [`llms.txt`](https://docs.decktalk.ai/llms.txt) indexes every page on this site, and [`llms-full.txt`](https://docs.decktalk.ai/llms-full.txt) is the whole site as one file. Hand those to an agent that can fetch a URL. * [The JSON contract](/agents/json) is what an agent reads back after every command. DeckTalk ships no MCP server. The CLI with `--json` and its exit codes is the machine interface, and the skills teach an agent to use it. # Changelog Source: https://docs.decktalk.ai/changelog See what changed in each DeckTalk release, including the file formats and the page contract. This page lists every DeckTalk release, newest first. DeckTalk follows semantic versioning. A change to a file format or to the page contract raises the minor version. The file formats are `decktalk.toml`, `cues.json`, and the build artifacts. A project keeps the `deck/decktalk-runtime.js` it was created with. To move a project onto a newer runtime, create a new project with the new DeckTalk and copy the deck pages across. **⚠ BREAKING CHANGES** * **verdicts:** give the three counted judgements verdicts of their own * **cli:** wire --from and --to, retire measure and check, and report a stale recording * **narrate:** fold the narration clock into the take index, with a lead and a tail per take * **cli:** make the CLI a package that prints one envelope and one exit code * **assemble:** split the stage into cut, mix, loudness and publish, and write what a viewer needs * **verdicts:** make an unparsed or missing KaTeX a certain finding * **record:** fold measure and check into one record package * **narrate:** identify a take by its content and price the plan before it runs * **align:** check cues and pages against each other in both directions * rename the vocabulary for 0.4.0 * **narrate:** fold the narration clock into the take index, with a lead and a tail per take ([5a6120a](https://github.com/jacobcbeaudin/decktalk/commit/5a6120a213de7d70ab0190bcc20c4afa65e59152)) * rename the vocabulary for 0.4.0 ([6b2a9fe](https://github.com/jacobcbeaudin/decktalk/commit/6b2a9fe552a88d86fa278d0e2ba073d53fb4c4ce)) **Features** * **align:** check cues and pages against each other in both directions ([7fd3603](https://github.com/jacobcbeaudin/decktalk/commit/7fd3603adb22b84c7e8423cdabc8598915a01e61)) * **api:** export the cut list reader ([6b81600](https://github.com/jacobcbeaudin/decktalk/commit/6b81600b635cc23619bb16065436c66d22be12c4)) * **assemble:** split the stage into cut, mix, loudness and publish, and write what a viewer needs ([5dccf25](https://github.com/jacobcbeaudin/decktalk/commit/5dccf25299eebc633e1b1841f6655a2029a607db)) * **build:** run the real verify last, and write a progress log an agent can read ([1ad53a9](https://github.com/jacobcbeaudin/decktalk/commit/1ad53a90f7b5362b9745910df0dd3b7b19b79aba)) * **cli:** make the CLI a package that prints one envelope and one exit code ([ee6bd9b](https://github.com/jacobcbeaudin/decktalk/commit/ee6bd9b4003bf46de2d8399d80a3828fff645110)) * **cli:** wire --from and --to, retire measure and check, and report a stale recording ([2289630](https://github.com/jacobcbeaudin/decktalk/commit/22896304be59b31a784ad2d679ea177be65d75ea)) * give every stage one result and enforce the five layers ([f51ba4d](https://github.com/jacobcbeaudin/decktalk/commit/f51ba4dce71bdb75ce55432e44a22b961c4ac3ae)) * **media:** fetch a pinned, SHA-256 verified ffmpeg build in place of static-ffmpeg ([ba8be62](https://github.com/jacobcbeaudin/decktalk/commit/ba8be627bb2a85d44f34badbb92c3339422cefc7)) * **media:** serve every page from a local origin, and add decktalk serve ([dc238f5](https://github.com/jacobcbeaudin/decktalk/commit/dc238f54b5b5ec501243073ef94e8a8c9b095f5d)) * **narrate:** identify a take by its content and price the plan before it runs ([0ac39c4](https://github.com/jacobcbeaudin/decktalk/commit/0ac39c44b11f228d5c90c0db7c20a0a7154c9225)) * **providers:** keep the ElevenLabs key on an https ElevenLabs host ([5d3b94d](https://github.com/jacobcbeaudin/decktalk/commit/5d3b94ddad1b9e67ec25765936a0fed9fb64dfa6)) * **record:** fold measure and check into one record package ([b02e56b](https://github.com/jacobcbeaudin/decktalk/commit/b02e56b8d77de9d101f43e8989df59a135bc99c2)) * **record:** keep a section whose scene, assets, words and cues are unchanged ([bf371fa](https://github.com/jacobcbeaudin/decktalk/commit/bf371fa15cde9ffdebdcf956cf93cfaefc121abf)) * **runtime:** rebuild the page runtime on markup slides and close the first-frame race ([39aab42](https://github.com/jacobcbeaudin/decktalk/commit/39aab42498a62c711652acb08b2e9bf0d6ac3865)) * **runtime:** write each reveal's description on the cue log row the transcript reads ([fe107ee](https://github.com/jacobcbeaudin/decktalk/commit/fe107ee4efc76f72bba27d232e83480251ddd489)) * **scaffold:** make init write a small starter, an example, the skills and AGENTS.md ([fcea3e8](https://github.com/jacobcbeaudin/decktalk/commit/fcea3e83643ae0fc407ef314fc6c5ee02fc2b88e)) * **scaffold:** ship KaTeX inside the wheel ([cbcad35](https://github.com/jacobcbeaudin/decktalk/commit/cbcad3544d7021393cfbdaf6c507b8681a27b1a7)) * **screenshots:** give every written PNG a row, and add --json ([93cb221](https://github.com/jacobcbeaudin/decktalk/commit/93cb2212d3844fe0c951a07ff4c2e72327f7974d)) * **scripts:** check every internal docs link and the navigation ([21c8a63](https://github.com/jacobcbeaudin/decktalk/commit/21c8a631ef39778ad76c37f327fce4d5536f502d)) * **skills:** ship the six skills an agent works from, and a test that keeps them true ([b237a47](https://github.com/jacobcbeaudin/decktalk/commit/b237a47fdc09438e1c6929219f5d766dc2a88be7)) * **status:** read the four input files against each other, and report the run ([b8c9298](https://github.com/jacobcbeaudin/decktalk/commit/b8c92985be3f622fe9bd4034cec839c1152648ed)) * **verdicts:** give the three counted judgements verdicts of their own ([5a7ed81](https://github.com/jacobcbeaudin/decktalk/commit/5a7ed8198bda15d318540ebb1f8bf13ca8c1995a)) * **verdicts:** make an unparsed or missing KaTeX a certain finding ([7eec66c](https://github.com/jacobcbeaudin/decktalk/commit/7eec66cff8043e3693ef61969b21f7f91274ddb6)) **Features** * **beats:** warn when a cue phrase occurs more than once in its section ([1174081](https://github.com/jacobcbeaudin/decktalk/commit/1174081fd0a1f49c855c779f9123fe9615cf2b55)) * **clip:** add decktalk clip and decktalk words ([846de9f](https://github.com/jacobcbeaudin/decktalk/commit/846de9f4f857f8d1621875f232b0ed9ff1f856bb)) * **narrate:** add lead\_seconds and tail\_seconds, and allow hold\_seconds on any page section ([ce4baf9](https://github.com/jacobcbeaudin/decktalk/commit/ce4baf9b0006a8489d8ea9efc8fbbb55f51b2105)) * **narrate:** print the take plan in a dry run, and add --json ([bd34f89](https://github.com/jacobcbeaudin/decktalk/commit/bd34f890c8515d0ce969f762ba46d9f39332336e)) * **preflight:** check a voiced build before it spends credits or records ([6af0507](https://github.com/jacobcbeaudin/decktalk/commit/6af050748dd21332306bac1cbeb0bfa96445207d)) * **runtime:** list each step's cues in the catalog, and freeze just before a cue ([fe329dd](https://github.com/jacobcbeaudin/decktalk/commit/fe329ddc1c2678314d3a3ecee5dc52b937632ab4)) * **verify:** check that the cut into a carries\_previous section does not pop ([91e6578](https://github.com/jacobcbeaudin/decktalk/commit/91e6578a842f8cd3db1d929751597cb9d88dd251)) * **verify:** mark a cue that passes by a thin change as THIN CHANGE? ([ffc09b6](https://github.com/jacobcbeaudin/decktalk/commit/ffc09b64f948c3e94f654999e55d28560215778c)) **Bug Fixes** * **assemble:** warn about a recording that measure has not read ([95896c0](https://github.com/jacobcbeaudin/decktalk/commit/95896c01f38cf80c075fd8c93c1157867b79abb5)) * **narrate:** refuse a silent run over voiced takes unless forced ([6517ebc](https://github.com/jacobcbeaudin/decktalk/commit/6517ebccf5629812ab1d0c938a4797515f6ab7db)) * **narrate:** stop padding a cached take again on every run ([93d643a](https://github.com/jacobcbeaudin/decktalk/commit/93d643ac7f3ad772c20534a0850eee3e59c9c9e9)) * **verify:** count only the sections in decktalk.toml ([32112eb](https://github.com/jacobcbeaudin/decktalk/commit/32112ebd8686f6b2a82efd1606ed6630f5d26fa0)) * **verify:** fit probes and controls between close cues ([f4dee0e](https://github.com/jacobcbeaudin/decktalk/commit/f4dee0ea54372c0e2e8f025a50708737901ce39b)) **Features** * **assemble:** caption the speech inside a clip section from its words file ([cf5b938](https://github.com/jacobcbeaudin/decktalk/commit/cf5b9388866a669d85c538cb3250ceb5cc1bd6c5)) * **captions:** break captions at sentence ends ([3a7df4c](https://github.com/jacobcbeaudin/decktalk/commit/3a7df4cbc470defc7a2d6460d72dc1ddbd36df82)) * **chapters:** merge consecutive sections with the same title into one chapter ([0191f1b](https://github.com/jacobcbeaudin/decktalk/commit/0191f1b17cdf6d4bd4dfcb753f1b59ba227d5025)) * **narrate:** keep a renumbered section's take by copying it to the new name ([41ced3f](https://github.com/jacobcbeaudin/decktalk/commit/41ced3ff188c1e4db83f5341289eeed37bc24b93)) * **template:** port the approved demo film's lesson into the scaffold ([0f5332a](https://github.com/jacobcbeaudin/decktalk/commit/0f5332a8d6cd31d8c1131f6bf3471e83ddaac516)) * **template:** port the v16 demo film's edit into the scaffold ([a530783](https://github.com/jacobcbeaudin/decktalk/commit/a530783abd2f52924457929dc95711589d89da93)) **Features** * a demo deck built to a fifteen-person review, with a WebGL loss surface ([148e59f](https://github.com/jacobcbeaudin/decktalk/commit/148e59f0dbdacd6c94de9ae58e3a6b0fa802d148)) * captions, chapters, measured cue offsets, and a loudness pass that hits its target ([27a8c1d](https://github.com/jacobcbeaudin/decktalk/commit/27a8c1d77ea1d38a477cf27e8123f0030dc83003)) * close on "Update your video the way you update a doc." ([339dfe9](https://github.com/jacobcbeaudin/decktalk/commit/339dfe990cda887f0063b16cb60ff2f753340bce)) * closing scene, a cut check in verify, and a cover that survives a late capture ([b1e1cdd](https://github.com/jacobcbeaudin/decktalk/commit/b1e1cdd5bf46637530a50f65009fe3c114e5abf0)) * demo deck after the round-two design and content review ([1c5e44c](https://github.com/jacobcbeaudin/decktalk/commit/1c5e44cbe724e37bf0650708a069c7d4928ab2f2)) * demo v10, the storytelling panel's rewrite of all five scenes ([8754f83](https://github.com/jacobcbeaudin/decktalk/commit/8754f83aff96f5791be69449c260a21a1339aa2c)) * demo v11, a lay lesson on how AI learns, with a slot for B-roll ([dd9147e](https://github.com/jacobcbeaudin/decktalk/commit/dd9147ee93be0a0a6d08aea5123733a417278c38)) * **demo:** a ball walks the too-long and too-short paths as their markers appear ([e25bade](https://github.com/jacobcbeaudin/decktalk/commit/e25bade3033a011d055280343753924b9a6d24a2)) * lesson-shaped scaffold, vendored KaTeX, pause directions, and page warnings ([5ee46a8](https://github.com/jacobcbeaudin/decktalk/commit/5ee46a8f27898a76d403b8d511ca53e93698d8b2)) * machine-readable output and one exit policy for the read-only commands ([393cbf8](https://github.com/jacobcbeaudin/decktalk/commit/393cbf8e109c2b7bce761c3a1695863dc3dcdad4)) * pass the slide and cue context to handlers, freeze at one cue, and warn on unreachable reveals ([675894d](https://github.com/jacobcbeaudin/decktalk/commit/675894d153dccfd635580991f86edad5dac83e46)) * play a clip between page sections, with the narration paused around it ([e884b29](https://github.com/jacobcbeaudin/decktalk/commit/e884b293551db437c0a485f79af5e47c2d10f98d)) * **record:** log when each cue was due, ran, and drew in the sidecar ([a9a25b3](https://github.com/jacobcbeaudin/decktalk/commit/a9a25b3801ada0e5be8c0d9c881f8b8743d99247)) * **record:** pass the previous section's words as prevwords, so scene 2 carries the Open's real count times ([04cb547](https://github.com/jacobcbeaudin/decktalk/commit/04cb547cdc315054c974d943c36d90ef68ff2320)) * refine the graphics and adopt the four-tick mark ([142f511](https://github.com/jacobcbeaudin/decktalk/commit/142f511310a7b31ea04459fbcbfb77512f93d928)) * show the function before naming it in the demo, and close on decktalk.app ([f9f251f](https://github.com/jacobcbeaudin/decktalk/commit/f9f251fd804f7d3b1022deb28aea3c2cb3c09955)) * **site:** host the demo video with the homepage ([fecdfa2](https://github.com/jacobcbeaudin/decktalk/commit/fecdfa2dcc0762e282f5fc87172ef2487c8ca8e4)) * social card, README rebuilt around the lesson scaffold, and limiter headroom ([2a4a93b](https://github.com/jacobcbeaudin/decktalk/commit/2a4a93bd0fa183b40aae22787bec5fe32233d0b8)) * teach the learning rate honestly in the demo's gradient descent scene ([0b8a958](https://github.com/jacobcbeaudin/decktalk/commit/0b8a95883617a28c1eb74c6f46118c58910696f0)) * **template:** open the lesson on its title and bring in the rules card on "rules" ([e596540](https://github.com/jacobcbeaudin/decktalk/commit/e596540019a6e7340e332a1d80dd9d87a06c254e)) * **template:** scaffold the v12 demo film ([ce17e25](https://github.com/jacobcbeaudin/decktalk/commit/ce17e256ee1b59fcdb1b0463acd8216fc221510e)) * verify every cue by default, catch unknown cue ids, and keep clips at the edges ([bd6c236](https://github.com/jacobcbeaudin/decktalk/commit/bd6c2365ca3d1691ff71705dbba5a05ff4a2fbc4)) **Bug Fixes** * accept -v and -q after the command, export result types, and report unexpected errors cleanly ([ffd86ad](https://github.com/jacobcbeaudin/decktalk/commit/ffd86ad00fd9878b7c28bb3998319612e0767952)) * captions keep the script's punctuation and the onset scan sees low-contrast reveals ([24357b7](https://github.com/jacobcbeaudin/decktalk/commit/24357b7a5ed10fcb1a984c8629f47b7be87abf3a)) * count only the part of a frame stall that falls after narration starts ([1be18ee](https://github.com/jacobcbeaudin/decktalk/commit/1be18eebae076cd8f89df37cb3ddc477c339046b)) * cover the page until the clock starts so a cut cannot miss the marker ([f33a2d9](https://github.com/jacobcbeaudin/decktalk/commit/f33a2d94c3872ad932798395d05904ad9c1038dc)) * define gradient descent before the surface appears, and hand off once at the edit ([1b25627](https://github.com/jacobcbeaudin/decktalk/commit/1b25627b091a0553435b9cc5e6eb041b454b106f)) * exact frame seeks on every ffmpeg build, and sidecars that explain synced reveals ([2499312](https://github.com/jacobcbeaudin/decktalk/commit/249931205f61c076273fc997338c5e0748402aef)) * fail check on a page error, warn on bad TeX and dead cues, ease the ball steps out, and keep doctor from downloading ([171f4bf](https://github.com/jacobcbeaudin/decktalk/commit/171f4bf654c0a88fa6c10f01c692cb69862ce111)) * **narrate:** pad cached takes to min\_tail\_seconds ([df93b08](https://github.com/jacobcbeaudin/decktalk/commit/df93b087172e56ee098f6e034507539dc5023c40)) * print a skipped verify row's reason once ([2cf727d](https://github.com/jacobcbeaudin/decktalk/commit/2cf727d38d4787a5fbcf00764f018d36f00422f1)) * quote scene 3's real length in the edit scene and give the close a beat ([7f368cc](https://github.com/jacobcbeaudin/decktalk/commit/7f368cc999818551585a8da4f62e27a436c763c2)) * read and write every text file as UTF-8, so init works on Windows ([645aebc](https://github.com/jacobcbeaudin/decktalk/commit/645aebc79fe183cc6dc3b8898fabe14ac4468874)) * **record:** keep frames flowing after the cover so still pages record reveals on time ([0abcf45](https://github.com/jacobcbeaudin/decktalk/commit/0abcf4532d7ec8b1254e6e795355e95d40beeb5c)) * report unknown cue ids from beats instead of stopping before the table ([63208b9](https://github.com/jacobcbeaudin/decktalk/commit/63208b98518df26d254ab07df8675b5da4c9cef7)) * **runtime:** draw the preview index in the page's own colors ([5eec565](https://github.com/jacobcbeaudin/decktalk/commit/5eec565d761f7a0e0265e90c0f56a7dd8f6d0c2f)) * say the thesis once in the demo deck ([a677391](https://github.com/jacobcbeaudin/decktalk/commit/a677391b3d7b10f52c02474ecca54370f241046e)) * start the clock on the next frame, detect and retry stalled recordings, and make frame comparisons build-proof ([91cd81c](https://github.com/jacobcbeaudin/decktalk/commit/91cd81c3de6846ae0f338a8875447870994d1d8f)) * timeline positions from decoded audio, word-synced reveals, a click test for sync, and a scaffold that teaches gradient descent ([9b38bc2](https://github.com/jacobcbeaudin/decktalk/commit/9b38bc23cfb177853e4e283c6a84c681057ae541)) * trim the scanned span per stream so the looped reference frame is not seeked past its end ([028c845](https://github.com/jacobcbeaudin/decktalk/commit/028c84501cc976655cb8e3250f2cb2c5d6b11e97)) * **verify:** compare luma only, so a static colored frame reads zero ([d74bdc6](https://github.com/jacobcbeaudin/decktalk/commit/d74bdc6ca2f7b9f1a3e8a5ec0e8b680344ead6ac)) * **verify:** confirm each onset on 8 by 8 blocks, so encoder ringing never reads as an early reveal ([8e9887a](https://github.com/jacobcbeaudin/decktalk/commit/8e9887afa7a48b9961b83b98c175ab571b66806f)) * warm every scene 3 mesh so no cue freezes the page on its first draw ([938f9a3](https://github.com/jacobcbeaudin/decktalk/commit/938f9a3637f2a90756e176e1185c3844f755f485)) * warm the scene 3 figure at its real drawing state before narration starts ([90807c4](https://github.com/jacobcbeaudin/decktalk/commit/90807c4cdd8844a67690a93edc1f4cb41822862f)) * warn on unknown decktalk.toml keys, let doctor pass without KaTeX, and add optional clip sections ([dcc4dbf](https://github.com/jacobcbeaudin/decktalk/commit/dcc4dbf1bcdee7327d38fb57107c1074fbaf25cc)) * warn when a step mounts equations without KaTeX in cue mode ([2005a4a](https://github.com/jacobcbeaudin/decktalk/commit/2005a4a30b5e57336c1bc3fa87cc4532326525b5)) First release. DeckTalk turns a markdown script, plain HTML slides, and an ElevenLabs voice into one mp4 in which every reveal lands on the word that introduces it. It records the slides in headless Chromium, cuts each section to the narration frame-exactly, mixes an optional soundscape, normalizes loudness, and verifies the result. Chromium and ffmpeg arrive through Python packages, and `--silent` renders a full draft without an API key. # Cues Source: https://docs.decktalk.ai/concepts/cues Learn how a cue phrase in cues.json becomes a cue time, and what happens when a cue and its page disagree. A cue connects a spoken phrase to something that happens on a page, such as an element that appears. Read this page when you choose a phrase, or when a cue does not resolve or starts on the wrong word. [Writing for the ear](/guides/writing-for-the-ear) is the guide for writing phrases. The starter's section 1 shows one cue, `1.1script`, in `script.md`, `cues.json`, and `deck/index.html`. ```md script.md theme={null} ## 1. Open This is DeckTalk. [beat] You write what you will say, [beat] and you write the slides that go with it. [beat] Then every picture waits for its own word. ``` ```json cues.json theme={null} { "sections": { "1": { "cues": [ { "cue": "1.1title", "on": "This is DeckTalk", "occurrence": 1 }, { "cue": "1.1script", "on": "what you will say", "occurrence": 1 }, { "cue": "1.1deck", "on": "the slides", "occurrence": 1 }, { "cue": "1.1word", "on": "its own word", "occurrence": 1 } ] } } } ``` ```html deck/index.html theme={null}
script.md
what you will say
```
1. You write the cue phrase in `cues.json`, and the cue id in the page. 2. The `align` stage resolves each cue phrase to a cue time, in seconds after the section start. The cue time of `1.1script` is the start of the word "what". 3. The `align` stage writes the cue times to `build/cue-times.json`. 4. The recorder passes the cue times to the page as `?cues=`, and the page fires each cue at its time. 5. `decktalk verify` checks that each reveal starts on its word. `decktalk align` prints the cue times of every section. These lines come from a build without voice of the starter, so the times are estimates. ```text theme={null} sec speech need cues 01 13.0 - 1.1title@0.5,1.1script@2.9,1.1deck@6.27,1.1word@11.56 02 14.6 - 2.1average@1.44,2.1formula@5.67,2.1code@9.43,2.1waited@11.31 03 11.9 - 3.1idea@1.41,3.1again@7.8,3.1make@8.71 3 sections with cues, 0 unresolved (estimated words: times are placeholders) ``` ## Matching rules The `align` stage matches a cue phrase against the section's spoken words with these rules: 1. It splits the phrase and the spoken words on whitespace only. 2. Each token keeps only A to Z, a to z, 0 to 9, and the straight apostrophe. Accented letters, Greek letters, and curly apostrophes are removed. 3. Matching ignores case, unless `case_sensitive` is true. 4. A phrase token with no characters left is ignored. For example, the phrase `learning rate η` matches as `learning rate`. 5. A hyphenated word is one token. "zig-zag" becomes `zigzag`, so the phrase "zig zag" does not match it. 6. The stage looks through the spoken words, in order, for a run that matches the phrase token for token. `occurrence` picks the run, and the default is the first. When the phrase matches more than once and the cue does not set `occurrence`, the stage warns and names each match with its time. 7. The cue time is the start of the run's first word, plus the offset key, rounded to hundredths of a second. 8. `$start` is 0.0 s plus the offset key. A negative result stays negative, and the page fires it at t=0. 9. `$end` is the end of the section's last spoken word, plus the offset key. The page matches `data-text="spoken"` text with a different rule. It ignores apostrophes, but the `align` stage keeps them. [`data-text="spoken"`](/reference/runtime#data-text="spoken") has the page's rules. Write a cue phrase the way the voice says it, because the spoken words come from the voice. The starter's script says "decktalk dot AI", and its end card shows decktalk.ai. So the cue `3.1make` matches on "Make your own" and never on the text of the card. A number written in digits comes back in the form the voice chose, so avoid digits in a cue phrase. Stage directions are not spoken words. A `[beat]` becomes a dash, and a `[pause N]` becomes a break tag. Neither is a word, so a phrase that spans a direction still matches. [script.md](/reference/script-md) has the rules for directions. ## Which sections have cues Every section key in `cues.json` must name a `[[section]]` in `decktalk.toml`. A section can have no cues. A page section with no cues plays its preview timing in the recording. A clip section has no narration, so the `align` stage skips its cues. ## Cue mismatches A cue can disagree with its page in six ways. The `align` stage catches the first one before the recording. The page catches the others during the recording. | Mismatch | Caught by | Message | Result | | ---------------------------------------------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | A cue id in `cues.json` appears nowhere in its page. | the `align` stage | `: not in deck/index.html` | `align` exits 1, and `build` stops after the `align` table. | | No slide of the playing scene owns the cue. | the page, at the start | `unknown cue id (no slide id, owns list or preview object matches it)` | The cue fires but mounts no slide. A matching element appears only if its slide is mounted at that time. | | A slide owns the cue, but no element, handler, or slide id matches it. | the page, when the cue fires | `cue "" matches no element, handler, or slide` | The cue fires with no visible effect. It still counts toward the mount time of its slide. | | An element's `data-cue` is not in `?cues=`. | the page, when the slide mounts | `data-cue "" is not in ?cues=, so it reveals at its data-delay time after the mount` | The element appears at its `data-delay` time after the slide mounts. | | A slide owns no cue in `?cues=`. | the page, at the start | `slide "" owns no cue in ?cues=, so it never appears` | The slide never mounts. | | No cue in `?cues=` has an owner. | the page, at the start | `no slide owns any listed cue, so nothing will mount` | No slide mounts, and the recording shows an empty stage element. | In each message, `` and `` stand for real ids. The page lists its warnings in `window.__decktalk.warnings`. The recorder logs each one and writes it to the recording log. A handler that `DeckTalk.on` registers for an id stops the second and third warnings for that id. [Cue ownership](/concepts/page-contract#cue-ownership) has the ownership rules. The `align` stage looks for each cue id in the page as a quoted literal, in single quotes, double quotes, or backticks. So `data-cue="1.1bowl"`, a key of a slide's `preview` object, and a key of an `on` object all count. The stage skips clip sections, and it skips a page file that does not exist, because the recorder reports that. The cue still resolves, and `align` still writes `cue-times.json`. These lines come from a copy of the starter whose `data-cue="2.1code"` was renamed `2.1codex` in `deck/index.html`. `align` exits 1. ```text theme={null} sec speech need cues 02 14.6 - 2.1average@1.44,2.1formula@5.67,2.1code@9.43,2.1waited@11.31 ! 2.1code: not in deck/index.html ! 2.1codex: data-cue="2.1codex" in deck/index.html has no entry in cues.json 3 sections with cues, 0 unresolved (estimated words: times are placeholders) ``` To fix an unknown id, add it to the page, or fix the id in `cues.json`. If a page builds its ids at run time, pass `--allow-unknown-cues` to `align`, `preflight`, or `build`. The page warnings are then the only check. ## Elements nobody cued The check runs the other way too, because a cue and the element it reveals are one thing written in two files. `align` reads every `data-cue` attribute in the pages and reports each id that `cues.json` never names, as `UNCUED ELEMENT`, a certain finding. Such an element sits on the slide waiting for a phrase nobody wrote, so the reveal never fires. It is the second line of the sample above, the one that names `2.1codex`. Add the cue to `cues.json`, or drop the attribute from the page. An id that a script builds from a variable, such as `data-cue="${IDS[i]}"`, is not a literal that any reader of the file can resolve, so `align` leaves it to `decktalk preflight`, which reads the page's own catalog. ## Unresolved cues If the `align` stage cannot match a phrase, it notes `phrase not found` and exits 1. These lines come from a copy of the starter whose cue `2.1formula` has the typo "the formular". ```text theme={null} sec speech need cues 02 14.6 - 2.1average@1.44,2.1code@9.43,2.1waited@11.31 ! 2.1formula: phrase not found: 'the formular' 3 sections with cues, 1 unresolved (estimated words: times are placeholders) ``` `decktalk build` stops after the `align` stage, because a slide whose only cues are unresolved never mounts. Fix the phrase, or pass `build --allow-unresolved-cues` to build without the cue. The page then warns that the cue is not in `?cues=`, and its element appears at its `data-delay` time. The `align` command itself has no such flag, because reporting the phrase is its whole job. A cue that resolves past the end of the section's audio stays, with a note. A build without voice estimates word times, so its cue times are estimates too. The table then ends with `(estimated words: times are placeholders)`. ## Phrase choice These guidelines help a reveal start on the right word. [Writing for the ear](/guides/writing-for-the-ear) explains each one. * Start the phrase with the word that names the thing that appears, because the cue time is the start of the first word. A noun such as `box of knobs` works, and so does a verb with its object, such as `steps downhill`. A connective such as "and then" does not. * If a word repeats in the section, use two or three words, or set `occurrence`. Set `occurrence` to 1 to keep the first match and silence the warning. * Set a small positive offset key, 0.1 to 0.2 s, so the viewer hears the word start before the element moves. * Use a negative offset key only when the audience must read along. * Put a `[beat]` or a `[pause N]` before a reveal that should feel like an event. * Cue a summary on `$end` to show it as the voice stops. * Write numbers as words in the script, and show digits on the slide. ## Sound on cues A sound effect in `[[mix.sfx]]` names a `section` and a `cue`. The `assemble` stage places the sound effect at that cue's time. Music marker uses the same `on`, `occurrence`, `case_sensitive`, and `offset` keys to raise or mute the music at a phrase. [Sound](/concepts/sound) explains both. ## Terms | Term | Meaning | | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | [cue](/reference/glossary) | A cue is one entry in `cues.json`. It pairs a cue id with a cue phrase. | | [cue id](/reference/glossary) | A cue id ties a cue to the page. It is a slide id plus a short word, such as `1.1bowl`. | | [cue phrase](/reference/glossary) | The cue phrase is the `on` value: a spoken phrase, `$start`, or `$end`. | | [resolve](/reference/glossary) | The `align` stage resolves a cue phrase to a cue time. | | [cue time](/reference/glossary) | The cue time is the start of the cued word plus the offset key, rounded to hundredths, in seconds after the section start. | | [offset key](/reference/glossary) | The offset key is the cue's `offset` in `cues.json`. A positive value moves the cue time later. | | [spoken words](/reference/glossary) | The spoken words are the words in the section's words file. Matching and `?words=` use them. | ## Next * **Write phrases that time well:** [Writing for the ear](/guides/writing-for-the-ear) * **Look up every key:** [cues.json](/reference/cues-json) * **Learn which slide owns a cue:** [The page contract](/concepts/page-contract#cue-ownership) # How it works Source: https://docs.decktalk.ai/concepts/how-it-works Understand how DeckTalk turns a script and HTML pages into one mp4, and why each reveal starts on its word. `decktalk build` turns your script and pages into one mp4 in five stages. To build your first video, follow the [Quickstart](/quickstart). Four panels. Write shows a markdown script. Narrate runs narrate and align, and shows a tick for every word of "A bowl. A ball. One. Two, three." with 1.25 over "bowl". Record runs record, and shows a slide where a bowl draws on and a ball steps down it. Assemble runs assemble and verify, and shows one mp4. Four panels. Write shows a markdown script. Narrate runs narrate and align, and shows a tick for every word of "A bowl. A ball. One. Two, three." with 1.25 over "bowl". Record runs record, and shows a slide where a bowl draws on and a ball steps down it. Assemble runs assemble and verify, and shows one mp4. 1. You write four files: `script.md`, `decktalk.toml`, `cues.json`, and the page. 2. The speech provider voices the script and returns a start and an end time for every word. 3. Headless Chromium records each page section, and each reveal fires at its cue time. 4. ffmpeg cuts each recording to its section and joins the sections into one mp4. 5. `verify` measures the final mp4, so an early or late reveal shows as a number. ## Stages The five stages always run in this order. Each stage reads files that the stages before it wrote. ```mermaid theme={null} flowchart TB S["script.md"] --> narrate narrate -- "words files, takes.json" --> align C["cues.json"] --> align align -- "cue-times.json" --> record P["deck pages"] --> record record -- "webm and recording log" --> assemble assemble -- "name.mp4, captions, chapters" --> verify ``` | Stage | One job | Writes | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- | | `narrate` | `narrate` voices each section and gets a time for every word. | `build/narration/.mp3`, `.words.json`, `takes.json`, `narration.mp3` | | `align` | `align` turns each cue phrase into a cue time. | `build/cue-times.json` | | `record` | `record` records each page section in headless Chromium, finds narration t=0 in the webm, and flags a recording that is black, short, stalled, or uncovered. | `build/recordings/NN.webm`, `recordings/NN.json` | | `assemble` | `assemble` cuts, joins, and mixes the sections into one mp4. | `build/sections/NN.mp4`, `.mp4`, captions, chapters | | `verify` | `verify` checks section starts, cuts, and cut continuity in the final mp4. | nothing | Each stage is also a command with the same name, and [the CLI reference](/reference/cli) lists them. [Build artifacts](/reference/artifacts) gives the fields of every file. `assemble` also runs the [loudness pass](/concepts/sound#loudness). ## Why the cuts are exact Each reveal starts on its word because DeckTalk finds narration t=0 inside every recording, to the frame. A strip of recorded frames. Three magenta cover frames come first, and a line marks t=0 at the first clean frame. Under the strip, the narration "A bowl. A ball. Watch it step down" starts at t=0. Dashed leads join "bowl" and "ball" to the outlined frames where the bowl and then the ball appear. A strip of recorded frames. Three magenta cover frames come first, and a line marks t=0 at the first clean frame. Under the strip, the narration "A bowl. A ball. Watch it step down" starts at t=0. Dashed leads join "bowl" and "ball" to the outlined frames where the bowl and then the ball appear. | Risk | What DeckTalk does | | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- | | A browser starts to record at an unknown moment. Chromium on Windows starts later than on Linux. | The recorder covers the page in magenta and takes the first frame after the cover as narration t=0. | | Chromium sends a frame only when the page paints. | The cover holds an element that always moves, so frames keep coming while the page is covered. | | t=0 is known only to the nearest frame. | The video runs at 25 fps, the rate that Chromium records at. t=0 is exact to one frame, 40 ms. | | Section lengths add up to fractions of a frame. | `assemble` rounds each section span to whole frames counted from the start of the narration. | | A recording can end before its section span. | `assemble` repeats the last frame to fill the span. `hold_seconds` uses the same repeat. | | Joined audio adds a short delay at each join. | Each `sections/NN.mp4` has no audio. `assemble` mixes the sound once, from zero. | The recorder removes the cover only when the page is ready. [The handshake](/concepts/page-contract#the-handshake) shows each wait. `record` looks for the cover only in the first `[record] cover_scan_seconds` of each recording, 4 s by default. The alignment never depends on wall-clock time. ## The build without voice A build without voice replaces the voice with a click track, so it needs no account and no API key. ```text theme={null} [sil ] 01 26 words -> 12.60s (estimated words, sound ends 12.487, lead 0.5s, tail 1.3s) [sil ] 02 30 words -> 14.20s (estimated words, sound ends 14.087, lead 0.5s, tail 1.3s) [sil ] 03 25 words -> 11.50s (estimated words, sound ends 11.387, lead 0.5s, tail 1.3s) ``` * The click track has a soft click at the start of every estimated word, and one where the last word ends, at -24 dBFS. * `narrate` spaces the estimated words evenly at `silent_words_per_minute`, 150 by default. It adds each pause, beat, and tail. [script.md](/reference/script-md) gives the length rule. * Every other stage runs as usual. A build without voice therefore tests the cues, the pages, the recording, the cut, and the checks. * The `narrate` and `align` tables mark their times as estimated. * The clicks let `verify` compare the picture with the sound in the final mp4. [Verify](/reference/verify#cues) explains the check. * A build without voice skips the loudness pass, so the clicks keep their level. A build without voice stops when the project has voiced takes, because it would empty the narration cache. [A build without voice and a voiced build](/guides/rebuild-one-section#a-build-without-voice-and-a-voiced-build) explains the cost. ## Clips and the narration A clip section plays a video with its own audio. `narrate` and `record` skip it, and the narration pauses for a clip between two page sections. Lanes on one time axis. The narration.mp3 lane holds sections 1, 2, 3, 4, 6, 8, and 9 back to back, with a split before sections 6 and 8. The video lane plays the 4 s clip of section 5 after section 4 and the 5.04 s slate of section 7 after section 6, and dashed leads move sections 6, 8, and 9 later. Captions sit under page sections only. Chapters mark the start of each title, and the five sections titled The edit share one. Lanes on one time axis. The narration.mp3 lane holds sections 1, 2, 3, 4, 6, 8, and 9 back to back, with a split before sections 6 and 8. The video lane plays the 4 s clip of section 5 after section 4 and the 5.04 s slate of section 7 after section 6, and dashed leads move sections 6, 8, and 9 later. Captions sit under page sections only. Chapters mark the start of each title, and the five sections titled The edit share one. [Add a clip section](/guides/clip-section) shows how to place a clip and how the narration splits. ## Caching `narrate` keeps each section's audio and a hash of its text in `build/narration/takes.json`. After an edit, `decktalk build` voices again only the sections whose text changed. `record` keeps each section's recording the same way. The recording log holds `input_hash`, the digest of the page URL with its cues and its spoken words, the frame geometry, the markup of the one scene the section plays, the rest of that page, which every scene shares, and the content of every project file the page loaded, which the log lists under `assets`. A section whose hash has not moved is kept. So an edit inside one scene records the sections that play it and leaves the other sections of that page alone, while an edit to the page's head, its styles or its scripts records every section of it. Replacing a picture the page loads records that section again, although no line of HTML changed. `decktalk build --only N` records section N whether it changed or not. A recording stays valid when a neighbor section changes length, because cue times count from the start of their own section. [Rebuild one section](/guides/rebuild-one-section) says what each edit runs again. ## What verify checks `verify` checks the final mp4 in three ways. * **Section starts:** the frame 0.2 s after each section start has a pixel brighter than luma 60. * **Cuts:** the narration is at or below -40 dBFS in the last 0.15 s before each cut. * **Cues:** the picture changes within 80 ms of each cue time. After a build without voice, it also changes within 120 ms of the click plus the offset key. The verify stage inside `decktalk build` checks section starts and cuts, and cut continuity for a section that sets `seamless`. `decktalk verify` also checks the cues. [Verify](/reference/verify) defines every measurement, limit, and result. ## Terms | Term | Meaning | | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- | | [Stage](/reference/glossary) | One of `narrate`, `align`, `record`, `assemble`, and `verify`, in that order. | | [Build without voice](/reference/glossary) | A build with `--no-voice`. It uses estimated word times and writes a click track. | | [Voiced build](/reference/glossary) | A build without `--no-voice`. The speech provider voices the script. | | [Click track](/reference/glossary) | The audio of a build without voice, with a soft click at every estimated word start and at the end of the last word. | | [Narration t=0](/reference/glossary) | The first frame after the magenta cover, where the page clock starts. | | [Recording](/reference/glossary) | The webm that `record` writes for a page section. | | [Cut](/reference/glossary) | The boundary between two sections in the final video. | ## Next * **Build your first video:** [Quickstart](/quickstart) * **Rebuild after an edit:** [Rebuild one section](/guides/rebuild-one-section) * **Read a verify table:** [Verify](/reference/verify) # The page contract Source: https://docs.decktalk.ai/concepts/page-contract Learn how the recorder plays an HTML page in time with the narration, and which rules a page follows. The page contract is the set of rules that lets the recorder play an HTML page in time with the narration. To write a page, follow [Your first deck](/guides/first-deck). 1. `decktalk-runtime.js` implements the contract. A page can also [follow it by hand](/guides/page-by-hand). 2. A scene is markup: a `[data-scene]` wrapper holding one `