feat: text_split primitive (WO27 Part 1a) - #13
Merged
Merged
Conversation
Implements text_split(text: Text, sep: Text) -> List Text with
non-overlapping left-to-right substring matching, preserving leading,
trailing, and repeated empty pieces (e.g. text_split("aaa", "aa") ==
["", "a"]). A directly written empty separator is checker error H0636;
a runtime-computed empty separator raises TextSplitError.SepEmpty through
normal try/fail. Only a directly written non-empty Text literal separator
is exempt from try.
Notes: the canonical escape/nesting-aware call-argument scanner was pulled
forward (needed by text_split(line, ", ")); the temporary wrappers in
full_type_check.rs and run.rs are removed in Part 1b. Owned copies per piece
are deliberate performance debt (researcher ledger PD-001), not a settled
optimum.
Evidence: splitter 4/4, text_split 14/14, literal recognizer 1/1, focused
catalog 15/15; check_all.ps1 Session AB covers checker acceptance, repeated
separators, the aaa/aa edge, H0636, and H0901 JSON output.
Flip 0023 to accepted 2026-09-22, BDFL ruling on the review at da58a58. Batched into the Part 1a PR deliberately: docs/decisions is an unregistered path and would route a standalone PR at Full (~40 min) for a one-line status change. This PR already pays Full because it adds files.
Part 1a's Session AB block (WO27 text_split evidence) added to Invoke-HumCompilerCorpusChecks, and the 90->92 diagnostic catalog count change touched Invoke-HumCompilerFrontChecks. Both function bodies changed, so their SHA-256 pins are updated to the new digests. The other two pins are unchanged. This pin update is the deliberate acknowledgment of the Session AB addition, not a weakening of the shared-body protection.
undergroundrap
marked this pull request as ready for review
September 23, 2026 04:47
undergroundrap
added a commit
that referenced
this pull request
Sep 23, 2026
Run 35918468834's enriched stderr (read by Claude) gave the real cause of the Windows Session AG failure: hum run of the APP entry is refused by the full type-check gate (recognized_core_body_type_gate_v0: blocked_by_unchecked_body_types_v0, unchecked_statements=10, execution_ready=0). The unchecked statements are the for_each headers (iterator_type_checking_not_implemented, friction ledger #13). The --entry runs bypass the gate and Linux fails earlier on files_read, so this was never visible. Consequence: wordfreq has never run end-to-end as an app on any platform. The mixed-separator fixture-path hypothesis is disproven by that stderr and recorded as such in the ledger and the code comment; per-segment joins stay as canonical form only. Session AG Windows block now pins the actual fail-closed behaviour (exit 1 with blocked_by_unchecked_body_types_v0), labelled a known limitation gated on ledger #13, with enriched stderr/stdout in every failure message. The ungranted-path probe pins the same gate refusal: the gate preempts capability enforcement, so no typed FileReadError.denied is observable yet. WO28 orders #13 (type for-each loop variables) first with done-condition 'wordfreq runs end-to-end on Windows', flipping this assertion back to the byte-exact success check. Loop typing is out of WO27 scope, not implemented here. Ledger #13 is now BLOCKING (not a workaround): the helper-task workaround unblocked the --entry paths only; the app path runs nowhere today. Invoke-HumCompilerCorpusChecks digest pin updated to b8db419b (body change is exactly the assertion flip; thresholds and helpers unchanged).
undergroundrap
added a commit
that referenced
this pull request
Sep 23, 2026
Run 35918468834's enriched stderr (read by Claude) gave the real cause of the Windows Session AG failure: hum run of the APP entry is refused by the full type-check gate (recognized_core_body_type_gate_v0: blocked_by_unchecked_body_types_v0, unchecked_statements=10, execution_ready=0). The unchecked statements are the for_each headers (iterator_type_checking_not_implemented, friction ledger #13). The --entry runs bypass the gate and Linux fails earlier on files_read, so this was never visible. Consequence: wordfreq has never run end-to-end as an app on any platform. The mixed-separator fixture-path hypothesis is disproven by that stderr and recorded as such in the ledger and the code comment; per-segment joins stay as canonical form only. Session AG Windows block now pins the actual fail-closed behaviour (exit 1 with blocked_by_unchecked_body_types_v0), labelled a known limitation gated on ledger #13, with enriched stderr/stdout in every failure message. The ungranted-path probe pins the same gate refusal: the gate preempts capability enforcement, so no typed FileReadError.denied is observable yet. WO28 orders #13 (type for-each loop variables) first with done-condition 'wordfreq runs end-to-end on Windows', flipping this assertion back to the byte-exact success check. Loop typing is out of WO27 scope, not implemented here. Ledger #13 is now BLOCKING (not a workaround): the helper-task workaround unblocked the --entry paths only; the app path runs nowhere today. Invoke-HumCompilerCorpusChecks digest pin updated to b8db419b (body change is exactly the assertion flip; thresholds and helpers unchanged).
undergroundrap
added a commit
that referenced
this pull request
Sep 23, 2026
Run 35918468834's enriched stderr (read by Claude) gave the real cause of the Windows Session AG failure: hum run of the APP entry is refused by the full type-check gate (recognized_core_body_type_gate_v0: blocked_by_unchecked_body_types_v0, unchecked_statements=10, execution_ready=0). The unchecked statements are the for_each headers (iterator_type_checking_not_implemented, friction ledger #13). The --entry runs bypass the gate and Linux fails earlier on files_read, so this was never visible. Consequence: wordfreq has never run end-to-end as an app on any platform. The mixed-separator fixture-path hypothesis is disproven by that stderr and recorded as such in the ledger and the code comment; per-segment joins stay as canonical form only. Session AG Windows block now pins the actual fail-closed behaviour (exit 1 with blocked_by_unchecked_body_types_v0), labelled a known limitation gated on ledger #13, with enriched stderr/stdout in every failure message. The ungranted-path probe pins the same gate refusal: the gate preempts capability enforcement, so no typed FileReadError.denied is observable yet. WO28 orders #13 (type for-each loop variables) first with done-condition 'wordfreq runs end-to-end on Windows', flipping this assertion back to the byte-exact success check. Loop typing is out of WO27 scope, not implemented here. Ledger #13 is now BLOCKING (not a workaround): the helper-task workaround unblocked the --entry paths only; the app path runs nowhere today. Invoke-HumCompilerCorpusChecks digest pin updated to b8db419b (body change is exactly the assertion flip; thresholds and helpers unchanged).
undergroundrap
added a commit
that referenced
this pull request
Sep 24, 2026
WO27 closes with the program written and app execution gated on friction ledger #13 (for-each loop-variable types). Never claims wordfreq delivered; frequency summary stays blocked on ledger #5 / decision 0028. WO28 issues as the active Work Order with the dependency order #13, #4, #15, #7, then decision 0028 (uint_to_text / int_to_text). #15 records the confirmed fail-closed gap from the 2026-09-23 probe: hum check accepts contract-only list_count in task bodies at module and app scope while hum run traps at runtime; the general fix is a typed checker diagnostic, not list_count special-casing. Local evidence: check_all.ps1 -EvidenceTier Language green, test_ci_policy 429 assertions green, public readiness + text hygiene green, cargo fmt and clippy clean.
undergroundrap
added a commit
that referenced
this pull request
Sep 24, 2026
…ummary item Pre-issuance review correction: decision 0028's 'Correction: C alone solves wordfreq's output' says output composes by sequential writes, so uint_to_text/int_to_text DO enable printing word: count lines; the prior draft wrongly claimed concatenation was required. Concatenation is needed only for text as a VALUE. What still blocks a real frequency summary is computing per-word counts without a map type: quadratic nested loops (0028 out of scope), which also need #13. Adds the optional final item: once #13 and 0028 land, wordfreq prints a word: count summary using nested loops with honestly declared quadratic cost, or a ledger entry if it proves awkward.
undergroundrap
added a commit
that referenced
this pull request
Sep 24, 2026
WO27 closes with the program written and app execution gated on friction ledger #13 (for-each loop-variable types). Never claims wordfreq delivered; frequency summary stays blocked on ledger #5 / decision 0028. WO28 issues as the active Work Order with the dependency order #13, #4, #15, #7, then decision 0028 (uint_to_text / int_to_text). #15 records the confirmed fail-closed gap from the 2026-09-23 probe: hum check accepts contract-only list_count in task bodies at module and app scope while hum run traps at runtime; the general fix is a typed checker diagnostic, not list_count special-casing. Local evidence: check_all.ps1 -EvidenceTier Language green, test_ci_policy 429 assertions green, public readiness + text hygiene green, cargo fmt and clippy clean.
undergroundrap
added a commit
that referenced
this pull request
Sep 24, 2026
…ummary item Pre-issuance review correction: decision 0028's 'Correction: C alone solves wordfreq's output' says output composes by sequential writes, so uint_to_text/int_to_text DO enable printing word: count lines; the prior draft wrongly claimed concatenation was required. Concatenation is needed only for text as a VALUE. What still blocks a real frequency summary is computing per-word counts without a map type: quadratic nested loops (0028 out of scope), which also need #13. Adds the optional final item: once #13 and 0028 land, wordfreq prints a word: count summary using nested loops with honestly declared quadratic cost, or a ledger entry if it proves awkward.
undergroundrap
added a commit
that referenced
this pull request
Sep 24, 2026
…13) for_each_binding types the loop variable from the iterated expression: for each word in words where words: List Text binds word: Text (accepted_for_each_binding_v0). Non-list and unprovable shapes fail closed into the existing unchecked path (iterator_type_checking_not_implemented). Two adjacent completions the acceptance command required to open the full-type gate for wordfreq's actual code: list_len(...) call expressions type as UInt in return position, and test_expectation accepts when the callee's declared return type matches the returns literal shape (accepted_test_expectation_v0; mismatches stay unchecked, fail-closed). No new language surface: all three follow the existing semantic rules. Collateral fix: place_type_fact no longer snake-normalizes a whole condition expression to a bound name. if piece != "" with piece: Text was rejected (the normalized text collided with the bound name and returned Text before condition inference could return Bool); it is now accepted via the inferred Bool. Plain-name fallback is unchanged. Tests: five focused unit tests (positive + fail-closed for for-each binding, the condition-name guard, matching/mismatched expectations). Root suite: 507 passed. clippy/fmt/hygiene/readiness clean. Session AG: flipped back to the byte-exact stdout success check (exit 0, exactly 13 bytes, three 0x0A, no 0x0D) per the WO28 #13 done-condition; the typed-denial misuse assertion returns (WordfreqError.read caused by FileReadError.denied). Digest pin recomputed (9b5d6b79); changed body needs independent final review. Friction ledger #13 marked RESOLVED. Local evidence (Linux): hum full-type-check on wordfreq.hum is recognized_core_body_types_checked_v0 with 0 unchecked/blocking; hum run of the APP entry reaches the platform boundary (native_path_input_unavailable_on_non_windows_v0, exit 2) -- the type gate is open. Final proof of the done-condition rests with Windows CI.
undergroundrap
added a commit
that referenced
this pull request
Sep 24, 2026
WO28 #13 review probes found a pre-existing soundness gap: the resolver treats if-blocks and for-each headers as scopes (H0601 after block close), but the full-type-check environment is flat per task and the runtime leaks if-block lets. Observed wrong acceptance (checker) and wrong-value execution (runtime) on a let/if/return probe; the for-each binder behaves identically to existing block-scoped lets, so #13 stands. New correctness item #16 ordered after #15, before #7; ledger entry #16 records the probe evidence.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WO27 Part 1a: text_split
Implements
text_split(text: Text, sep: Text) -> List Text: exact substring matching, left-to-right, non-overlapping; preserves leading, trailing, and repeated empty pieces (required edge:text_split("aaa", "aa") == ["", "a"]). Directly written empty separator is checker error H0636; runtime-computed empty separator raisesTextSplitError.SepEmptythrough normaltry/fail. Only a directly written non-empty Text literal separator is exempt fromtry. Adds H0636/H0637 diagnostics (catalog now 92 codes / 184 causes).Notes for review:
text_split(line, ", "));full_type_check.rsandrun.rskeep temporary no-logic wrappers, removed in Part 1b.ensuresuntouched.0023 acceptance folded into this PR
Second commit flips decision 0023 to "accepted 2026-09-22, BDFL ruling on the review at da58a58". Batched deliberately: docs/decisions/ is an unregistered path, so a standalone PR would route Full (~40 min) for a one-line status change. This PR already pays Full because it adds files.
Evidence
cargo fmt,cargo clippy --workspace --all-targets(no warnings), public readiness 605 files, text hygiene 605 files.check_all.ps1Session AB covers checker acceptance, app success, repeated separators, the aaa/aa edge, H0636, H0901 JSON output. A local Fast rerun is in progress in parallel; any findings land as follow-up commits.Routing
No
hum-full-validationlabel. The classifier will select Full (this PR adds files). Reporting profile + per-job wall-clock as the first per-route measurement.