Skip to content

EffectPlan bash materializer: If, Let and Call arms materialize (for gunbc-private#60) - #11357

Merged
gunbai-bot[bot] merged 5 commits into
mainfrom
session/clever-pike-500
Sep 14, 2026
Merged

gunbai-bot[bot] merged 5 commits into
mainfrom
session/clever-pike-500

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

What this lands

v2.workflow.effect_plan_bash_materialize now materializes the If, Let and Call arms of v2.std.effect_plan EffectPlanStep, which it previously refused with EffectPlanBashUnsupportedStep UnsupportedIf / UnsupportedLet / UnsupportedCall. For, While and Retry keep refusing with their unchanged causes; nothing demands them.

All three arms lower through the grammar rows the Do arm already emits and serialize through the one serializer (bash_command_fold_serialize); there is no second bash emitter and nothing routes through v2.std.orchestration Pipeline/PipelineStep.

arm lowering emitted shape
Let { name, ExprLit } bash_build_assign_lit NAME='value'
Let { name, ExprVarRef } bash_build_assign_var NAME="$SOURCE"
Let { name, ExprCmdSubst } bash_build_assign over bash_build_word_cmdsubst NAME=$('callee' 'arg' …)
Call { callee, args } bash_build_command_from_lits 'callee' 'arg' …
If { cond, then_, Absent } bash_build_if if COND; then BODY; fi
If { cond, then_, Present } bash_build_if_else (new builder tag, serializer binds the existing bash_orch_if bash_stmt_if_else_target_model; same precedent as bash_build_negation) if COND; then A; else B; fi
StrEq / StrEmpty / StrNonempty / IntNe bash_build_test_bracket '[' 'a' '=' 'b' ']' etc.
Not / And / Or bash_build_negation / bash_build_and_then / bash_build_or_else ! …, … && …, … || …

Branch bodies are encoded by the same fold as the top-level plan, so FailFast inside a branch is the same && relation; a refusal inside a branch surfaces at the If step as NestedBranchRefused { branch, refusal }.

Quoting is the safety subject

Every interpolated value is a bash_token_lit word and goes through the existing quoting authority (extdeps.posix.shell_command_language posix_single_quote). Names are the one position quoting cannot cover — a variable name renders bare in name= and inside "$name" — so Let names and ExprVarRef names must satisfy the POSIX name rule (XBD 3.235) or refuse ShellNameInvalid. That rule now has one home, posix_name_ok in extdeps.posix.shell_command_language beside posix_single_quote; bash_orch_if bash_path_ident_ok delegates to it (it had its own copy of the alphabets, and importing bash_orch_if from the materializer would have been a new realization-vocabulary leak).

Typed refusals, none of which emit anything:

  • ShellNameInvalid { name } — Let name / ExprVarRef name outside the POSIX name rule.
  • ArgumentContainsNul { argument_index } — NUL in a Let value, cmdsubst word, Call word or predicate operand (the one byte no quoting carries; same arm the Do path uses).
  • CallCalleeEmpty — a plan that calls nothing.
  • IntNeOperandNotDecimalInteger { operand } — [ a -ne 1 ] prints an error and takes the ELSE branch with status 2, a wrong answer wearing a diagnostic.
  • PredicateOperandCarriesShellSpelling { operand } — see the fork below.
  • UnsupportedPredicate { ExitZeroRunPredicate | LogMatchesPredicate } — ExitZero carries a Run whose command is a raw string; LogMatches has no grep argv authority (extdeps.tools.grep is a tool row only).
  • IfBranchEmpty { branch } — then ; fi is a syntax error.
  • NestedBranchRefused { branch, refusal }.

effect_plan_bash_refusal_label stays total over the widened coproduct.

The meaning fork this surfaced, and the disposition taken

v2.std.orchestration Predicate operands are bare String. The Pipeline string tier (05_emit_orchestration) reads them as raw shell spellings — its authors write lhs: "\"$X\"". This transport reads an operand as a value and quotes it, so that spelling would silently compare four literal characters. That is the consumer-varying form of gunbc.recurring_failure_mode.meaning_fork; a receipt and evidence are appended to that row (not a new row). Disposition: operands carrying any of $ \ " ' ` refuse with PredicateOperandCarriesShellSpelling. That is a bounded, loud over-prohibition of a quotable literal, with its dissolution named in the module: Predicate operands carried as Expr (ExprLit | ExprVarRef | ExprCmdSubst, which already exists beside Predicate in the same module).

What that means for the consumer, stated plainly: with this PR, If compares literals only. gunbc-private#60's pin check is Let a captured revision, then If on that variable — that needs a variable-reference operand, which Predicate cannot yet express. So this PR satisfies #60's trigger text exactly as written (the three arms materialize rather than refuse), but the Predicate→Expr operand migration in v2.std.orchestration is the remaining capability #60's If shape needs. That migration touches every string-tier Predicate author and their goldens; it is not this PR and I have not started it. Flagged to bright-eagle-728 for a decision on whether it is the next lane.

Evidence, per arm

Real execution (test.claim.effect_plan_bash_materialize_real_execution_witness, materialize → bash_materialized_transport → bash, observed effect):

  • Let: Let TOOL='id' then a Do whose argv[0] is HostEnvVar TOOL executes id -u through the bound variable (lit, var-ref chain, cmdsubst). RED: id; printf PWN, a cmdsubst arg with the same payload, and a newline payload each stay one word: command-not-found, no output.
  • Call: printf '%s' <payload> with a'b; $(id) \id` | printf PWN > /dev/null "x" \ $HOME— stdout equals the payload byte-for-byte. RED: a calleetrue; printf PWN/true\nprintf PWN` is a name, cannot run, and FailFast stops the plan.
  • If: then/else branch observed by which printf runs, Not, and sequencing with a following step. RED: an operand a; printf PWN | id -u compares as one word (eq when equal, ne when not). A failing Do inside a then-branch stops the branch and the plan.

Unit (v2.test.claim.effect_plan_bash_materialize_test): exact emitted bytes per arm, metacharacter round-trip for Let value / Call arg / If operand, every new refusal arm asserted at its step index with its cause, the label fold rendering a nested refusal, and:

  • control effect_plan_bash_RED_for_while_retry_still_refuse_with_unchanged_causes;
  • permanent regression controls (DESIGN 4b(4)) effect_plan_bash_if_let_call_no_longer_refuse_as_unsupported — the former UnsupportedIf/Let/Call expecting-red assertions flipped, not deleted; the arms stay in the coproduct.

The seven new witness identities reach shell.Exec.Run on the hermetic floor exactly as the file's existing three do and are enrolled in floor_route_gap chunk_11 at the same operation and ground.

Consumer (DESIGN 3c)

Consumed by execution in this change: the witnesses above. Named later consumer: gunbc-private#60 (strategy.private_witness_workflow), whose declared debt annotation_step_shell_medium_dissolution reads: "v2.workflow.effect_plan_bash_materialize materializes EffectPlanStep If, Let and Call rather than refusing them with EffectPlanBashUnsupportedStep UnsupportedIf / UnsupportedLet / UnsupportedCall -- the model side (v2.std.effect_plan) already expresses these steps, only the bash transport refuses them". gunbc-private is not edited here.

posix_name_ok is consumed by bash_path_ident_ok (three existing call sites) and by the materializer.

Not in scope

For, While, Retry; ExitZero/LogMatches predicates; the Predicate→Expr operand migration; v2.std.orchestration Pipeline/PipelineStep.

🤖 Generated with Claude Code

https://claude.ai/code/session_016gwiHZMAPm5jevzYYoiJPC

Brian Searls and others added 2 commits September 14, 2026 08:17
…c-private#60 trigger)

v2.workflow.effect_plan_bash_materialize lowers If, Let and Call through the
grammar rows the Do arm already uses (assign, command, test bracket, if,
new builder-only if-else tag bound to bash_orch_if's if-else target model)
and serializes through the one serializer. For, While and Retry keep refusing.

Every interpolated value is a single-quoted lit word; names must satisfy the
POSIX name rule, now homed as posix_name_ok in
extdeps.posix.shell_command_language (bash_orch_if bash_path_ident_ok
delegates). Predicate operands carrying POSIX quoting/expansion characters
refuse (PredicateOperandCarriesShellSpelling) rather than silently comparing
as literals -- the consumer-varying meaning fork recorded on
gunbc.recurring_failure_mode.meaning_fork; dissolves when Predicate operands
are Expr.

Evidence: real-execution witnesses per arm (observe through the bound
variable, exact stdout round-trip, observed branch), metacharacter REDs,
For/While/Retry control, and the three Unsupported* assertions flipped to
permanent regression controls. New witness identities enrolled in
floor_route_gap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016gwiHZMAPm5jevzYYoiJPC
…lete the alias

Review 66070: the delegating body referenced posix_name_ok without importing
it, and bash_path_ident_ok was a second name for the POSIX name rule. The
import lands, the alias deletes, and the three sites read posix_name_ok.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016gwiHZMAPm5jevzYYoiJPC
@gunbai-bot

gunbai-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Review 66070, both findings fixed in 8fc2bac: (1) bash_orch_if now imports extdeps.posix.shell_command_language { posix_name_ok } — the earlier edit to its import block had not applied and no claim in my local closure covered that module; verified by running proc_self_cgroup_witness_test cgroup_locate_body_preserves_ascent_holds and orchestration_if_emit_test orch_if_streq_else_holds through it. (2) bash_path_ident_ok is deleted; its three callers read posix_name_ok directly, so the rule has one name.

— sent from clever-pike-500

…et lane

The ten members of test.claim.effect_plan_bash_materialize_real_execution_witness
enroll on local_repo_wet_schedule so each changed identity's hermetic route
gap on shell.Exec.Run joins a wet terminal (HermeticRouteGapHeldAndWetPassed);
measured on run 34823848386 as changed_witness_planned_without_terminal_verdict.
The file's ci_layer_roots row moves from the bin-witness wet class (standing
carried by excl_bin_wet_reason) to LocalRepoWetLane with its own reason and
dissolution. Route-gap rows stay; nothing is unenrolled or dropped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016gwiHZMAPm5jevzYYoiJPC
@gunbai-bot

gunbai-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Floor RED on 8fc2bac (changed_witness_planned_without_terminal_verdict x8) fixed in 1a66621: the witness file's ten members are scheduled on the required floor's local-repo wet lane (local_repo_wet_schedule) so each changed identity's hermetic route gap on shell.Exec.Run joins a wet terminal against this candidate; the file moves from the bin-witness wet class (excl_bin_wet_reason) to LocalRepoWetLane with its own reason and dissolution row. Route-gap rows stay; nothing is unenrolled, expected-red, or dropped.

— sent from clever-pike-500

Brian Searls and others added 2 commits September 14, 2026 10:18
… bin-wet class (review 66111)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016gwiHZMAPm5jevzYYoiJPC
…eview 66126)

The climb keeps the flipped assertions enrolled as regression controls and
deletes the constructors nothing produces (DESIGN 4b(4)); the label fold
shrinks with the coproduct.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016gwiHZMAPm5jevzYYoiJPC
@gunbai-bot

gunbai-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Review 66126: agreed and fixed in c615248. UnsupportedIf/UnsupportedLet/UnsupportedCall and their label rows are deleted as obsoleted production machinery; effect_plan_bash_if_let_call_no_longer_refuse_as_unsupported stays enrolled as the three step_materializes calls (the flipped expecting-red probes, per DESIGN 4b(4)). For/While/Retry arms remain, still produced and still controlled.

— sent from clever-pike-500

@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Sep 14, 2026
Merged via the queue into main with commit cf37d1e Sep 14, 2026
4 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/clever-pike-500 branch September 14, 2026 18:48
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.

0 participants