Skip to content

feat: text_split primitive (WO27 Part 1a) - #13

Merged
undergroundrap merged 3 commits into
mainfrom
wip/wordfreq
Sep 23, 2026
Merged

undergroundrap merged 3 commits into
mainfrom
wip/wordfreq

Conversation

@undergroundrap

Copy link
Copy Markdown
Owner

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 raises TextSplitError.SepEmpty through normal try/fail. Only a directly written non-empty Text literal separator is exempt from try. Adds H0636/H0637 diagnostics (catalog now 92 codes / 184 causes).

Notes for review:

  • The canonical escape/nesting-aware call-argument scanner was pulled forward (needed by text_split(line, ", ")); full_type_check.rs and run.rs keep temporary no-logic wrappers, removed in Part 1b.
  • Owned copies per piece are deliberate performance debt (researcher ledger PD-001), not a settled optimum.
  • No views, no parser/syntax changes, no unrelated builtins; ensures untouched.

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

  • Splitter tests 4/4, text_split 14/14, literal recognizer 1/1, focused catalog 15/15.
  • cargo fmt, cargo clippy --workspace --all-targets (no warnings), public readiness 605 files, text hygiene 605 files.
  • check_all.ps1 Session 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-validation label. The classifier will select Full (this PR adds files). Reporting profile + per-job wall-clock as the first per-route measurement.

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
undergroundrap marked this pull request as ready for review September 23, 2026 04:47
@undergroundrap
undergroundrap merged commit 373aa47 into main Sep 23, 2026
4 checks passed
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant