Skip to content

module_id: refuse path components longer than 255 UTF-8 bytes (#97 prerequisite) - #101

Merged
ualtinok merged 1 commit into
cortexkit:masterfrom
iceteaSA:feat/cgroup-placement
Sep 17, 2026
Merged

ualtinok merged 1 commit into
cortexkit:masterfrom
iceteaSA:feat/cgroup-placement

Conversation

@iceteaSA

@iceteaSA iceteaSA commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Option 2 from #97, split out on its own because placement is blocked behind the forbid(unsafe_code) question I raised on the issue — and because this stands alone: it fixes a latent store-path failure whether or not placement ever ships.

What

module_id_path_hazard (registry.rs) and its TypeScript twin assertPathSafeModuleId (clients/store/src/derivation.ts) both gain one clause: refuse an id longer than 255 UTF-8 bytes, reason is longer than 255 bytes, identical string on both sides.

Framed as a path-component limit, per your ruling, not a cgroup limit. The comments say why it is not a new restriction: module_id is already one literal component in the store-path derivation, so a 256-byte id fails to get a store directory today on any platform with NAME_MAX 255. This states the clause that was always implicit.

Bytes, not characters — and the test that can tell

NAME_MAX counts bytes. Rust str::len() is bytes; JavaScript String.length is UTF-16 code units. A naive moduleId.length <= 255 in TS accepts an id that mkdir rejects, and an ASCII-only test suite cannot see the difference — both implementations agree on every ASCII vector while disagreeing on real input.

So the fixture carries a straddle case: 64 emoji = 128 UTF-16 code units, 256 UTF-8 bytes. It must be refused. That single vector is what separates a correct implementation from a plausible one.

Fixture

Your comment named crates/subc-core/tests/fixtures/store_paths_golden.json; that file does not exist. What exists is clients/store/tests/golden/storage_vectors.json — TS-side, positive vectors only, no refusal cases anywhere — and the Rust-authored data_home_resolution.json that the TS suite already reads by relative require. The refusal predicate had nothing binding the two languages.

Rather than invent a scheme, this follows that existing precedent: a new Rust-authored crates/subc-core/tests/golden/module_id_path_component_refusals.json, read by Rust via include_str! and by TS via the same relative require. One file, two readers — so the two implementations cannot drift apart silently. Vectors: ASCII 255 accepted, ASCII 256 refused, and the emoji straddle.

Verification

Mutation-proved in both languages, each reddening by name:

registry.rs:340:  assertion `left == right` failed:
                  shared refusal vector "ascii_256_bytes_refused" diverged
registry.rs:340:  assertion `left == right` failed:
                  shared refusal vector "emoji_256_utf8_bytes_refused_despite_128_utf16_code_units" diverged
storage.test.ts:145: error: emoji_256_utf8_bytes_refused_despite_128_utf16_code_units

I ran the discriminating one independently rather than take it on report — swapping the TS bound from TextEncoder().encode(id).length to moduleId.length fails exactly one test, the emoji vector, and leaves the other ten green. That is the check doing its job: the ASCII vectors cannot distinguish the two implementations, and the straddle case can.

Gates: cargo test --workspace --locked, cargo clippy --workspace --all-targets --locked -D warnings, cargo fmt --all --check, clients/store bun test 11 pass / 66 assertions, wire-version check 6 crates examined none unbumped. subc-core 0.17.44, @cortexkit/store 0.1.2, Cargo.lock committed.

CONSUMER-IMPACT: ids longer than 255 UTF-8 bytes are now refused at config parse and HELLO instead of failing later at store-path derivation. No id that ever worked is affected.

Still open on #97

The placement half needs your call on the forbid(unsafe_code) question — tokio::process::Command::pre_exec is unsafe, forbid cannot be locally allowed, and the four options with my lean are on the issue. Post-spawn placement is the one I'd argue against: it leaves exactly the window that #97 exists to close.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Rejects module_id values longer than 255 UTF-8 bytes during config parsing and HELLO instead of letting store-path derivation fail later. Existing working IDs, including 255-byte IDs, remain valid.

  • Applies the same byte-based check in Rust and TypeScript, with shared ASCII and emoji fixtures.
  • Bumps subc-core to 0.17.46 and @cortexkit/store to 0.1.2.

Written for commit 1701768. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 7 files

Re-trigger cubic

@subc-alfonso

subc-alfonso Bot commented Sep 16, 2026

Copy link
Copy Markdown

Read the diff, not the description, and it is the shape #97's ruling asked for: one clause on both twins, identical reason string, framed as the path-component limit that was always implicit, and the emoji straddle (128 UTF-16 code units / 256 UTF-8 bytes) is the vector that separates a correct implementation from a plausible one — the ASCII rows cannot. Shared Rust-authored fixture with two readers is the right precedent to follow, and your independent re-run of the discriminating mutation (TextEncoder.length reds exactly the emoji vector, ten others green) is the evidence I would have asked for.

One collision before this can land: you branched from 206701ba and bumped subc-core 0.17.43 → 0.17.44, but master moved to 0.17.44 at c1cac9a6 an hour before your push (relative-storage-data-home refusal). Two different trees at one version; the wire-crate check on the merge commit will refuse it against the previous push. Please rebase onto master and bump to 0.17.45 — nothing else in your diff touches what landed in between (daemon_config.rs and the config-home golden), so the rebase should be clean.

I will run the twin on the full matrix once the rebase is up and merge on green. Thank you for citing the actual fixture path rather than the one I named that does not exist — that is my error in the #97 comment, and the file you found is the right one to extend.

@iceteaSA
iceteaSA force-pushed the feat/cgroup-placement branch from fed74e4 to 83243fb Compare September 16, 2026 18:35
@iceteaSA

Copy link
Copy Markdown
Collaborator Author

Rebased onto master d8355fcf, bumped to subc-core 0.17.45, head is 83243fb7.

Clean rebase, no conflicts — and the collision was slightly sharper than "two trees at one version": my commit bumped 0.17.43 → 0.17.44, master independently landed 0.17.44, so after the rebase my bump was a no-op and git dropped Cargo.toml from the diff entirely. The branch would have carried the length bound at master's version with no bump of its own, which is the state the wire-crate check exists to refuse. Bumped to 0.17.45 explicitly and amended it back in; Cargo.lock updated to match.

@cortexkit/store stays 0.1.2 — master is on 0.1.1, so there was no collision on that side.

Gates on the rebased commit: cargo test --workspace --locked zero failures across all binaries · cargo clippy --workspace --all-targets --locked -- -D warnings exit 0 · cargo fmt --all --check exit 0 · clients/store bun test 11 pass / 0 fail / 66 assertions · check-wire-crate-versions.sh origin/master 6 crates examined, none unbumped · porcelain clean.

No need to apologise for the fixture path — I only found the real one because I went looking to brief the implementation accurately, and finding that storage_vectors.json has no refusal vectors at all was the more useful half of that dig. The refusal predicate had been duplicated across two languages with nothing binding it since it was written; this PR is the first thing that ties them.

Ready for the twin whenever you are. The unsafe question on #97 is the only thing still blocking the placement half.

ualtinok added a commit that referenced this pull request Sep 16, 2026
…fusal does not refuse the tests

c1cac9a (0.17.44) made parse_doc refuse a relative storage data home. Three
existing tests then failed on the Windows leg: /data, /forced/data/home and
/abs/home are RELATIVE on Windows (no drive letter), so the refusal was firing
on the tests' own fixtures rather than on anything the tests meant to assert.
Master has been red on Windows since that commit; the push runs in between were
cancelled by my own docs-only pushes (cancel-in-progress), and the first run to
complete was d8355fc's. Both iceteaSA twins (#101, #102) inherited the red.

Fix is in the tests, not the guard: an abs() helper that yields C:\... on
Windows and the POSIX path elsewhere, with the reason at its definition. The
guard is correct; the fixtures assumed POSIX absoluteness.

subc-core 0.17.45 (the wire check counts every non-comment line, test or not).
#101 and #102 will need one more bump each on rebase; that cost is mine.
@subc-alfonso

subc-alfonso Bot commented Sep 16, 2026

Copy link
Copy Markdown

The Windows red on your twin was mine, not yours — read from the job log rather than assumed: the three failing tests (relative_storage_data_home_is_refused_at_parse, sqlite_storage_parses_with_explicit_data_home, sqlite_storage_defaults_data_home_when_omitted) are all in daemon_config.rs, which this PR does not touch. My c1cac9a6 made the daemon refuse a relative storage data home, and /data is relative on Windows, so the refusal fired on three test fixtures that assumed POSIX absoluteness. Master was red on that leg from that commit; the runs between were cancelled by my own docs pushes, so the first completed run to show it was after your twin was cut.

Fixed at 4a258064 as 0.17.45 (test fixtures now use a platform-absolute helper; the guard is unchanged). That takes the version this PR was holding — one more rebase and bump, please: #101 → 0.17.46, #102 → 0.17.47 (and #102 keeps subc-protocol 0.19.2). I will re-run both twins on the rebased heads and merge in that order on green. The extra round is my cost, not a review finding.

CONSUMER-IMPACT: ids longer than 255 UTF-8 bytes are now refused at config/HELLO instead of failing later at store-path derivation.
@iceteaSA
iceteaSA force-pushed the feat/cgroup-placement branch from 83243fb to 1701768 Compare September 16, 2026 22:21
@iceteaSA

Copy link
Copy Markdown
Collaborator Author

Rebased onto 4a258064, subc-core 0.17.46, head 17017688.

Gates: workspace tests zero failures · clippy -D warnings exit 0 · fmt exit 0 · clients/store 11 pass / 66 assertions · check-wire-crate-versions.sh origin/master exit 0, 6 crates none unbumped · porcelain clean.

No cost owed on your side — reading the job log rather than assuming the red was mine is the same move I'd want from a reviewer, and /data being relative on Windows is a genuinely good catch that only a real matrix finds. Worth noting the failure mode it exposes: master went red on a leg, and the runs that would have shown it were cancelled by subsequent pushes, so the first completed run carrying the signal was a PR twin. A cancelled run is not a green one, but a queue of cancellations reads like quiet.

#102 is rebased to 0.17.47 alongside, subc-protocol held at 0.19.2 as you asked.

@subc-alfonso

subc-alfonso Bot commented Sep 17, 2026

Copy link
Copy Markdown

Landing this myself rather than asking for a fourth rebase. While you were rebasing I took 0.17.46 for a forwarding-test drain-budget fix (5d35c531, the 0.17.45 release's Windows verify leg), so #101 at 0.17.46 collided with master a third time — every collision today was on my side of the clock, and a third round-trip for a version number is not a review finding, it is my scheduling.

Merged locally onto master as a stack: #101 → 0.17.47 (aa1cff3e), #102 → 0.17.48 on top (2a1713b9, subc-protocol 0.19.2 held). The only change in either merge commit is the version line; your trees are byte-identical otherwise. Full gate on the stacked tree: fmt, workspace clippy on host and both windows-gnu profiles, 1345/0 workspace tests, wire check 6 examined none unbumped, clients/store 11/11. Twin is running on train/merge-101-102 (ba2c13f5); on every leg green the stack fast-forwards to master and both PRs close as merged. If any leg reds I will read the job log before saying whose it is.

@ualtinok
ualtinok merged commit 776bea3 into cortexkit:master Sep 17, 2026
17 checks passed
@subc-alfonso

subc-alfonso Bot commented Sep 17, 2026

Copy link
Copy Markdown

Published: subc-core-v0.17.48 is undrafted by its own gate and the signed index at cortexkit.io/releases/v1/index.json serves 0.17.48 on all five targets. This is the first user-installable release carrying your change.

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.

2 participants