Turn an hour of speech into units that walk back to timestamps
Pin a timestamped transcript by hash, cut it into units from exact spans, and see a proposal that names a line that does not exist refused with its reason.
An hour of speech is not an addressable thing. The chapter’s move is to make it one: a timestamped transcript becomes a hash-pinned source, and a unit is one or more exact spans of it. A proposer — a model, in practice — may suggest which lines form a unit, what role it has and what it depends on. It cannot supply the text: that is always the text at the spans.
Run it
git clone https://github.com/ernanhughes/project-language.git
cd project-language
git checkout 329660f679a5a87831f91fe1430c20ac9360dda8
npm ci
npm run example:talkNo host, no interface and no model are involved. The proposer is a scripted function and the transcript is one invented talk of 17 lines.
Expected outcome
The units are cut from the transcript, and one of them joins minute 4 with minute 53:
...
17 lines, 1286 characters, sha256 1ecff193254759fc…
u-intro other [0:00-0:21]
u-history other [3:15]
u-scope claim [4:02] + [53:20]
u-sync-sense qualifier [4:30]
u-quorum other [5:10]
u-round other [6:00] needs u-quorum
u-proof evidence [12:40]
u-benchmark claim [41:50-42:10] needs u-scope
u-clock-sync evidence [47:30] needs u-scopeA proposal that names a line that does not exist is refused, and so is a unit that depends on it:
...
refused proposals:
x-invented-line UNKNOWN_LINE: no such line: 99
x-needs-it DEPENDENCY_NOT_ACCEPTED: x-invented-lineThe unit’s text is the text at its spans, never what the proposer said:
...
A unit's text is the text at its spans, never what the proposer says. u-scope joins minute 4 and minute 53:
"For the purposes of this talk, a fault means a crash and nothing worse. Good question. This result assumes bounded delays between machines. The title does not say that."The text is derived, never supplied
decompose is where a proposal becomes a unit. The text is sliced from the
source; a proposal naming a missing line, no line, a duplicate id, or a
dependency that is not itself an accepted unit is refused with its reason.
export function decompose(parsed: ParsedTranscript, propose: Proposer): Decomposition {
const proposals = propose(parsed.lines);
const byLine = new Map(parsed.lines.map(l => [l.n, l]));
const units: Unit[] = []; const refused: Decomposition["refused"] = [];
const seen = new Set<string>();
for (const p of proposals) {
if (seen.has(p.id)) { refused.push({ id: p.id, code: "DUPLICATE_ID", detail: p.id }); continue; }
seen.add(p.id);
const ns = [...new Set(p.lines)].sort((a, b) => a - b);
if (ns.length === 0) { refused.push({ id: p.id, code: "NO_LINES", detail: "a unit needs at least one line" }); continue; }
const missing = ns.filter(n => !byLine.has(n));
if (missing.length) { refused.push({ id: p.id, code: "UNKNOWN_LINE", detail: `no such line: ${missing.join(",")}` }); continue; }And the unit’s own validation refuses a unit whose text is not the text at its spans — a property that holds for any unit, not only the fixture:
export function validateUnits(units: readonly Unit[], source: SourceVersion): UnitProblem[] {
const out: UnitProblem[] = [];
const ids = new Set<string>();
if (sha256(source.text) !== source.sha256) out.push({ unit: "*", code: "SOURCE_HASH_MISMATCH", detail: source.id });
for (const u of units) {
if (ids.has(u.id)) out.push({ unit: u.id, code: "DUPLICATE_ID", detail: u.id });
ids.add(u.id);
if (u.source_id !== source.id || u.source_version !== source.version) out.push({ unit: u.id, code: "WRONG_SOURCE", detail: `${u.source_id}@${u.source_version}` });
if (u.spans.length === 0) out.push({ unit: u.id, code: "NO_SPAN", detail: "a unit needs at least one span" });
for (const s of u.spans) {
if (!Number.isInteger(s.start) || !Number.isInteger(s.end) || s.start < 0 || s.end > source.text.length || s.start >= s.end)
out.push({ unit: u.id, code: "BAD_SPAN", detail: JSON.stringify(s) });
}
if (!out.some(p => p.unit === u.id && p.code === "BAD_SPAN") && u.spans.length && sliceOf(source, u) !== u.text)
out.push({ unit: u.id, code: "TEXT_NOT_SPAN", detail: "unit text is not the text at its spans" });
}Mechanism and limitations
Documented: the input format ([m:ss] caption text), the rule that the
proposer cannot supply content, and the “judging pieces one at a time” failure
are stated in the header comment of
core/radar/src/attention/transcript.ts.
Observed: the run above. The transcript parsed to 17 lines and 1,286 characters with a pinned hash; a unit joined two spans 49 minutes apart; two proposals were refused with their reasons; and the joined unit’s text is the source text at those spans.
Not shown. Nothing here finds units: the proposals are scripted. There is no video, no audio and no slides — the input is a text transcript, and nothing is persisted. The local-window scorer in the same file is a deliberately naive baseline used for a constructed contrast; it is not a claim about any system that exists. Timestamps are checked for plausibility, not verified against a recording.
Understand this example
Copy this prompt into your AI tool. No code runs here.
Apply this example
Copy this prompt into your AI tool. No code runs here.