Found by the #2669 pass over every non-green cell in the keyword-rosetta bias report (2026-09-05, engine 0e521e8). yaml/args reads 0 against a median of 13 and avg_func_args 0.0 against 1.0; both are ledgered as args-no-parameter-surface-morphology because the rule "matches a with: block, which binds to uses: and never to the corpus's run: steps". The rule is narrower than the language on two axes.
1. with: is the call-site argument block; the declarations are inputs:
yaml.py's args matches only
r"^[ \t]*with:[ \t]*(?:#.*)?\n(?:[ \t]*(?:#.*)?\n){0,10}[ \t]+[a-zA-Z0-9_-]+:[ \t]*.*"
with: supplies arguments to an action. The parameter surface a YAML file declares — the thing every other language's args rule reads off the def/function/sub line — is inputs::
None of these match today. On language-crucible's yaml corpus (8 Ansible task files, 77bc85e) with: fires 0 times, so the rule is also unexercised by the golden master (the caveat #2646/#2647 recorded as language-crucible#23). Ansible's own parameter blocks — vars: on include_role/include_tasks (5 in crucible) and module args: — are likewise unseen.
2. It counts blocks, not parameters
The regex consumes the block header through its first key as one match, so with: carrying five inputs is args 1. yaml defines none of the _args_* capture helpers, so nothing splits the list. Every other language records one hit per parameter; a with: block with five keys is five arguments by the same definition.
Candidate shape
# Parameter surfaces: a declared inputs: block (workflow_dispatch / workflow_call /
# action.yml) or a supplied with: / args: block. One hit per key, not per block.
"args": re.compile(
r"^[ \t]*(?:with|inputs|args):[ \t]*(?:#.*)?\n"
r"(?:[ \t]*(?:#.*)?\n){0,10}[ \t]+([a-zA-Z0-9_-]+):",
re.M,
),
plus an _args_pattern_list_groups-style helper (or an indentation walk) to count every key at the block's child indentation rather than the first. Whether Ansible vars: is a parameter block or a variable block is the design question — it is both, depending on the task.
Overlap, measured (screen_plant.py)
An inputs: block under the corpus's existing on: workflow_dispatch: fires nothing new (the api hit shown is the pre-existing trigger). The plant must use required:/default: only — a description: under an input is a doc hit (yaml's doc rule matches every description: line). A with: under a uses: step is the ledger's rejected form (it needs a uses: line = import + a DAG edge); an inputs: declaration needs neither.
Corpus pairing
a.yml already carries on: workflow_dispatch:; an inputs: block with one key per probe (12 keys) reads args 12 under a per-key count, or 1 under today's per-block count. Either ends the 0; the ledger entry narrows or retires for yaml.
Part of #2669 (rosetta[yaml] is #2606).
Filed together on 2026-09-05: jcl api #2748 · jcl cleanup #2749 · jcl globals #2750 · jcl high_risk_execution #2751 · css io #2752 · yaml args #2753. Corpus-side pairings: squid-protocol/keyword-rosetta#59.
Found by the #2669 pass over every non-green cell in the keyword-rosetta bias report (2026-09-05, engine 0e521e8).
yaml/argsreads 0 against a median of 13 andavg_func_args0.0 against 1.0; both are ledgered asargs-no-parameter-surface-morphologybecause the rule "matches awith:block, which binds touses:and never to the corpus'srun:steps". The rule is narrower than the language on two axes.1.
with:is the call-site argument block; the declarations areinputs:yaml.py'sargsmatches onlyr"^[ \t]*with:[ \t]*(?:#.*)?\n(?:[ \t]*(?:#.*)?\n){0,10}[ \t]+[a-zA-Z0-9_-]+:[ \t]*.*"with:supplies arguments to an action. The parameter surface a YAML file declares — the thing every other language'sargsrule reads off thedef/function/subline — isinputs::on: workflow_dispatch: inputs:/on: workflow_call: inputs:— a workflow's parameters (workflow_call is the reusable-workflow API fix(core-engine): give the api rule a stated contract and audit all 46 languages against it (#2730) #2743 added to theapitrigger set; itsinputs:block is that API's signature);inputs:inaction.yml— a composite action's parameters.None of these match today. On language-crucible's yaml corpus (8 Ansible task files,
77bc85e)with:fires 0 times, so the rule is also unexercised by the golden master (the caveat #2646/#2647 recorded as language-crucible#23). Ansible's own parameter blocks —vars:oninclude_role/include_tasks(5 in crucible) and moduleargs:— are likewise unseen.2. It counts blocks, not parameters
The regex consumes the block header through its first key as one match, so
with:carrying five inputs isargs1. yaml defines none of the_args_*capture helpers, so nothing splits the list. Every other language records one hit per parameter; awith:block with five keys is five arguments by the same definition.Candidate shape
plus an
_args_pattern_list_groups-style helper (or an indentation walk) to count every key at the block's child indentation rather than the first. Whether Ansiblevars:is a parameter block or a variable block is the design question — it is both, depending on the task.Overlap, measured (
screen_plant.py)An
inputs:block under the corpus's existingon: workflow_dispatch:fires nothing new (theapihit shown is the pre-existing trigger). The plant must userequired:/default:only — adescription:under an input is adochit (yaml's doc rule matches everydescription:line). Awith:under auses:step is the ledger's rejected form (it needs auses:line =import+ a DAG edge); aninputs:declaration needs neither.Corpus pairing
a.ymlalready carrieson: workflow_dispatch:; aninputs:block with one key per probe (12 keys) readsargs12 under a per-key count, or 1 under today's per-block count. Either ends the 0; the ledger entry narrows or retires for yaml.Part of #2669 (rosetta[yaml] is #2606).
Filed together on 2026-09-05: jcl
api#2748 · jclcleanup#2749 · jclglobals#2750 · jclhigh_risk_execution#2751 · cssio#2752 · yamlargs#2753. Corpus-side pairings: squid-protocol/keyword-rosetta#59.