Repository navigation
shell-typed-invocation first slice: the USB gadget setup is described commands, not shell text - #12579
Conversation
… commands, not shell text Each ConfigfsOp now maps to a typed command -- an ArgvCommand minted through the tool's own cited builder (mkdir/ln/rm/rmdir, PATH-resolved), a typed attribute write, or a typed refusal for a path outside the gadget configfs tree -- and the string-building (configfs_op_line, configfs_ops_script, usb_gadget_setup_script, usb_gadget_teardown_script) is deleted, not stubbed. Consumers read the described form: bmc_virtual_media carries *_commands/*_display, the acceptance oracle usb_gadget_setup_emits_no_shell_control_word is green by execution, the bmc witness module asserts on commands, and the floor_expected_red roster row for the greened probe is removed per DESIGN 4b(4) (the witness stays as the permanent regression control). Verified locally by execution: shell oracle, all four bmc witness fns, and migrated_production_retirement_conditions_are_bound all exit 0.
…er; import ends_with required-ci refused this head twice. (1) CITED-DECLARATION-ABSENT: configfs_op_render_dissolution_trigger cites configfs_op_line, which the migration deleted -- a retirement obligation whose declaration no longer exists is complete, so the trigger went with the declaration (kept-and-citing-absent was exactly the dead vocabulary the repository removes). The discharge is recorded where the trigger stood, in the census witness's comment (five live obligations remain, not six -- explained, not silently dropped), and in the coreutils prose. (2) UnimportedBareProvider: usb_gadget declares imports, so its bare channel is off; ends_with now comes from gunbc.rust_item_scan, the same provider heal_publisher_provision imports it from (starts_with is a builtin and stays bare). Verified locally by execution: dissolution census, shell oracle, and bmc witness wrappers all exit 0.
…consumer
Finding 1: the per-op ConfigfsOpRefused arm is gone. The ops fold into one
ConfigfsStaging -- ConfigfsStagingRan { commands } or
ConfigfsStagingRefused { reason } -- so a bad argument stops the WHOLE
setup or teardown instead of sitting beside runnable commands. The
discriminating fixtures run at the staging grain: a path outside the
gadget tree refuses naming the path, the same bad op mixed into an
otherwise-good staging refuses the whole staging, the good staging alone
mints its one command, and the symlink arm refuses until its link is
directory-spelled.
Finding 2: the display fold is modeled once, in configfs_staging_display;
srv4_vm_gadget_setup_display and the new srv4_vm_gadget_teardown_display
call it instead of repeating the fold beside it. The dangling
usb_gadget_setup_display caller is gone with the duplication.
Finding 3: the already-absent tolerance the old '|| true' carried is a
declared frontier (DESIGN section 3c) beside rmdir_command, and the
attribute write's realization is a declared frontier beside
ConfigfsOpWritesAttr -- each names its later consumer (the host-effect
lane's configfs effect family), the named later change (roadmap node
rn_TAPSSCRTTBBV274TFK1PAGAP49, fleet-closed-loop-converge,
docs/plans/host-effect-orchestration.md), and the trigger stated beside
the declaration.
The teardown now has its described-form consumer: the BMC witness reads
the teardown staging (seven commands, the empty UDC write first, rm -f on
the mass-storage link, every command naming the gadget's own configfs
directory) and the shell witness gates the teardown display against shell
control words. Oracle wrappers extended accordingly; all green by
execution.
|
Addressing review 38602 — all three findings, fixed in 31ffa0d: 1. The refusal is now a stop at the grain a caller consumes, and it is exercised. The per-op 2. The display fold is modeled once. 3. Both "later consumer" gaps are now declared frontiers with the trigger stated beside the declaration (DESIGN §3c). The dropping of Oracle wrappers extended to carry the new fixtures; all green by execution (shell, bmc, dissolution census). — sent from stern-moth-249 |
…ardown's last act The staging-refusal fixture now refuses a write-attr outside the tree too (/etc/passwd, reason naming the path), so the discriminator covers both non-argv arms, and the BMC teardown witness pins the teardown's last act: an rmdir of the gadget root itself (the closing quote in the pattern keeps deeper rmdirs from satisfying it). With these, the stacked slice's witness content is fully absorbed by this PR.
…gebra The changed-witness sublane rosters bare providers on changed lines: the staging fold's append(cs, items: minted) rode a loose local channel the required-ci adjudication does not honor (UnimportedBareProvider Unrostered usb_gadget.dag#append). The builtin's rostered name is list_append in std.algebra; the fold now calls it by that name and the module imports it.
A refused staging renders as 'refused: ...' lines, and those carry no shell control word -- so an all-refused fixture (the gadget's config_root changed away from the gadget tree) would green usb_gadget_setup_emits_no_shell_control_word while the setup would act on nothing. Both oracle fns now red on a refusal rendering; the route itself is asserted in the bmc witness, which matches ConfigfsStagingRan and fails on Refused for the same fixture. Dashboard review against fb46893 (artifact /api/reviews/72447), finding 1's witness clause.
|
Addressing the dashboard review against fb46893 (artifact /api/reviews/72447/artifacts/stdout.log) — same shape as review 38602, and both are addressed by the same climb. Per finding: 1. The refusal doesn't stop anything; the oracle can't tell a refusal from success. Fixed in 31ffa0d: the per-op 2. No-consumer declarations + duplicated fold. All wired or deleted in 31ffa0d/d0be5f7309: 3. Advisory: the extdeps import of a string helper from a gunbc scan module. Verified: — sent from stern-moth-249 |
… prose Strings review 72478 (against d0be5f7, applies equally to the head): both frontier rows were data-String commentary nothing could read -- the exact defect DESIGN 4c names -- and the already-absent rmdir outcome had no typed rung-change record (4b(3)). Both rows are now List<FrontierRow> exports built with frontier_row_decl + unbound_dissolution (the house spelling of extdeps.tools.sha256sum's sha256_verify_native_transport_frontier_rows and std.machine_constraints' machine_width_literal_roster_frontier), with the fronting rationale moved into // comments, and both are appended to gunbc.census_closure_frontier's roster -- the census now reads them, so they are consumed declarations (3c) and not dead data (4c). UNBOUND, deliberately: each condition is the ARRIVAL of a family the host-effect plan (rn_TAPSSCRTTBBV274TFK1PAGAP49) names nowhere, so a bound forward ref would cite a symbol nobody declared -- the fabricated-citation class std.dissolution refuses; stated beside each row. The rmdir carrier's description carries the full 4b(3) record: previous rung (unmodeled shell fallback -- silent absorption, below the ladder), temporary rung (honest nonzero, witnesses gate the staging), reason, bounded population (ConfigfsRmdir arm, the builder's sole executing call site), restoration trigger (the lane's configfs teardown realization). The discharged renderer trigger was NOT restored: required-ci refused it as CITED-DECLARATION-ABSENT (its ref named configfs_op_line, which the migration deleted -- a retirement obligation whose declaration no longer exists is complete); the discharge stays recorded in the census witness. Verified locally by execution: dissolution census, shell oracle, and bmc witness wrapper mains all exit 0 on this head; the census consumers assert membership and structural partitions only, so the two new rows shift no asserted count.
|
Addressing review 72478 (dashboard-only artifact, against d0be5f7; both findings apply equally to the prior head and are now fixed on the new head 438bdb3). Finding 1 — the two frontier rows were unread prose
On "a step backwards": the deletion of Finding 2 — no typed rung-change record for the already-absent rmdir outcome (§4b(3)). Fixed in the same commit: the Verification: the three hermetic wrapper mains (dissolution census, shell oracle, BMC witness) all exit 0 on 438bdb3 locally. The census consumers assert membership and structural partitions only — — sent from stern-moth-249 |
|
Addressing review 72511 (against f792108; the head is now 438bdb3). Finding 1 — the two frontier rows were commentary stored as Finding 2 — The §5 limb — "a dissolution condition … does not authorize creating the debt … Approval is external to the diff" — is satisfied by that externality, and I want to cite it precisely. The debt's authorization is this slice's assignment under the roadmap node this PR implements (rn_7T6WSS1JYCV6GQNW1PZG73B0G3), and the node's authority document,
So the document that mandates this change also reserves the execution-binding vertical as a separate, gated lane — which is why this slice must not start it, and why the frontier (now typed and census-visible) is the correct in-slice record of that debt. On "Before this PR, the script was at least an executable instruction": a factual correction from git history. The pre-PR artifact was Finding 3 (minor) — — sent from stern-moth-249 |
The ln-flags comment said the one live caller was configfs_op_command -- a function the staging climb deleted when it folded per-op minting into configfs_op_staging. The refusal claim itself is unchanged and true: the sole executing call site (usb_gadget.dag, the staging's link arm) refuses a destination that is not directory-spelled before any argv is minted. The citation now names the function that does it. Comment-only change; all three wrapper mains (oracle, bmc, dissolution census) exit 0 after.
|
Addressing review 72541 (against 223ec93; the head has since moved twice and now sits at a28daf4). Finding 1 — the two frontier rows were plain prose strings nothing reads (§4c, §3c). Already fixed before this review arrived: commit 438bdb3 (one commit after the 223ec93 you reviewed) retyped both rows as typed Finding 2 — a comment names a symbol that doesn't exist ( — sent from stern-moth-249 |
briansrls
left a comment
There was a problem hiding this comment.
HOLD at a28daf48440557b190211b53c28ca7ef1882ac1a.
The root cut is right: the script builders are deleted rather than wrapped, refusal now stops the whole staging, and the setup/teardown consumers read the typed route. The required witnesses run is green. I found three correctness blockers before this is an honest handback.
-
configfs_op_stagingdoes not establish containment, and it ignores the gadget's declared root. Every arm accepts a rawstarts_with(path, gadget_configfs_root)check. A path such as/sys/kernel/config/usb_gadget/../outsidepasses and is then handed tomkdir,rm,rmdir, or the future native write even though it resolves outside the gadget tree. Conversely,UsbGadget.config_rootis an input to the model, but staging hard-codes/sys/kernel/config/usb_gadget/, so any other admitted configfs mount is refused. Carry the gadget/root context into staging and construct or validate typed descendants of that root (including rejection of./..segments), rather than treating a string prefix as containment. The controls should include both an escaping-path RED and an alternate-root positive. -
The symlink arm validates only the destination spelling; it never validates the target or the relation between the two. It will mint
ln -sffor an arbitrary target, and the new positive fixture actually uses/dev/nbd0as the target. The configfs operation here is associating one of this gadget's function instances with one of this gadget's configurations; an arbitrary target is an unavailable/invalid operation that should refuse before argv is minted under this node's own red control. Model that relation (target under this gadget'sfunctions, destination under itsconfigs) and add the opposite control. Also give the missing-trailing-slash case its own refusal reason; today it is reported as “outside the gadget tree” even when it is inside. -
rmdir_already_absent_realization_frontier_rowsis attached to the wrong authority and its claimed population is false. Plainrmdir_commandcorrectly models the generic coreutils operation; “already absent means converged” is configfs teardown realization/policy, not a property of everyrmdirinvocation. The carrier also says the bounded population is “i.e. the final rmdir,” whileusb_gadget_teardown_opsemits fiveConfigfsRmdiroperations. Put any genuinely owed absence handling besideConfigfsRmdir/the teardown realization and name the whole population, or delete this frontier and let the eventual host-effect realization model the outcome when it exists. Do not burden the generic tool builder with a configfs-specific debt row.
The PR description also needs rebinding to this head: it still describes the deleted ConfigfsOpRefused and *_commands surfaces, says configfs_op_render_dissolution_trigger was kept and bound although it was deleted, and reports 7 files / +236−53 rather than the current 9 / +515−65.
argv dissolution next: yes. This is a good first vertical once the validation boundary is repaired, but do not spread ConfigfsOpRunsArgv as the next authority. Keep the semantic configfs/tool operation as the value consumers inspect and make ArgvCommand a private leaf produced only at the process-realization edge. This vertical gives a bounded first cut—mkdir-parents, link-function-into-config, unlink, remove-empty-directory, plus the native attribute write. A useful closing control is that no domain consumer or witness needs shell_command_render or argument-string inspection to know what any of those operations means; only the execution boundary sees argv.
…argv leaves the semantic surface Blocker 1: the starts_with guard admitted <mount>/usb_gadget/<g>/../outside and every sibling gadget; the verdict is now one containment fact validated segment-wise against the gadget's own config_root (the hard-coded gadget_configfs_root data row is deleted -- the mount is a modeled input). Blocker 2: ConfigfsSymlink/ConfigfsRemoveLink carried paths and blessed any target (/dev/nbd0 was the fixture's positive); the ops are ConfigfsLinkFunction/ConfigfsUnlinkFunction carrying NAMES -- staging derives both endpoints from this gadget's record, so the functions-into-configs relationship is a construction, not a check. Refusal reasons are distinct per fault: outside the mount, escaped the gadget directory, name not a segment. Blocker 3: the rmdir already-absent frontier moved beside ConfigfsRmdir in usb_gadget.dag -- the debt is configfs TEARDOWN policy, not a property of every rmdir invocation -- and its population is the five teardown rmdirs, complete, not the final one. Recommended next slice: ConfigfsOpRunsArgv stops being a semantic surface. Staging returns ConfigfsPreparedOp (ConfigfsStagingReady, not Ran: nothing has run yet); configfs_prepared_realization lowers each prepared op at the realization boundary to the private ArgvCommand carrier or the native write. The domain witness stops at prepared ops and imports no argv type; the tool-realization witness asserts argv fields (program rows, argument counts, word membership, the directory argument's directory-spelled form) and never calls shell_command_render. Staging is refused as a whole at the first op that does not prepare; the teardown's order is gated by a phase fold over the authored ops, not by reading the display's first line. Locally verified: shell, bmc, and dissolution oracle wrappers all exit 0.
|
All three blockers are repaired and the recommended 1. Containment, against the gadget's declared root. 2. The link relation is a construction, not a check. 3. The rmdir frontier moved to the right authority, with the true population. PR description: rebound to this head — it now describes the shipped surfaces (staged/prepared ops, the verdicts, the two frontiers at their new home) and the current stats (9 files, +1055/−86). Nothing in it claims a deleted declaration was kept.
Locally verified at this head: shell, bmc, and dissolution oracle wrappers all exit 0. — sent from stern-moth-249 |
…se (review 72665, minor) The comment beside shell_typed_invocation_execution_contract still said the deleted usb_gadget_setup_script "prefixes set -e" and that the witness "FAILS TODAY" and "IS ENROLLED" in floor_expected_red -- all three true when written, all three false since the slice landed. It now narrates the completed transition: the script was then (and is deleted), the witness was red until 2026-09-29, the enrollment ended when the row left the roster by the counted un-quarantine, and the green probe stays as the regression control.
|
Thank you — approval noted. The one minor remark is fixed in — sent from stern-moth-249 |
…ed (review 72683, minor) The comment claimed its dissolve-on examples "are all live declarations"; the shell-typed-invocation slice deleted configfs_op_line, so one of them was not. The list now carries the live examples, and the retired one is named as retired -- its disappearance WAS the retirement, and it went.
|
Tidied in — sent from stern-moth-249 |
briansrls
left a comment
There was a problem hiding this comment.
HOLD at 51c4ca9e3ee78ff5a997eeefb8b827986a90be08.
The three blockers from a28daf4844 are repaired on the intended route: containment is now gadget-relative with escape/sibling refusals and an alternate-root positive; the link relation carries names and derives same-gadget endpoints; and the absent-rmdir frontier is beside ConfigfsRmdir with the complete five-operation population. The argv split is also materially better: the domain witness stops at semantic/prepared operations, and the tool witness inspects fields rather than scraping rendered shell. All exact-head CI lanes are green.
Two construction blockers remain before the handback is honest.
-
The prepared/realization boundary is publicly forgeable, so staging validation is optional.
ConfigfsPreparedOpis a plain coproduct,ConfigfsStagingReadyis a plain coproduct,ConfigfsOpCommandis a plain coproduct, andconfigfs_prepared_realizationis an exported function. In this language there is no function privacy, and the repository already records thatsole_constructoris discarded on coproduct variants (gunbc.recurring_failure_mode.modifier_accepted_on_a_shape_its_check_cannot_see; the repaired pattern is ingunbc.review_sheet_identity). A caller can therefore author, cross-module, for exampleConfigfsPreparedLinkFunction { gadget_dir: "/etc", function: "passwd", configuration: "x" }and pass it straight toconfigfs_prepared_realization, bypassing bothconfigfs_path_verdictandconfigfs_name_verdict. The openConfigfsOpWritesAttrarm has the same problem if the eventual host-effect consumer acceptsConfigfsOpCommand. Thus the comments and PR body currently overclaim “validated,” “cannot be authored wrong,” and “private process-boundary carrier.”Use the existing public disposition +
sole_constructorrecord wrapper pattern: staging alone mints a sealed prepared value; realization accepts that sealed value. Have the eventual executor consume the sealed prepared value and lower internally, or seal the realization result too—do not make an openConfigfsOpCommandthe authorization to act. Add a cross-module RED proving that direct preparation/realization construction is refused; the current route tests prove the honest path works, not that the bypass is unwritable. -
The gadget identity used to define containment is still not a path segment.
UsbGadget.nameremains a bareNonEmptyStr, andusb_gadget_dirjoins it into the root beforeconfigfs_path_verdictdefines what “inside this gadget” means. A gadget nameda/b(ora/) produces a nestedgadget_dir; every generated setup path then compares inside that same derived string and stages READY.configfs_name_verdictprotects function/configuration names, but never the gadget name. This is the same self-grounding hole one level up from the repaired link endpoints. Validate/mint the gadget name as one segment before deriving any path—preferably a sealed name carrier—and add the slash-containing-name RED plus the ordinary-name positive.
Minor handback drift: the PR body still says 9 files, +1055/−86, while this exact head is 11 files, +1073/−97 after the two comment fixes. Rebind that when the code blockers are repaired.
…a sealed segment Blocker 1 -- the prepared/realization boundary was publicly forgeable: a foreign module could author ConfigfsPreparedLinkFunction with a gadget_dir it chose and hand it to configfs_prepared_realization, so staging's containment and name verdicts were optional for anyone willing to skip them. The five prepared variants are now the public descriptive sum ConfigfsPreparedDisposition, and ConfigfsPreparedOp is a sole_constructor record wrapper around it: construction is confined to this file, the only mint is configfs_op_prepared behind both verdicts, and realization accepts the sealed value and nothing else. The eventual executor consumes the sealed prepared value (or a realization receipt it mints from it), never an open command sum as authorization. Cross-module RED: a probe source compiled through the guarantee probe corpus constructs the wrapper by hand and asserts the census refuses it (SoleConstructorViolation >= 1). Blocker 2 -- the gadget identity that defines containment was still not a path segment: usb_gadget_dir joined a bare NonEmptyStr under the class root before any verdict ran, so a gadget named a/b redefined its own containment boundary and self-validated inside it. UsbGadget.name now carries UsbGadgetName, a sealed single-segment carrier whose only mints are the verdict's well-formed arm and the named estate constant (gunbcvm_gadget_name); usb_gadget_dir derives from the proven segment. Controls: slash and dot-segment names refuse the name verdict, the ordinary name passes and agrees with the constant, and a second cross-module probe authors the carrier around "a/b" and is refused. Minor: the realization-boundary comment no longer calls ArgvCommand private -- the enforced boundary is the sealed staged value, not a visibility keyword the language does not have.
|
Addressing review 5357430436 at head Blocker 1 — the prepared/realization boundary is publicly forgeable. Repaired on exactly the named pattern: the five prepared variants are now the public descriptive sum Cross-module RED, as required: the domain witness now carries Blocker 2 — the gadget identity is still not a path segment. Repaired with the sealed name carrier: Controls: Minor — handback stats. Recomputed exactly at Local verification at this head: the shell oracle (all eight pre-existing witness tests plus the three new controls), the bmc oracle, and the dissolution oracle all exit 0 under Not merging from this session — the tally re-evaluation and merge decision stay with you. — sent from stern-moth-249 |
…uthorship is stated Finding 2 -- the sealed-name verdict was decoration: nothing on the production path called usb_gadget_name_verdict, and the estate constant was built directly beside it, so the check gated nothing and the test checked the verdict and the constant separately. Deleted on the review's own offered route: UsbGadgetName stays a sealed sole_constructor carrier, the estate constant stays an in-file authored data row, and the comment now states the enforcement plainly -- construction is confined to the authority module, the constant is authored there where the segment law lives, and the cross-module probe in the shell witness (constructing the carrier around a/b from outside, refused) is the RED. A runtime verdict kept beside the seal would have been a second copy of a law nothing called. Finding 1 -- the segment law was written out twice, once in configfs_name_verdict and once in the deleted verdict. With the verdict gone the law lives in exactly one place again. The n == "" arm stays in that one copy, with its reach stated: the type checker refuses an authored empty spelling, but a join over empty parts computes one, so the law stays total over what can reach it at runtime. Oracle reruns green at this head: all eight pre-existing witness tests plus both cross-module construction probes exit 0.
|
Addressing dashboard review 72848 at head Finding 1 — the same segment rule written out twice. Resolved by deletion, the smaller of the two routes the review offered: Finding 2 — the sealed name is never checked in production. Resolved on the review's own offered route: the verdict is deleted, and the authorship is stated plainly. Both fixes are comment-and-deletion only around the seal that review 5357430436 required — Not merging from this session; the tally and merge decision stay with you. — sent from stern-moth-249 |
briansrls
left a comment
There was a problem hiding this comment.
HOLD at a00c5f6a8386d1c8e08361f3b951cebbc9a0d514.
The prepared/realization blocker is repaired. ConfigfsPreparedDisposition is descriptive, ConfigfsPreparedOp is now a sole_constructor record wrapper, realization requires that sealed value, and the foreign-module probe exercises the exact bypass. The exact-head workflow is green in all five jobs, and the PR stats are rebound correctly.
I do not think the gadget-name blocker is closed at the guarantee the PR now claims.
UsbGadgetName seals constructor custody; it does not enforce the segment law.
type UsbGadgetName sole_constructor { segment: NonEmptyStr } refuses construction from another module, regardless of the spelling. The cross-module probe would therefore refuse "gunbcvm" exactly as it refuses "a/b"; it discriminates who constructed the record, not whether segment is one path segment. Inside extdeps.linux.usb_gadget, UsbGadgetName { segment: "a/b" } remains constructible, and gunbcvm_gadget_name is itself minted by that direct route without passing configfs_name_verdict. Deleting the unused usb_gadget_name_verdict removed decoration, but it did not turn the remaining brand into validation. Thus the PR-body claims that this is a “sealed single-segment carrier,” that an a/b name cannot redefine containment, and that no unvalidated name can reach staging are above the evidence: the current one constant is valid by reviewed authorship, not by construction or acceptance.
The repair also moved the concrete estate policy "gunbcvm" from gunbc.bmc_virtual_media into the generic Linux dependency-interface module. That reverses DESIGN §3’s interface/policy boundary: extdeps owns the configfs interface and the segment rule; the consumer owns which gadget name it chooses.
Keep the prepared-op seal. Move the concrete spelling back to gunbc.bmc_virtual_media, and reuse the existing configfs_name_verdict on gadget.name inside the only mint of ConfigfsPreparedOp—before usb_gadget_dir establishes the containment root—or mint a sealed prepared-gadget value from that verdict and have operation preparation require it. Then pair the same production route: a/b must yield ConfigfsStagingRefused, while gunbcvm yields ConfigfsStagingReady. That RED distinguishes the segment law; the current foreign-construction RED distinguishes only the module boundary.
|
Verifying review 72848 item by item against current head
Readiness at this head is unchanged by the verification: dashboard APPROVE 1/1 (review 72910, zero findings), no dashboard REQUEST_CHANGES, CI all five lanes green (run 36639055952), mergeStateStatus CLEAN. The only outstanding requirement is the GitHub CHANGES_REQUESTED held by the operator's own review 5357430436 — dismissal or re-review is theirs, as is the merge. — sent from stern-moth-249 |
…ment law is taken at the staging mint Review 5360078848 (briansrls, a00c5f6): the sealed name carrier proves custody, not segment validity -- in-file construction of UsbGadgetName{segment:"a/b"} stays legal, so the estate constant is well-formed only by reviewed authorship, and moving the spelling into extdeps reversed the consumer/authority boundary. The sketch: the spelling back to gunbc.bmc_virtual_media; the segment verdict inside the only mint of a sealed prepared operation, before usb_gadget_dir establishes the containment root; a paired control on the production route, since a foreign-construction probe distinguishes module ownership, not content. - UsbGadget.name is a plain NonEmptyStr again; UsbGadgetName and gunbcvm_gadget_name are deleted; gunbc.bmc_virtual_media authors "gunbcvm" where the consumer's literal belongs. - configfs_staging_of takes configfs_name_verdict(gadget.name) FIRST, refusing the whole staging with a distinct reason spelling (configfs_staging_refused_gadget_name) before any op is prepared; the link/configuration name law now also gates the gadget's own name. - Witness: the foreign-construction probe for the prepared-op seal stays; the carrier probe is deleted; a paired production-route control stages the slash-named gadget REFUSED and the estate's own name READY (sixteen prepared operations) on the identical route.
|
Review 5360078848 — repaired on your sketch, head is now e1bbd12 (PR rebinding of the gadget-name paragraph done; wrappers re-run at the new head). The one blocker — the carrier proves custody, not segment validity. Repaired exactly on the sketch:
The prepared-op seal itself is untouched: Verified at the new head: the witness oracle wrapper (ten chained tests, construction probe + paired name control included) and the BMC witness wrapper both exit 0 locally; CI has queued the five checks on e1bbd12. PR body's containment paragraph is rebound to the acceptance story, stats now 11 files, +1200 / −97. When you're satisfied, I'll ask the operator to land — the re-review and the tally are yours to rebind. — sent from stern-moth-249 |
briansrls
left a comment
There was a problem hiding this comment.
HOLD at e1bbd12223e5d70a295cb3a451e591c90c857018.
The content-law rework is correct on its intended route: UsbGadget.name is a consumer-owned NonEmptyStr again; the concrete "gunbcvm" spelling is back in gunbc.bmc_virtual_media; configfs_staging_of takes configfs_name_verdict(gadget.name) before its fold; and the paired control now distinguishes a/b from gunbcvm by the production staging result rather than by constructor custody. The prepared-op seal remains intact. The PR body and stats are rebound. Exact-head CI is still queued, not failed.
One construction bypass remains:
configfs_op_prepared is the actual mint, and the new name gate does not guard direct calls to it.
configfs_staging_of may be its sole caller in the current grep, but this language has no function visibility: module functions are importable unless a call wall says otherwise. configfs_op_prepared(gadget, op) remains exported and unrestricted, and it constructs the sealed ConfigfsPreparedOp itself. A foreign module can therefore import it, supply UsbGadget { name: "a/b", ... } and (for example) ConfigfsMkdir { path: "/sys/kernel/config/usb_gadget/a/b" }, and call the mint directly. configfs_path_verdict then compares that path against the same slash-derived usb_gadget_dir, classifies it Inside, and configfs_op_prepared returns ConfigfsStagingReady carrying a sealed prepared operation. The caller can extract that operation and hand it to configfs_prepared_realization. The top-level staging gate was skipped completely.
So the new paired test proves the routed consumer is safe; it does not establish the PR-body claim that the only mint is behind the gadget-name verdict. “Sole caller today” is a census, not an enforcement boundary—the same no-function-privacy fact that required the prepared-op wrapper in the previous round applies here too.
Make the gate unavoidable at the mint. Any one of these shapes works:
- bind
configfs_op_preparedwith an enforcedadmit_callerswall admitting onlyconfigfs_staging_of, plus a cross-module RED for the direct call; - have the name verdict mint a
sole_constructorprepared-gadget record and makeconfigfs_op_preparedrequire that sealed record; or - put the gadget-name verdict inside
configfs_op_prepareditself (less efficient because the fold repeats it, but sound).
Add the discriminating foreign-module control that calls configfs_op_prepared directly with the slash-named gadget and requires refusal. Keep the existing production-route RED/positive pair; it proves the intended integration, while this new control proves the mint cannot be bypassed.
Review 5360613023 (briansrls, e1bbd12): "configfs_staging_of is the sole caller today" is a census fact, not an enforcement boundary. The language has no ordinary function privacy, so a foreign module could skip the staging fold and call the mint directly with a slash-named gadget: inside the direct call, configfs_path_verdict compares against the same slash-derived usb_gadget_dir, classifies the authored path as inside, and returns a sealed prepared operation to the caller who authored that root. The top-level name gate never ran. - configfs_op_prepared carries an admit_callers wall naming exactly one production caller, configfs_staging_of (same shape as the caller- sealed transport_script_seal): the direct call is a ConstructorCallAdmissionRefused at resolve time. - Witness keeps a foreign-module RED for the wall: direct import and call of the mint with the slash-named gadget, census requires the call-wall refusal. The route-level slash-REFUSED / name-READY pair stays as the integration control. Sealed-value custody (sole_constructor), construction-route custody (admit_callers), and name content (verdict at the staging fold) are three laws; each has its own enforced boundary and its own control.
|
Review 5360613023 — repaired on your route 1, head is now f0b76e7. The remaining bypass — the mint is directly callable. You were right that "configfs_staging_of is the sole caller today" is a census fact, not an enforcement boundary. Closed with the wall:
Verified at the new head: the witness oracle wrapper now chains eleven typed tests (hand-authored-value RED, direct-mint wall RED, route-level pair, all passing) and the BMC wrapper and the whole-tree dissolution census all exit 0 locally. CI has queued the five checks on f0b76e7; PR body's realization-boundary paragraph is rebound to the three-law story, stats 11 files, +1238 / −97. When your re-review rebinds at the new head, I'll ask the operator to land. — sent from stern-moth-249 |
|
Landing state after the default-approved "Land" decision: the mechanical paths are exhausted for the bot token. — sent from stern-moth-249 |
…am identities
Main refactored argv_command to program: ProgramIdentity and replaced the
coreutils ln/rm/rmdir NonEmptyStr data rows with *_program() mints. The
three-way merge kept main's refactored exec.command side and this branch's
added ln -sf / rmdir builders side by side: textually clean, semantically
broken -- the composed tree referenced the deleted rows, which the merge-queue
floor caught twice (undefined 'coreutils_ln_binary_name'; NonEmptyStr passed
for ProgramIdentity). The resolution re-expresses the added builders through
ln_program()/rmdir_program() and asserts invocation words ('mkdir' / 'ln' /
'rm' / 'rmdir') in the bmc virtual-media witness, matching main's
bmc_netboot_serve witness convention.
The merge re-expressed the gadget rmdir builder's mint through
uncataloged_program, whose caller-sealed wall admits exactly the mint sites
named in extdeps.exec.program. The floor's generated-artifacts resolve refused
the new mint ('uncataloged_program refuses call from ...rmdir_program'), so
the wall gains the row beside rm_program.
|
The four merge-queue evictions (14:06Z and 16:39Z on 09-30, 04:58Z and 15:13:50Z today) are diagnosed and repaired at head Cause — a semantic merge conflict, invisible to git. The queue composes this PR with current Repairs:
Both defects were caught by the floor lane exactly as designed; re-enqueuing now. |
Roadmap node:
rn_7T6WSS1JYCV6GQNW1PZG73B0G3— shell-typed-invocation ("Describe what a command should do, instead of writing shell text"). Authority:docs/plans/shell-to-dag-residual-census-and-arc-completion.md; specimen: the USB gadget configfs setup, whose script once prefixedset -eand joined rendered lines carrying2>/dev/null || true.What this change does
The script builders are deleted, not wrapped. The instruction is now the typed staging:
ConfigfsOp(five arms) folds throughconfigfs_staging_ofinto oneConfigfsStaging—ConfigfsStagingReady { operations: List<ConfigfsPreparedOp> }orConfigfsStagingRefused { reason }. There is no state in which a refused op sits beside prepared ones: the fold stops at the first op that does not prepare, and a refused staging carries no operation list at all, so a bad argument cannot act while the ops around it are skipped past. Ready, not Ran — nothing has run yet; the name says what the value is.Containment is one validated fact, not a string prefix:
configfs_path_verdict(gadget, path)answers Inside / Escaped / Outside for THIS gadget's ownconfig_root, segment-wise —./..segments and sibling gadgets under the same mount refuse with a distinct escape reason; paths off the mount refuse with the outside reason. The old hard-codedgadget_configfs_rootdata row is deleted; the mount is a modeled input, and a fixture gadget rooted at a different configfs mount stages READY. The gadget's own name is a plainNonEmptyStr, and the spelling is the consumer's literal again, where the literal belongs (review 5360078848's sketch: the carrier proved custody, not segment validity — in-file construction arounda/bstayed legal, so the constant was well-formed only by reviewed authorship, and moving the spelling into extdeps reversed the consumer/authority boundary). The segment law is instead taken by acceptance at the staging mint:configfs_staging_oftakesconfigfs_name_verdict(gadget.name)FIRST — inside the only mint of sealed prepared operations, beforeusb_gadget_direstablishes the containment root — refusing the whole staging with a distinct reason before any op is prepared, so a gadget nameda/bcannot redefine the boundary its own paths would be validated against.configfs_name_verdictstays the one place the law lives (dashboard review 72848, finding 1); it now gates link names, configuration names, and the gadget's own name. The paired production-route control distinguishes content, which a foreign-construction probe cannot: the slash-named gadget stages REFUSED, the estate's own name stages READY (sixteen prepared operations) on the identical route.The link arms carry names, not paths:
ConfigfsLinkFunction { function, configuration }/ConfigfsUnlinkFunction. Both endpoints are derived from the gadget's own record (<g>/functions/<f>,<g>/configs/<c>/), so "this gadget's function into this gadget's configuration" is a construction that cannot be authored wrong — an arbitrary target (the old fixture's/dev/nbd0positive) is no longer expressible, and a name that is not a single well-formed segment refuses before anything is prepared.Realization boundary (recommended next slice, delivered in the same change)
ConfigfsOpRunsArgvis no longer a semantic surface.ConfigfsPreparedOpis a sealed staged value: the five prepared variants are the public descriptive sumConfigfsPreparedDisposition, and the wrapper is asole_constructorrecord — construction is confined toextdeps.linux.usb_gadget, the only mint isconfigfs_op_preparedbehind the containment and name verdicts — and the mint itself is caller-sealed (review 5360613023's finding: "sole caller today" is a census fact, not an enforcement boundary; without a wall a foreign module could call the mint directly with a slash-named gadget andconfigfs_path_verdictwould validate against the root that name redefined):configfs_op_preparedcarries anadmit_callerswall naming exactly one production caller,configfs_staging_of, so the direct call is aConstructorCallAdmissionRefusedat resolve time — the same wall the caller-sealedtransport_script_sealrelies on. Sealed-value custody (sole_constructor), construction-route custody (admit_callers), and name content (verdict at the staging fold) are three laws; each has its own enforced boundary and its own witness control.configfs_prepared_realizationaccepts the sealed value and nothing else, so a cross-module author of a prepared op with agadget_dirit chose is refused at compile time — proven, not asserted: the domain witness compiles a foreign module source through the guarantee probe corpus and requires the census to refuse the hand-authored construction (SoleConstructorViolation). The lowering:mkdir -p(mkdir builder),ln -sf <functions endpoint> <config dir/>(the into-directory ln builder),rm -f(rm builder),rmdir(rmdir builder) — or the nativeConfigfsOpWritesAttr(a typed write(2), never a shell redirect). The eventual executor consumes the sealed prepared value (or a realization receipt minted from it), never an open command sum as authorization. Consumers of the semantic operation stop at the sealed staged value; only the execution boundary and the tool-realization witness see argv.Witness split.
dag/test/claim/shell/shell_typed_invocation_witness_test.dagis the domain side: verdicts, relationships, refusals-as-a-whole, the gadget-name segment guard, the phase fold that pins the teardown's order (detach → unlink → five rmdirs, innermost first), the two render-absence oracles the roadmap contract pins by name (usb_gadget_setup_emits_no_shell_control_word, teardown sibling), and the cross-module construction probe (prepared-op seal) with the paired production-route name control beside it. It imports no argv type.dag/test/claim/bmc/bmc_virtual_media_witness_test.dagis the fixture + tool-realization side: which builder each prepared op lowers to, asserted over argv fields (program against the builder's declared name row, argument counts, word membership, exactly one directory-spelled argument on the ln command) — it never callsshell_command_renderand never searches a rendered string. Closing control, structural: no domain witness or configfs/BMC consumer learns what an operation means from anything rendered.Frontiers (typed DissolutionConditions on the census roster, beside the declarations they front)
configfs_attr_write_realization_frontier_rows— the typed attribute write is owed to the host-effect lane's configfs effect family (roadmaprn_TAPSSCRTTBBV274TFK1PAGAP49,docs/plans/host-effect-orchestration.md). Unbound deliberately: the family is not yet named anywhere in the lane's plan, so a bound forward ref would cite a symbol nobody declared.configfs_rmdir_absent_as_converged_frontier_rows— moved here from extdeps.tools.gnu_coreutils (it fronted the generic rmdir builder, whose other callers have no such policy): "already absent means converged" is configfs teardown policy. Population, complete: the fiveConfigfsRmdiroperations of the gunbcvm teardown —functions/mass_storage.0,configs/c.1/strings/0x409,configs/c.1,strings/0x409, and the gadget root. The plain rmdir stays the instruction; no|| truere-enters.The old renderer's retirement obligation is discharged, not carried:
configfs_op_lineis deleted and its trigger went with it — a trigger citing an absent declaration is the dead vocabulary this repository removes.Scope and verification
srv4_nbd_*) is the pre-existing CLI-authority surface and is untouched.srv4_vm_gadget()is the fixture accessor the witnesses build from.gunbc run, both foreign-module wall probes and the paired name control included); CI runs the same estate.— sent from stern-moth-249