cjm-transcript-decomp-core 0.0.9__tar.gz → 0.0.20__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. {cjm_transcript_decomp_core-0.0.9/cjm_transcript_decomp_core.egg-info → cjm_transcript_decomp_core-0.0.20}/PKG-INFO +63 -6
  2. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/README.md +59 -3
  3. cjm_transcript_decomp_core-0.0.20/cjm_transcript_decomp_core/__init__.py +1 -0
  4. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/alignment.py +50 -2
  5. cjm_transcript_decomp_core-0.0.20/cjm_transcript_decomp_core/cli.py +745 -0
  6. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/graph.py +67 -5
  7. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/models.py +7 -1
  8. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/pipeline.py +108 -23
  9. cjm_transcript_decomp_core-0.0.20/cjm_transcript_decomp_core/respine.py +738 -0
  10. cjm_transcript_decomp_core-0.0.20/cjm_transcript_decomp_core/retire.py +531 -0
  11. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/runs.py +19 -5
  12. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20/cjm_transcript_decomp_core.egg-info}/PKG-INFO +63 -6
  13. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core.egg-info/SOURCES.txt +5 -0
  14. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core.egg-info/requires.txt +3 -2
  15. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/pyproject.toml +1 -1
  16. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/tests/test_alignment.py +38 -0
  17. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/tests/test_batch_logic.py +21 -0
  18. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/tests/test_cli.py +14 -0
  19. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/tests/test_graph.py +73 -0
  20. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/tests/test_pipeline.py +85 -0
  21. cjm_transcript_decomp_core-0.0.20/tests/test_prompt_slots.py +25 -0
  22. cjm_transcript_decomp_core-0.0.20/tests/test_respine.py +293 -0
  23. cjm_transcript_decomp_core-0.0.20/tests/test_retire.py +245 -0
  24. cjm_transcript_decomp_core-0.0.9/cjm_transcript_decomp_core/__init__.py +0 -1
  25. cjm_transcript_decomp_core-0.0.9/cjm_transcript_decomp_core/cli.py +0 -266
  26. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/LICENSE +0 -0
  27. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/MANIFEST.in +0 -0
  28. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/discovery.py +0 -0
  29. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/launch.py +0 -0
  30. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/segments.py +0 -0
  31. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/state.py +0 -0
  32. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core.egg-info/dependency_links.txt +0 -0
  33. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core.egg-info/entry_points.txt +0 -0
  34. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core.egg-info/top_level.txt +0 -0
  35. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/setup.cfg +0 -0
  36. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/tests/test_models.py +0 -0
  37. {cjm_transcript_decomp_core-0.0.9 → cjm_transcript_decomp_core-0.0.20}/tests/test_segment_logic.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cjm-transcript-decomp-core
3
- Version: 0.0.9
3
+ Version: 0.0.20
4
4
  Summary: A frontend-agnostic core for the transcript decomposition workflow — composes isolated capability workers (forced alignment, VAD, graph storage) into a headless pipeline that decomposes transcription run manifests into a VAD-aligned context-graph spine with traceable provenance, and a CLI as its first driver.
5
5
  Author-email: "Christian J. Mills" <9126128+cj-mills@users.noreply.github.com>
6
6
  License: Apache-2.0
@@ -14,8 +14,9 @@ Requires-Dist: cjm_substrate>=0.0.63
14
14
  Requires-Dist: pyyaml
15
15
  Requires-Dist: cjm_context_graph_primitives>=0.0.15
16
16
  Requires-Dist: cjm_capability_primitives>=0.0.11
17
- Requires-Dist: cjm-context-graph-layer>=0.0.11
18
- Requires-Dist: cjm-transcript-graph-schema>=0.0.7
17
+ Requires-Dist: cjm-context-graph-layer>=0.0.13
18
+ Requires-Dist: cjm-transcript-graph-schema>=0.0.11
19
+ Requires-Dist: cjm-transcription-core>=0.0.21
19
20
  Dynamic: license-file
20
21
 
21
22
  # cjm-transcript-decomp-core
@@ -26,7 +27,7 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
26
27
 
27
28
  ## Modules
28
29
 
29
- - **`cjm_transcript_decomp_core`**
30
+ - **`cjm_transcript_decomp_core.__init__`**
30
31
  - **`cjm_transcript_decomp_core.alignment`** — Pure forced-alignment logic (no capability calls): map FA words back to character spans in the original text, assign words to VAD chunks by timestamp, and build one text segment per VAD chunk. Extracted from the page-centric ForcedAlignmentService (Tier-1 logic).
31
32
  - **`cjm_transcript_decomp_core.cli`** — The CLI driver — the decomposition core's first (and currently only) frontend.
32
33
  - **`cjm_transcript_decomp_core.discovery`** — Capability-role discovery by manifest surface match — the journaling-by-
@@ -34,6 +35,8 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
34
35
  - **`cjm_transcript_decomp_core.launch`** — The shared launch surface every decomp shell drives through: the argument
35
36
  - **`cjm_transcript_decomp_core.models`** — Lean data shapes for the transcript-decomposition pipeline: in-core mirrors of the forced-alignment / VAD / text DTOs (no FastHTML deps), run configuration, the committed graph-segment carrier, and the decomposition run manifest (proto-bundle).
36
37
  - **`cjm_transcript_decomp_core.pipeline`** — The headless decomposition pipeline (stage 5: decomp is an EXTENDER). Load a transcription run manifest, verify the transcription-emitted graph root exists (the graph begins at transcription), then per source per pipeline-segment run VAD + per-transcriber forced alignment, build one aligned segment per VAD chunk with per-transcriber text variants, and attach the fine spine under the existing AudioSegment nodes via the layer's idempotent extend_graph — with HITL approval seams between alignment, commit, and the next source.
38
+ - **`cjm_transcript_decomp_core.respine`** — Chunk RESPINE — re-derive ONE coarse chunk of a LIVE spine from a landed external transcript (work item 7a5e9c84; design ruling 0b4d5cfa, amendment 4a7ec4f8).
39
+ - **`cjm_transcript_decomp_core.retire`** — Spine RETIREMENT + COMPACTION — safe removal of superseded decomposition spines (ruling a7617bd4, item eaefebd2).
37
40
  - **`cjm_transcript_decomp_core.runs`** — Run-manifest indexes for the decomp-batch TUI (work item 0ff6bf0f): the
38
41
  - **`cjm_transcript_decomp_core.segments`** — Fine-segment inspection for the decomp TUI (work item 166dd2b8, half a):
39
42
  - **`cjm_transcript_decomp_core.state`** — Sidecar TUI state: last-used batch settings persisted across sessions (the
@@ -45,7 +48,9 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
45
48
  - `assign_words_to_chunks` _function_ — Assign each FA word to a VAD chunk by timestamp overlap.
46
49
  - `build_segments_from_alignment` _function_ — Build a TextSegment per VAD chunk by grouping words by chunk assignment.
47
50
  - `carve_chunks_at_event_spans` _function_ — The event-carve stage (EVENT_SPLIT_POLICY, respine trial DEC 6cc10fb7):
51
+ - `collapse_newlines` _function_ — The stored-text form of a slice that still holds a newline: a retained
48
52
  - `map_fa_words_to_text` _function_ — Map forced-alignment words back to character spans in the original text.
53
+ - `normalize_external_text` _function_ — Offset-preserving fold-input normalisation for EXTERNAL variants (finding
49
54
  - `rescue_gap_words` _function_ — The word-rescue stage (WORD_RESCUE_POLICY, 96edc646 verdict bc7ece7b):
50
55
  - `sentence_end_word_indices` _function_ — Map capability-delivered sentence boundaries onto FA words (B.5: the
51
56
  - `split_chunks_at_sentence_gaps` _function_ — The sentence-split stage (SENTENCE_SPLIT_POLICY, DEC f1024568): refine the
@@ -53,10 +58,13 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
53
58
 
54
59
  ### `cjm_transcript_decomp_core.cli`
55
60
 
61
+ - `backfill_provenance_command` _function_ — Execute `backfill-provenance`: mint the Segment -> Transcript DERIVED_FROM
56
62
  - `build_parser` _function_ — Build the CLI parser (subcommands: run).
57
63
  - `load_capabilities` _function_ — Discover manifests + load each requested capability (default instance).
58
64
  - `main` _function_ — CLI entry point (console script: `cjm-transcript-decomp-core`).
65
+ - `respine_chunk_command` _function_ — Execute `respine-chunk` (work item 7a5e9c84, ruling 0b4d5cfa; the machinery lives
59
66
  - `run_command` _function_ — Execute the `run` subcommand: extend transcription-run manifest(s) with the fine spine.
67
+ - `spine_command` _function_ — The spine retirement + compaction verbs (ruling a7617bd4; the machinery lives in
60
68
 
61
69
  ### `cjm_transcript_decomp_core.discovery`
62
70
 
@@ -67,7 +75,9 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
67
75
 
68
76
  - `SourceVerification` _class_ — Skeptical-lens verification of one Source's fine-spine extension under a
69
77
  - `build_extension_payload` _function_ — Build the fine-spine EXTENSION payload (pure; no capability calls).
78
+ - `provenance_edges_from_segment_wire` _function_ — Derive a Segment's Transcript provenance edges from the node itself:
70
79
  - `resolve_root_ids` _function_ — Recompute the transcription-emitted root node ids from manifest data.
80
+ - `segment_provenance_edges` _function_ — Text provenance as EDGES (finding 89b16be6, the references-must-be-edges
71
81
  - `verify_source` _function_ — Verify a Source's committed extension via server-side AGGREGATES (D13/D19).
72
82
 
73
83
  ### `cjm_transcript_decomp_core.launch`
@@ -108,6 +118,52 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
108
118
  - `submit_and_wait` _function_ — Submit one capability job, wait for it, and return its result (raise on failure).
109
119
  - `vad_chunks_from_result` _function_ — Normalize a typed VAD result into segment-local VAD chunks.
110
120
 
121
+ ### `cjm_transcript_decomp_core.respine`
122
+
123
+ - `bridge_edges` _function_ — Bridge NEXT from the previous live segment to the new first and from the new
124
+ - `build_respine_op` _function_ — ONE replayed property-update op (pure): superseded_by on the old segments, the
125
+ - `chunk_entry_for` _function_ — Resolve ONE coarse chunk of a source entry (pure): by manifest index, or by
126
+ - `classify_dependents` _function_ — Sort the chunk's dependents into the two transferable classes and the rest
127
+ - `decomp_config_from` _function_ — Rebuild the live spine's DecompConfig from its manifest snapshot (pure): the
128
+ - `describe_dependents` _function_ — The refusal / readout listing (pure).
129
+ - `find_decomp_manifest` _function_ — The decomp run that minted the live spine (pure): the newest manifest whose
130
+ - `live_chunk_segments` _function_ — The chunk's LIVE segments: PART_OF its rendition, on the spine, not superseded.
131
+ - `live_spine_index` _function_ — The whole live spine's (id, index) — the renumber plan's input.
132
+ - `plan_renumber` _function_ — The renumber plan (pure; 0b4d5cfa (4), the 'do it properly' ruling): the new
133
+ - `read_dependents` _function_ — What points at the old segments: every Correction with a CORRECTS edge into
134
+ - `render_chunk_prompt` _function_ — Mode one (--prompt): the chunk's escalation prompt WITH CONTEXT — no writes.
135
+ - `resolve_chunk_context` _function_ — Resolve the live spine -> its decomp manifest -> the transcription manifest it
136
+ - `respine_chunk` _function_ — Mode two (--text-file): land -> re-derive the chunk under the live policy ->
137
+ - `respine_op_id` _function_ — The op id `superseded_by` names: a function of (source, landed transcript).
138
+ - `source_entry_for` _function_ — The transcription manifest's entry for a Source, by recomputed identity (pure).
139
+ - `transfer_handler` _function_ — Discover the correction core's chunk-scoped transfer through the entry-point
140
+
141
+ ### `cjm_transcript_decomp_core.retire`
142
+
143
+ - `annotate_spines` _function_ — Mark each spine row with its retirement state (pure; rows copied).
144
+ - `apply_spine_fact` _function_ — Replay handler for spine-retire / spine-compaction / chunk-respine: property merges
145
+ - `compact_retired` _function_ — The compact act over every retired, not-yet-compacted spine of the given sources:
146
+ - `default_live_spine` _function_ — Which spine opens by default (pure). Preference is a DECLARED fact, never creation order:
147
+ - `dependents_free` _function_
148
+ - `dependents_map` _function_ — Every spine's dependents in one raw read (see DEPENDENTS_SQL); a spine absent from
149
+ - `get_source` _function_ — The Source node as a dict (None when absent).
150
+ - `journal_spine_retire` _function_ — Apply + journal one retirement fact (write-side dual of apply_spine_fact).
151
+ - `list_sources` _function_ — Every Source (optionally one collection's members).
152
+ - `list_spines` _function_ — The source's coexisting spines, grouped by skeleton hash and annotated with the retirement map.
153
+ - `live_spines` _function_
154
+ - `plan_retire` _function_ — Rules (a) + successor validation as a pure plan; the graph write is `journal_spine_retire`.
155
+ - `plan_superseded` _function_ — The batch candidate list: every LIVE spine that is not the source's default (rule (c)) —
156
+ - `resolve_source_id` _function_ — Resolve a human selector to ONE Source (refuses with the candidates).
157
+ - `resolve_spine` _function_ — Resolve a picker-style selector to exactly one spine row (pure); refuses with the roster.
158
+ - `retire_spine` _function_ — The whole retire act: list -> plan (rules a) -> dependents gate (rule b) -> journaled fact.
159
+ - `retired_spines` _function_ — The Source's retirement map (pure): {spine key: {reason, successor, actor, ts[, compacted]}}.
160
+ - `source_rendition_ids` _function_ — Every AudioRendition under a Source (all chains — retirement is per skeleton, not per chain).
161
+ - `spine_dependents` _function_ — Rule (b)'s evidence: what on the graph points at the spine's segments.
162
+ - `spine_fact_handlers` _function_
163
+ - `spine_key` _function_
164
+ - `spine_label` _function_
165
+ - `spine_segment_ids` _function_
166
+
111
167
  ### `cjm_transcript_decomp_core.runs`
112
168
 
113
169
  - `DecompIndex` _class_ — Decomp-core run manifests read back: coverage chips for the batch stage
@@ -115,6 +171,7 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
115
171
  - `SourceRunIndex` _class_ — Transcription-core run manifests — the decomp workflow's SOURCES — plus
116
172
  - `TrainingRunIndex` _class_ — Training-run manifests under the workspace training-runs/ dir — the
117
173
  - `group_batches` _function_ — Fold an ordered batch selection into headless hand-off groups.
174
+ - `is_external_transcriber` _function_ — Whether a transcriber name is an external landing's (the `/manual` marker).
118
175
 
119
176
  ### `cjm_transcript_decomp_core.segments`
120
177
 
@@ -140,5 +197,5 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
140
197
 
141
198
  ## Dependencies
142
199
 
143
- **Depends on:** `cjm-capability-primitives`, `cjm-context-graph-layer`, `cjm-context-graph-primitives`, `cjm-substrate`, `cjm-transcript-graph-schema`, `pyyaml`
144
- **Used by:** `cjm-transcript-decomp-qt`, `cjm-transcript-decomp-tui`
200
+ **Depends on:** `cjm-capability-primitives`, `cjm-context-graph-layer`, `cjm-context-graph-primitives`, `cjm-substrate`, `cjm-transcript-graph-schema`, `cjm-transcription-core`, `pyyaml`
201
+ **Used by:** `cjm-transcript-correction-qt`, `cjm-transcript-decomp-qt`
@@ -6,7 +6,7 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
6
6
 
7
7
  ## Modules
8
8
 
9
- - **`cjm_transcript_decomp_core`**
9
+ - **`cjm_transcript_decomp_core.__init__`**
10
10
  - **`cjm_transcript_decomp_core.alignment`** — Pure forced-alignment logic (no capability calls): map FA words back to character spans in the original text, assign words to VAD chunks by timestamp, and build one text segment per VAD chunk. Extracted from the page-centric ForcedAlignmentService (Tier-1 logic).
11
11
  - **`cjm_transcript_decomp_core.cli`** — The CLI driver — the decomposition core's first (and currently only) frontend.
12
12
  - **`cjm_transcript_decomp_core.discovery`** — Capability-role discovery by manifest surface match — the journaling-by-
@@ -14,6 +14,8 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
14
14
  - **`cjm_transcript_decomp_core.launch`** — The shared launch surface every decomp shell drives through: the argument
15
15
  - **`cjm_transcript_decomp_core.models`** — Lean data shapes for the transcript-decomposition pipeline: in-core mirrors of the forced-alignment / VAD / text DTOs (no FastHTML deps), run configuration, the committed graph-segment carrier, and the decomposition run manifest (proto-bundle).
16
16
  - **`cjm_transcript_decomp_core.pipeline`** — The headless decomposition pipeline (stage 5: decomp is an EXTENDER). Load a transcription run manifest, verify the transcription-emitted graph root exists (the graph begins at transcription), then per source per pipeline-segment run VAD + per-transcriber forced alignment, build one aligned segment per VAD chunk with per-transcriber text variants, and attach the fine spine under the existing AudioSegment nodes via the layer's idempotent extend_graph — with HITL approval seams between alignment, commit, and the next source.
17
+ - **`cjm_transcript_decomp_core.respine`** — Chunk RESPINE — re-derive ONE coarse chunk of a LIVE spine from a landed external transcript (work item 7a5e9c84; design ruling 0b4d5cfa, amendment 4a7ec4f8).
18
+ - **`cjm_transcript_decomp_core.retire`** — Spine RETIREMENT + COMPACTION — safe removal of superseded decomposition spines (ruling a7617bd4, item eaefebd2).
17
19
  - **`cjm_transcript_decomp_core.runs`** — Run-manifest indexes for the decomp-batch TUI (work item 0ff6bf0f): the
18
20
  - **`cjm_transcript_decomp_core.segments`** — Fine-segment inspection for the decomp TUI (work item 166dd2b8, half a):
19
21
  - **`cjm_transcript_decomp_core.state`** — Sidecar TUI state: last-used batch settings persisted across sessions (the
@@ -25,7 +27,9 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
25
27
  - `assign_words_to_chunks` _function_ — Assign each FA word to a VAD chunk by timestamp overlap.
26
28
  - `build_segments_from_alignment` _function_ — Build a TextSegment per VAD chunk by grouping words by chunk assignment.
27
29
  - `carve_chunks_at_event_spans` _function_ — The event-carve stage (EVENT_SPLIT_POLICY, respine trial DEC 6cc10fb7):
30
+ - `collapse_newlines` _function_ — The stored-text form of a slice that still holds a newline: a retained
28
31
  - `map_fa_words_to_text` _function_ — Map forced-alignment words back to character spans in the original text.
32
+ - `normalize_external_text` _function_ — Offset-preserving fold-input normalisation for EXTERNAL variants (finding
29
33
  - `rescue_gap_words` _function_ — The word-rescue stage (WORD_RESCUE_POLICY, 96edc646 verdict bc7ece7b):
30
34
  - `sentence_end_word_indices` _function_ — Map capability-delivered sentence boundaries onto FA words (B.5: the
31
35
  - `split_chunks_at_sentence_gaps` _function_ — The sentence-split stage (SENTENCE_SPLIT_POLICY, DEC f1024568): refine the
@@ -33,10 +37,13 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
33
37
 
34
38
  ### `cjm_transcript_decomp_core.cli`
35
39
 
40
+ - `backfill_provenance_command` _function_ — Execute `backfill-provenance`: mint the Segment -> Transcript DERIVED_FROM
36
41
  - `build_parser` _function_ — Build the CLI parser (subcommands: run).
37
42
  - `load_capabilities` _function_ — Discover manifests + load each requested capability (default instance).
38
43
  - `main` _function_ — CLI entry point (console script: `cjm-transcript-decomp-core`).
44
+ - `respine_chunk_command` _function_ — Execute `respine-chunk` (work item 7a5e9c84, ruling 0b4d5cfa; the machinery lives
39
45
  - `run_command` _function_ — Execute the `run` subcommand: extend transcription-run manifest(s) with the fine spine.
46
+ - `spine_command` _function_ — The spine retirement + compaction verbs (ruling a7617bd4; the machinery lives in
40
47
 
41
48
  ### `cjm_transcript_decomp_core.discovery`
42
49
 
@@ -47,7 +54,9 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
47
54
 
48
55
  - `SourceVerification` _class_ — Skeptical-lens verification of one Source's fine-spine extension under a
49
56
  - `build_extension_payload` _function_ — Build the fine-spine EXTENSION payload (pure; no capability calls).
57
+ - `provenance_edges_from_segment_wire` _function_ — Derive a Segment's Transcript provenance edges from the node itself:
50
58
  - `resolve_root_ids` _function_ — Recompute the transcription-emitted root node ids from manifest data.
59
+ - `segment_provenance_edges` _function_ — Text provenance as EDGES (finding 89b16be6, the references-must-be-edges
51
60
  - `verify_source` _function_ — Verify a Source's committed extension via server-side AGGREGATES (D13/D19).
52
61
 
53
62
  ### `cjm_transcript_decomp_core.launch`
@@ -88,6 +97,52 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
88
97
  - `submit_and_wait` _function_ — Submit one capability job, wait for it, and return its result (raise on failure).
89
98
  - `vad_chunks_from_result` _function_ — Normalize a typed VAD result into segment-local VAD chunks.
90
99
 
100
+ ### `cjm_transcript_decomp_core.respine`
101
+
102
+ - `bridge_edges` _function_ — Bridge NEXT from the previous live segment to the new first and from the new
103
+ - `build_respine_op` _function_ — ONE replayed property-update op (pure): superseded_by on the old segments, the
104
+ - `chunk_entry_for` _function_ — Resolve ONE coarse chunk of a source entry (pure): by manifest index, or by
105
+ - `classify_dependents` _function_ — Sort the chunk's dependents into the two transferable classes and the rest
106
+ - `decomp_config_from` _function_ — Rebuild the live spine's DecompConfig from its manifest snapshot (pure): the
107
+ - `describe_dependents` _function_ — The refusal / readout listing (pure).
108
+ - `find_decomp_manifest` _function_ — The decomp run that minted the live spine (pure): the newest manifest whose
109
+ - `live_chunk_segments` _function_ — The chunk's LIVE segments: PART_OF its rendition, on the spine, not superseded.
110
+ - `live_spine_index` _function_ — The whole live spine's (id, index) — the renumber plan's input.
111
+ - `plan_renumber` _function_ — The renumber plan (pure; 0b4d5cfa (4), the 'do it properly' ruling): the new
112
+ - `read_dependents` _function_ — What points at the old segments: every Correction with a CORRECTS edge into
113
+ - `render_chunk_prompt` _function_ — Mode one (--prompt): the chunk's escalation prompt WITH CONTEXT — no writes.
114
+ - `resolve_chunk_context` _function_ — Resolve the live spine -> its decomp manifest -> the transcription manifest it
115
+ - `respine_chunk` _function_ — Mode two (--text-file): land -> re-derive the chunk under the live policy ->
116
+ - `respine_op_id` _function_ — The op id `superseded_by` names: a function of (source, landed transcript).
117
+ - `source_entry_for` _function_ — The transcription manifest's entry for a Source, by recomputed identity (pure).
118
+ - `transfer_handler` _function_ — Discover the correction core's chunk-scoped transfer through the entry-point
119
+
120
+ ### `cjm_transcript_decomp_core.retire`
121
+
122
+ - `annotate_spines` _function_ — Mark each spine row with its retirement state (pure; rows copied).
123
+ - `apply_spine_fact` _function_ — Replay handler for spine-retire / spine-compaction / chunk-respine: property merges
124
+ - `compact_retired` _function_ — The compact act over every retired, not-yet-compacted spine of the given sources:
125
+ - `default_live_spine` _function_ — Which spine opens by default (pure). Preference is a DECLARED fact, never creation order:
126
+ - `dependents_free` _function_
127
+ - `dependents_map` _function_ — Every spine's dependents in one raw read (see DEPENDENTS_SQL); a spine absent from
128
+ - `get_source` _function_ — The Source node as a dict (None when absent).
129
+ - `journal_spine_retire` _function_ — Apply + journal one retirement fact (write-side dual of apply_spine_fact).
130
+ - `list_sources` _function_ — Every Source (optionally one collection's members).
131
+ - `list_spines` _function_ — The source's coexisting spines, grouped by skeleton hash and annotated with the retirement map.
132
+ - `live_spines` _function_
133
+ - `plan_retire` _function_ — Rules (a) + successor validation as a pure plan; the graph write is `journal_spine_retire`.
134
+ - `plan_superseded` _function_ — The batch candidate list: every LIVE spine that is not the source's default (rule (c)) —
135
+ - `resolve_source_id` _function_ — Resolve a human selector to ONE Source (refuses with the candidates).
136
+ - `resolve_spine` _function_ — Resolve a picker-style selector to exactly one spine row (pure); refuses with the roster.
137
+ - `retire_spine` _function_ — The whole retire act: list -> plan (rules a) -> dependents gate (rule b) -> journaled fact.
138
+ - `retired_spines` _function_ — The Source's retirement map (pure): {spine key: {reason, successor, actor, ts[, compacted]}}.
139
+ - `source_rendition_ids` _function_ — Every AudioRendition under a Source (all chains — retirement is per skeleton, not per chain).
140
+ - `spine_dependents` _function_ — Rule (b)'s evidence: what on the graph points at the spine's segments.
141
+ - `spine_fact_handlers` _function_
142
+ - `spine_key` _function_
143
+ - `spine_label` _function_
144
+ - `spine_segment_ids` _function_
145
+
91
146
  ### `cjm_transcript_decomp_core.runs`
92
147
 
93
148
  - `DecompIndex` _class_ — Decomp-core run manifests read back: coverage chips for the batch stage
@@ -95,6 +150,7 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
95
150
  - `SourceRunIndex` _class_ — Transcription-core run manifests — the decomp workflow's SOURCES — plus
96
151
  - `TrainingRunIndex` _class_ — Training-run manifests under the workspace training-runs/ dir — the
97
152
  - `group_batches` _function_ — Fold an ordered batch selection into headless hand-off groups.
153
+ - `is_external_transcriber` _function_ — Whether a transcriber name is an external landing's (the `/manual` marker).
98
154
 
99
155
  ### `cjm_transcript_decomp_core.segments`
100
156
 
@@ -120,5 +176,5 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
120
176
 
121
177
  ## Dependencies
122
178
 
123
- **Depends on:** `cjm-capability-primitives`, `cjm-context-graph-layer`, `cjm-context-graph-primitives`, `cjm-substrate`, `cjm-transcript-graph-schema`, `pyyaml`
124
- **Used by:** `cjm-transcript-decomp-qt`, `cjm-transcript-decomp-tui`
179
+ **Depends on:** `cjm-capability-primitives`, `cjm-context-graph-layer`, `cjm-context-graph-primitives`, `cjm-substrate`, `cjm-transcript-graph-schema`, `cjm-transcription-core`, `pyyaml`
180
+ **Used by:** `cjm-transcript-correction-qt`, `cjm-transcript-decomp-qt`
@@ -0,0 +1 @@
1
+ __version__ = "0.0.20"
@@ -16,7 +16,50 @@ _PUNCT_RE = re.compile(r"[^\w\s]", re.UNICODE)
16
16
  # authoritative text — the v1-v3 token heuristic (closer/abbreviation/stub
17
17
  # lists) is RETIRED. The segmenter's identity (capability name + config hash)
18
18
  # joins the skeleton-identity composite beside this tag.
19
- SENTENCE_SPLIT_POLICY = "sentence-split/capability"
19
+ # 'capability-v2' (finding efe88f17): the segmenter now reads EXTERNAL variants
20
+ # through `normalize_external_text` — a pasted landing's wordwrap newlines
21
+ # become spaces before the split, so pySBD (which splits at EVERY newline) no
22
+ # longer cuts the skeleton mid-sentence; a paragraph break keeps ONE newline
23
+ # and stays a boundary. Local transcriber text is untouched (it carries no
24
+ # newlines). The split input changed for those variants, so the tag bumps.
25
+ SENTENCE_SPLIT_POLICY = "sentence-split/capability-v2"
26
+
27
+ # A newline run: its LAST newline is retained for a paragraph break; everything
28
+ # else in the run becomes a space (length kept).
29
+ _NEWLINE_RUN_RE = re.compile(r"[\r\n]+")
30
+ _NEWLINE_INSIDE_RE = re.compile(r"[ \t]*[\r\n]+[ \t]*")
31
+
32
+
33
+ def normalize_external_text(
34
+ text: str, # An operator-landed external variant's text, verbatim as landed
35
+ ) -> str: # The fold-input form: same length, wraps -> spaces, paragraph breaks -> one retained newline
36
+ """Offset-preserving fold-input normalisation for EXTERNAL variants (finding
37
+ efe88f17): the AI Studio 'Copy as text' gesture hard-wraps the transcript, and
38
+ pySBD splits at every newline — so a wrap mid-sentence became a sentence end
39
+ and a skeleton cut. Every newline run becomes spaces, except that a run
40
+ holding TWO OR MORE '\\n' (a paragraph / speaker break the model wrote on
41
+ purpose) keeps ONE '\\n' as its last character so the segmenter still sees
42
+ the boundary. '\\r' counts as whitespace of the run. The length never changes,
43
+ so every char-slice ref into the verbatim Transcript text stays valid; the
44
+ Transcript node itself is never rewritten — this is the fold's reading of it."""
45
+ def _run(m: "re.Match[str]") -> str:
46
+ run = m.group(0)
47
+ if run.count("\n") >= 2:
48
+ return " " * (len(run) - 1) + "\n"
49
+ return " " * len(run)
50
+ return _NEWLINE_RUN_RE.sub(_run, text)
51
+
52
+
53
+ def collapse_newlines(
54
+ text: str, # A segment's sliced text (may still hold a retained paragraph newline)
55
+ ) -> str: # The same words, every newline run (with its flanking blanks) as one space
56
+ """The stored-text form of a slice that still holds a newline: a retained
57
+ paragraph break inside a segment (the split refused the cut, or no split ran)
58
+ is presentation, not content — it collapses to one space so the correction
59
+ app's <pre> walk lane never renders a ragged block. Slice refs are untouched
60
+ (they index the verbatim Transcript text, not this string)."""
61
+ return _NEWLINE_INSIDE_RE.sub(" ", text).strip()
62
+
20
63
 
21
64
  # Event-carve policy tag (respine trial DEC 6cc10fb7): versioned for the same
22
65
  # reason — it is a SKELETON IDENTITY input. 'v1' = cut-don't-label: model event
@@ -180,11 +223,14 @@ def build_segments_from_alignment(
180
223
  num_chunks: int, # Total number of VAD chunks
181
224
  source_id: Optional[str] = None, # Source row id for traceability
182
225
  source_provider_id: Optional[str] = None, # Source provider identifier
226
+ collapse_newlines_in_text: bool = False, # External variants (efe88f17): a newline still inside a slice -> one space in the stored text
183
227
  ) -> List[TextSegment]: # One segment per VAD chunk
184
228
  """Build a TextSegment per VAD chunk by grouping words by chunk assignment.
185
229
 
186
230
  Each chunk's text is the original (punctuated) slice from the first to the
187
- last word assigned to it; chunks with no words become empty segments.
231
+ last word assigned to it; chunks with no words become empty segments. With
232
+ `collapse_newlines_in_text` (external variants) the STORED text collapses any
233
+ newline run to one space — the slice refs stay the verbatim offsets.
188
234
  """
189
235
  chunk_spans: Dict[int, List[Tuple[int, int]]] = {}
190
236
  for span, chunk_idx in zip(spans, assignments):
@@ -197,6 +243,8 @@ def build_segments_from_alignment(
197
243
  seg_start = word_spans[0][0]
198
244
  seg_end = word_spans[-1][1]
199
245
  seg_text = text[seg_start:seg_end].strip()
246
+ if collapse_newlines_in_text:
247
+ seg_text = collapse_newlines(seg_text)
200
248
  else:
201
249
  seg_text = ""
202
250
  seg_start = None