cjm-transcript-decomp-core 0.0.10__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.10/cjm_transcript_decomp_core.egg-info → cjm_transcript_decomp_core-0.0.20}/PKG-INFO +59 -5
  2. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/README.md +55 -2
  3. cjm_transcript_decomp_core-0.0.20/cjm_transcript_decomp_core/__init__.py +1 -0
  4. {cjm_transcript_decomp_core-0.0.10 → 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.10 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/graph.py +10 -3
  7. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/models.py +7 -1
  8. {cjm_transcript_decomp_core-0.0.10 → 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.10 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/runs.py +19 -5
  12. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20/cjm_transcript_decomp_core.egg-info}/PKG-INFO +59 -5
  13. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core.egg-info/SOURCES.txt +5 -0
  14. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core.egg-info/requires.txt +3 -2
  15. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/pyproject.toml +1 -1
  16. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/tests/test_alignment.py +38 -0
  17. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/tests/test_batch_logic.py +21 -0
  18. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/tests/test_cli.py +14 -0
  19. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/tests/test_graph.py +41 -0
  20. {cjm_transcript_decomp_core-0.0.10 → 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.10/cjm_transcript_decomp_core/__init__.py +0 -1
  25. cjm_transcript_decomp_core-0.0.10/cjm_transcript_decomp_core/cli.py +0 -369
  26. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/LICENSE +0 -0
  27. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/MANIFEST.in +0 -0
  28. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/discovery.py +0 -0
  29. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/launch.py +0 -0
  30. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/segments.py +0 -0
  31. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/cjm_transcript_decomp_core/state.py +0 -0
  32. {cjm_transcript_decomp_core-0.0.10 → 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.10 → 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.10 → 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.10 → cjm_transcript_decomp_core-0.0.20}/setup.cfg +0 -0
  36. {cjm_transcript_decomp_core-0.0.10 → cjm_transcript_decomp_core-0.0.20}/tests/test_models.py +0 -0
  37. {cjm_transcript_decomp_core-0.0.10 → 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.10
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
@@ -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
@@ -57,7 +62,9 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
57
62
  - `build_parser` _function_ — Build the CLI parser (subcommands: run).
58
63
  - `load_capabilities` _function_ — Discover manifests + load each requested capability (default instance).
59
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
60
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
61
68
 
62
69
  ### `cjm_transcript_decomp_core.discovery`
63
70
 
@@ -111,6 +118,52 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
111
118
  - `submit_and_wait` _function_ — Submit one capability job, wait for it, and return its result (raise on failure).
112
119
  - `vad_chunks_from_result` _function_ — Normalize a typed VAD result into segment-local VAD chunks.
113
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
+
114
167
  ### `cjm_transcript_decomp_core.runs`
115
168
 
116
169
  - `DecompIndex` _class_ — Decomp-core run manifests read back: coverage chips for the batch stage
@@ -118,6 +171,7 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
118
171
  - `SourceRunIndex` _class_ — Transcription-core run manifests — the decomp workflow's SOURCES — plus
119
172
  - `TrainingRunIndex` _class_ — Training-run manifests under the workspace training-runs/ dir — the
120
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).
121
175
 
122
176
  ### `cjm_transcript_decomp_core.segments`
123
177
 
@@ -143,5 +197,5 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
143
197
 
144
198
  ## Dependencies
145
199
 
146
- **Depends on:** `cjm-capability-primitives`, `cjm-context-graph-layer`, `cjm-context-graph-primitives`, `cjm-substrate`, `cjm-transcript-graph-schema`, `pyyaml`
147
- **Used by:** `cjm-transcript-decomp-qt`
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`
@@ -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
@@ -37,7 +41,9 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
37
41
  - `build_parser` _function_ — Build the CLI parser (subcommands: run).
38
42
  - `load_capabilities` _function_ — Discover manifests + load each requested capability (default instance).
39
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
40
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
41
47
 
42
48
  ### `cjm_transcript_decomp_core.discovery`
43
49
 
@@ -91,6 +97,52 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
91
97
  - `submit_and_wait` _function_ — Submit one capability job, wait for it, and return its result (raise on failure).
92
98
  - `vad_chunks_from_result` _function_ — Normalize a typed VAD result into segment-local VAD chunks.
93
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
+
94
146
  ### `cjm_transcript_decomp_core.runs`
95
147
 
96
148
  - `DecompIndex` _class_ — Decomp-core run manifests read back: coverage chips for the batch stage
@@ -98,6 +150,7 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
98
150
  - `SourceRunIndex` _class_ — Transcription-core run manifests — the decomp workflow's SOURCES — plus
99
151
  - `TrainingRunIndex` _class_ — Training-run manifests under the workspace training-runs/ dir — the
100
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).
101
154
 
102
155
  ### `cjm_transcript_decomp_core.segments`
103
156
 
@@ -123,5 +176,5 @@ A frontend-agnostic core for the transcript decomposition workflow — composes
123
176
 
124
177
  ## Dependencies
125
178
 
126
- **Depends on:** `cjm-capability-primitives`, `cjm-context-graph-layer`, `cjm-context-graph-primitives`, `cjm-substrate`, `cjm-transcript-graph-schema`, `pyyaml`
127
- **Used by:** `cjm-transcript-decomp-qt`
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