CtrlK
BlogDocsLog inGet started
Tessl Logo
PluginPublic

jbaruch/speaker-toolkit

Eleven skills for conference speakers covering rhetoric analysis, talk authoring, deck building and export, CFP submissions, publishing, illustrations, shownotes, and screencast production, informed by a 113-entry Presentation Patterns taxonomy (83 observable: 64 patterns + 19 antipatterns; 30 unobservable: 21 patterns + 9 antipatterns).

Quality

90%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Low

Low-risk findings worth noting

Overview
Quality
Evals
Security
Files

schemas-obligations.mdskills/vault-ingress/references/

Ingress Run Obligations Schema

{vault_root}/ingress-obligations.json records what each vault-ingress run still owes the speaker after its batches persist. A closed queue claim in tracking-database.json is processing completion; this ledger holds run completion, which arrives only when the clarification offer carries an explicit disposition and the end report has been delivered.

Ownership and Access

  • Owner: vault-ingress. skills/vault-ingress/scripts/run-obligations.py is the only writer and owns every shape change and migration.
  • Readers: vault-ingress Step 1 (pending) and Steps 9 and 11 (status); vault-clarification Step 2 (session-agenda) to resolve the seed agenda of a pending session. vault-profile never reads it.
  • vault-clarification claims the session it runs (claim-session, Step 2) through the owner script, and counts it with count-session (Step 9), which writes the tracking database's counter while holding this ledger's lock. A standalone session then records it through record-session, the same owner command vault-ingress Step 9 uses with the claim token the session returned; the script stays the only writer.
  • Every write goes through the tracking-database io helpers: sibling lock file, exact-generation check, staged candidate, atomic replace. Never edit the ledger by hand.
  • A missing ledger means the vault has not adopted this contract yet: adopt --now at Step 1 creates it and records every claim already closed as history by exact identity. Until then pending reports adopt_required: true and reconciles nothing, and every other command refuses with ledger_not_adopted.
  • A ledger carrying another schema_version is refused with ledger_schema_unsupported; the reader neither repairs nor downgrades it. Update speaker-toolkit, or inspect the file by hand.

Root

FieldTypeMeaning
schema_versioninteger1
adopted_attimestampwhen adopt ran
adopted_factsarraythe exact identity ([run_id, filename, batch_id, generation, released_at]) of every claim already closed at adoption — history, never reconciled; identity rather than time, since persist-results.py --run-date can stamp a batch at midnight
dismissed_runsarray{run_id, dismissed_at, reason, facts} per run the operator chose not to open, facts being the exact identities that dismissal covered
runsarrayone record per run_id, in the order runs were opened

Run Record

FieldTypeMeaning
schema_versioninteger3 — the run record's own version, checked on every read; a 1 or 2 record is read and upgraded (see Migration)
run_idstringthe queue claim's run id (queue-state.py claim --run-id); the same identifier contract as the claim (non-empty, no whitespace), never used raw as a path
opened_attimestampfirst open
updated_attimestamplast command that changed the record
talksarrayevery talk open recorded for the run, sorted by filename
downstreamobject{state: owed | completed, completed_at} — the rendering, summary, profile, and goal steps (Step 4's rendering through Step 8) for the run's talks
clarificationobjectthe offer, its disposition, and the session
end_reportobjectthe delivered report
completed_attimestamp or nullset by record-report; the run is complete

Timestamps are the canonical UTC whole-second ISO-8601 form (2026-09-14T12:00:00+00:00), within years 1 to 9999 once normalized; every command takes them from --now, never from the clock, and stores the normalized form. Every read parses each stored timestamp, requires that canonical form so stamps compare as text, and checks the state-dependent invariants below; a record that breaks one is refused as ledger_invalid naming the field.

Every recording command is replay-safe: repeating it with the inputs it already recorded is an unchanged success carrying replayed: true, so a caller that lost the first response can retry; a different answer to an already-answered question is refused as invalid_transition naming what stands. A replayed open never touches a frozen recency snapshot.

Talk entry

FieldMeaning
filenamethe talk's filename in the tracking database
statusthe talk's status at the time of open, one of the queue contract's known statuses
delivery_datethe talk's date when it is a YYYY-MM-DD string, else null
days_since_deliverywhole days between delivery_date and --now, else null
recency_bucketsame_week, recent, older, or unknown
claim_run_idthe run id of the closed return_persisted claim that persisted the talk: this run's own newest claim when it has one, else the newest of any run; open refuses a talk with no such claim (talk_not_persisted)
claim_batch_idthat claim's batch_id
claim_generationthat claim's reprocess_generation
claim_released_atthat claim's released_at; with claim_run_id, claim_batch_id, and claim_generation it names one persisted fact — persist-results.py stamps one release time on a whole batch — so a talk merged again under the same run is a new fact that re-owes the downstream steps. Newer means a later generation, then a later release; the batch id is identity, never order

Bucket boundaries and the rule that an undated or future-dated talk is unknown are the script's; the reference to its constants lives in clarification-handoff.md.

Downstream steps

open owes them whenever a new talk joins the run; record-downstream, run after Step 8, marks them completed with its --now and is replay-safe. record-offer and record-report refuse (invalid_transition) while they are owed, so a run resumed after a crash between the merge and Step 8 goes back through rendering, summary, profile, and goals before anything is offered or reported.

Clarification

FieldMeaning
stateone of the states below
offer_modeinline, recommend_full, recommend_compressed, or none; how the run's talks decide it is the script's rule, referenced from clarification-handoff.md
recency_as_ofthe --now the stored recency and offer_mode were last computed from; null before the first computation
topicsthe candidate topics recorded with the offer
offered_atwhen the offer was put to the speaker
resolved_atwhen the disposition was recorded
return_conditionthe speaker's words for when a deferred offer is raised again; null otherwise
sessionnull, or {state: pending | running | completed, completed_at, profile_inputs, profile_refreshed, claim_token, claimed_at} once accepted, plus legacy_count on a session upgraded from run-record schema 2 while pending; profile_inputs is changed or unchanged as the session reported it; claim_token and claimed_at name the claim a host holds (claim-session); legacy_count is unknown until reconcile-count records counted or uncounted
StateMeaningSet by
owedat least one analyzed talk; the offer has not been put to the speakeropen
offeredthe offer was put to the speaker and no answer is recordedrecord-offer
acceptedthe speaker accepted; session.state says whether the session finishedrecord-disposition
declinedthe speaker declinedrecord-disposition
deferredthe speaker deferred, with return_condition; answered again when that condition is metrecord-disposition
not_applicablethe run analyzed no talkopen

State-dependent invariants: offered_at is set from offered on and null before; resolved_at is set for accepted, declined, and deferred and null otherwise; return_condition is a non-blank string for deferred and null otherwise; session is an object only for accepted, with completed_at, profile_inputs, and a boolean profile_refreshed set once completed and null otherwise. A running session carries a 32-hex-digit claim_token and its claimed_at; a pending one carries neither; a completed one keeps the claim it completed under, or neither when it completed before claims existed. Whether a session was counted is the tracking database's fact, not the ledger's: its run id in config.clarification_counted_runs, which only count-session writes: it holds this ledger's writer lock from the claim check through the database commit (lock order: ledger, then database), counts a run only while its session is running under the named claim, and never writes the ledger. A schema-2 session whose legacy_count is unknown refuses (session_count_unknown) until reconciled, and one reconciled as counted joins the counted runs without advancing the counter. offer_mode is the one the recorded talks give (run-obligations.py offer_mode_for), and the state is not_applicable exactly when that mode is none.

Transitions: owed → offered → accepted | declined | deferred; an accepted session goes pending → running through claim-session, back to pending through release-session (the operator's decision that the claim's host will not finish), and running → completed through record-session under the same claim once count-session has counted its run; a deferred offer is the one answer that can be given again, deferred → accepted | declined | deferred, when the speaker's return condition is met — the report may already be delivered by then, and a session accepted afterwards is still listed by pending until it completes. not_applicable is terminal. Silence, elapsed time, and an invitation merely sent cause no transition: an offered run stays pending until the speaker answers.

The stored recency is a snapshot, labeled by recency_as_of; a refresh never changes a talk's recorded claim link. open recomputes it from the newest --now while the state is owed or not_applicable; a new fact joining while the offer is offered withdraws the offer back to owed so Step 9 makes it again for the fuller scope, and a new fact cannot join once the offer was answered — it goes under a fresh run id. record-offer recomputes it once more, against the current database and its own --now, at the moment the offer is made, and its output carries the offer_mode the offer must use; a run resumed weeks later never promises an inline session for a talk that is no longer same-week. If that refresh finds no analyzed talk left (a talk was requeued since open), the state is persisted as not_applicable, the command succeeds with offered: false, and nothing is asked; the report is then no longer blocked on an offer. Once offered, the buckets and offer_mode are frozen; later open calls only append talks.

End report

FieldMeaning
stateowed or delivered
delivered_atwhen record-report accepted the delivered text
report_pathingress-reports/{stem}.{sha256}.md, relative to the vault root, the byte-exact copy; stem is the run id with every character outside A-Za-z0-9._- replaced by _, bounded for long ids by the script's REPORT_STEM_* constants, so a ledger-edited id never names a path outside the directory, a long id never exceeds a filename limit, and the full digest keeps two texts from ever sharing a path. Validation recomputes this path from the run id and digest and refuses any other value. Relative, the binding is the same whether a command reached the vault through the ~/.claude symlink or its target
report_sha256digest of the delivered text
reopened_atwhen a clarification session accepted after delivery reported changed profile inputs, sending the run back to owed for a fresh report; null otherwise

Once delivered, path, digest, and delivered_at are set, completed_at equals delivered_at, the downstream steps are completed, and the offer has an answer (any state but owed or offered); while owed, path, digest, delivered_at, and completed_at are null. A record claiming delivery without those is refused as ledger_invalid. A record-session that reports changed profile inputs after the report was delivered — the only way is a deferred offer answered again — resets the report to owed, stamps reopened_at, clears completed_at, and answers report_reopened: true; Step 11 runs again so the report carries the refreshed profile.

record-report refuses (invalid_transition) until the downstream steps are recorded and the clarification is resolved: declined, deferred, not_applicable, or accepted with a completed session. An empty or whitespace-only file is refused (report_empty). The copy is content-addressed and installed with a directory fsync before the ledger commit binds it. A copy the commit then fails to bind is retained: identical bytes always name the same file, so it is shared by every delivery of that text and the retry reuses it; removing it could take a copy out from under a delivery that did bind it. Re-recording identical bytes changes nothing in the ledger but still verifies the copy, recreating one that went missing and syncing its directory; different bytes add a second copy (the earlier one stays on disk) and re-stamp delivered_at. The copy is staged and installed relative to a descriptor opened on the real ingress-reports directory without following links, so a symlink at that path, or anything at the copy path that is not a regular file, is refused (report_copy_failed) and nothing that happens to the path mid-write can redirect the copy outside the vault. The --report-file input is likewise read only as a regular file; a link or a special file is report_unreadable.

Commands

CommandPreconditionEffect
adopt --now—creates the ledger with adopted_at and adopted_facts; replay-safe
open --run-id --now (--talk ... | --talks-from) [--from-run]the ledger is adopted; every talk (from --talk or one per line in the --talks-from file) is a filename in the current tracking database with a closed return_persisted claim (under --from-run when given); a completed run accepts only an exact replay of its recorded facts; a run whose offer was answered accepts no new factcreates or extends the run record; recomputes recency and offer_mode while unoffered; a new fact joining while the offer stands withdraws the offer; owes the downstream steps when a new fact joins
record-downstream --run-id --nowthe run existsdownstream completed; replay-safe
record-offer --run-id --now [--topic ...] [--topics-from]downstream completed; state owedrefreshes recency, then offered with topics and offered: true; or, with no analyzed talk left, not_applicable and offered: false
record-disposition --run-id --now --disposition ... [--return-condition | --return-condition-from]state offered or deferred; deferred needs a non-blank return conditionthe disposition; accepted opens a pending session
claim-session --run-id --now [--claim-token]state accepted, session pending; a running session refuses (session_claimed) unless --claim-token names its claim, which is a replaysession running with a new claim_token, returned as claim_token
release-session --run-id --now --claim-token (--reason | --reason-from)session running under the named claim; another claim refuses (session_claimed), so a retry never frees a claim taken since; pending is a replaysession pending without a claim; returns the released claim
count-session --run-id --claim-tokensession running under that claim (session_claimed otherwise; invalid_transition when unclaimed); legacy_count: unknown refuses (session_count_unknown); a run already counted is a replayunder the ledger's lock, config.clarification_sessions_completed advances (not for a session reconciled counted) and the run joins config.clarification_counted_runs in one tracking-database write; the ledger is unchanged
reconcile-count --run-id --now --claim-token --counted yes|nosession running under that claim with legacy_count: unknown; the same answer is a replay, a different recorded one conflicts, and a session without legacy_count refuses (invalid_transition)legacy_count counted or uncounted
record-session --run-id --now --claim-token --profile-inputs changed|unchanged [--profile-refreshed]session running under that claim (session_claimed otherwise), and its run in config.clarification_counted_runs (session_not_counted otherwise); changed with {vault_root}/speaker-profile.json present needs --profile-refreshed (profile_refresh_required otherwise)session completed with both flags recorded
record-report --run-id --now --report-filedownstream completed; clarification resolved; non-empty filecopies the report, binds its digest, sets completed_at
dismiss --run-id --now (--reason | --reason-from)the ledger is adopted; the run has no record and is listed as unrecorded_runrecords the exact facts listed now as deliberately not opened; a run listed again after an earlier dismissal renews that entry, adding the new facts (renewed: true); with nothing new listed, the same reason is a replay and another reason is refused

Free text — talk filenames, candidate topics, the speaker's reason or return condition — reaches the script through a file (--talks-from, --topics-from, --reason-from, --return-condition-from), one entry per line, never through a shell string. Run ids are chosen by the skill at Step 2 from letters, digits, ., _, and -, so the {run_id} and {iso_timestamp} placeholders in the references need no quoting beyond the double quotes shown. | pending | — | runs owing a step, deferred offers, and uncovered persisted facts | | status --run-id | the ledger is adopted; the run exists | the record and its summary | | session-agenda [--run-id] | with --run-id: the ledger is adopted, the run exists, and its session is pending | the accepted session to run with its seed agenda, or null; read-only |

Every command reads the tracking database through the owner's strict reader and requires the current generation (database_unusable otherwise); the ledger path is derived from the database-bound vault root, which is re-resolved on every re-read and must not move while a command runs (vault_root_changed).

Every mutating command re-reads the tracking database, re-checks the vault root, and re-validates the ledger immediately before it writes. Exit 0 emits one JSON object. A mutating command's object carries written (whether bytes were installed), durability_state (durable, unchanged, or a named degradation such as installed_verification_failed), and warnings; every warning is also printed to stderr. Exit 2 emits {"ok": false, "error", "reason_code"} on stdout and the same message on stderr; a vault-root authority failure carries its own reason_code the same way. A failure in the io layer — an unreadable file, a duplicate JSON key, a lost write race — is reported through that layer's closed diagnostic vocabulary with its typed io reason appended, never the decoder's own text, which can echo the rejected key or value. Reason codes: invalid_arguments, invalid_timestamp, database_unusable, vault_root_changed, ledger_not_adopted, talk_not_found, talk_not_persisted, run_not_found, invalid_transition, session_claimed, session_not_counted, profile_refresh_required, report_unreadable, report_empty, report_copy_failed, ledger_unreadable, ledger_invalid, ledger_schema_unsupported, ledger_write_failed.

Every read validates the whole ledger — required keys, container types, timestamps, state vocabularies, and the state-dependent invariants above — before any command runs; a malformed field is refused as ledger_invalid naming the field, never repaired and never allowed to surface as a traceback.

Reader Contract

pending emits {ok, ledger_path, ledger_present, adopt_required, adopted_at, pending: [...], count, deferred_offers: [...], open_required: [...]}. Each pending entry is a run whose next_action is not none, carrying run_id, opened_at, next_action, downstream_state, clarification_state, offer_mode, recency_as_of, end_report_state, and talk_count. Treat an unoffered entry's offer_mode as the snapshot it is; record-offer returns the mode the offer must use.

deferred_offers lists every run whose offer is deferred, with run_id, return_condition, topics, and resolved_at, so the offer can be raised again when the speaker's condition is met.

open_required reconciles the ledger against the tracking database: a claim closed with release_reason: return_persisted says its talk persisted, and a persisted fact the ledger neither excludes nor covers crashed between the merge and open. A persisted fact is one closed claim: run id, filename, batch id, reprocess generation, and release time. It is excluded when adopted_facts or a dismissal's facts name that exact identity. It is covered when any run record lists the talk linked to that claim, or to a newer claim of the same run (a later generation or release — a run that merged a talk again superseded its earlier result), whichever run id recorded it, so a recovery under a fresh run id is never reported again and a talk merged again under the same run, even within the same second, is a new fact until it is recorded. An uncovered run stays listed until it is opened or dismissed; no later run's existence and no timestamp stands in for either. Each entry carries run_id, talks (only the uncovered ones), latest_released_at, reason, and next_action: open_obligations:

reasonMeaningAction
missing_talksa recorded run's closed claims name talks its record lacks — a later batch that never openedopen the run with exactly those talks
talks_persisted_after_completionthe same, on a run whose report is already deliveredopen them under a fresh run id with --from-run naming the listed run, so the exact fact is covered even when another run merged the talk again since; open refuses a completed run
talks_persisted_after_answerthe same, on a run whose offer was answered but whose report is still owedthe same fresh-run-id recovery; a new fact never joins an answered run
unrecorded_runa run with no record at allopen it with the listed talks (with --from-run when another run merged a talk again since), or dismiss it with a reason; only a run listed here can be dismissed, and a dismissal covers the facts that existed when it was recorded — a fact the run persists afterwards is listed again

The exact coverage predicate is the script's rule — see run-obligations.py, the open_required docstring.

session-agenda [--run-id] emits {ok, ledger_path, ledger_present, adopt_required, adopted_at, session, pending_sessions, running_sessions}. session is null or one accepted session that is pending: run_id, opened_at, offered_at, resolved_at, offer_mode, topics in recorded order, and the run's summary. pending_sessions lists every such run id in selection order; the selection rule is the script's (run-obligations.py, the pending_sessions docstring). running_sessions lists each claimed session's run_id and claimed_at; a claimed session is never selected. With --run-id the named run's session is returned, or the command refuses: ledger_not_adopted, run_not_found, session_claimed when another host holds it, or invalid_transition when that run has no pending session, naming what stands.

next_action is one of:

next_actionResume at
open_obligationsStep 1: open the run with the listed talks
complete_downstream_stepsStep 4's rendering when the batch returns are still on disk, then Steps 5–8, then record-downstream
offer_clarificationStep 9: compute topics and make the offer
await_dispositionStep 9: put the recorded offer to the speaker again and wait
complete_clarification_sessionStep 9: run the accepted session, then record it
deliver_end_reportStep 11: deliver and record the report
nonenothing pending (never listed by pending)

Migration

This release reads ledger schema 1 and run-record schemas 1, 2 and 3, and writes run-record schema 3. Any shape change bumps the affected schema_version and ships its on-read upgrade in skills/vault-ingress/scripts/run-obligations.py in the same change.

Run-record schema 1 recorded end_report.report_path as an absolute path under whichever spelling of the vault root the delivering command was given. Every command reads a schema-1 record as schema 2, its path made vault-relative, so a copy recorded under the ~/.claude symlink or its target binds the same; how the relative path is derived is the script's (_upgrade_legacy_report_path). A recorded path outside the vault, a relative or unresolvable one, or one naming any copy but the bound one is refused (ledger_invalid) with a path-neutral diagnostic naming the recovery. The read that upgrades a record rewrites the whole ledger at the current schema through the same lock and generation check as every owner write, read-only commands included, before the command continues; an older record never outlives the first command to read it.

Run-record schema 2 predates session claims. Every command reads a schema-2 record as schema 3, a session it carries gaining a null claim_token and claimed_at (_upgrade_session_claim): a pending session stays claimable, and one completed before claims stays completed. Schema 2's Step 9 advanced the counter before recording the session, so a pending one may already be counted: it gains legacy_count: unknown, and its count waits for reconcile-count.

The ledger is a gate store, and it claims the rules/stateful-artifacts.md Migration Policy exception for gate stores: an open record refuses actions (an owed report, an unanswered offer, a pending session), so reading a version this script does not accept as "no usable prior state" would silently drop those obligations. A script older than the ledger it reads therefore refuses it (ledger_schema_unsupported for the envelope, ledger_invalid naming the run-record version) and never rewrites it. A version above the one it writes tells the operator to update speaker-toolkit; any other unreadable value, which no speaker-toolkit writes, tells them to inspect the ledger or restore an owner-written copy; every older version it does accept is migrated and rewritten on read, as above.

skills

vault-ingress

SKILL.md

README.md

tile.json