Skip to content

I3862 — The shell floor is held by a case that reads what the rule forbids

Give rule project.portable-shell a case that exercises what it states. The case reads every shell unit the shell-lint gate lints and refuses an associative array, mapfile or readarray, and a GNU-only flag (stat -c, date -d, readlink -f, grep -P, sed -r, xargs -r, find -printf, cp --parents) unless the same line carries its BSD form after ||. Its scanner is first held to a fixture whose answer is known, so a scanner that stopped finding them cannot pass for a clean tree. test/mesh-lab/run, the one unit that broke the floor, keeps its scenario verdicts in one JSON object instead of two associative arrays. The rule names the new case beside case 08 and says what each holds, with shellcheck as the shell-lint gate's half.

READY wave 0 · p2 · implementation profile · parallel safe

Part of a-claim-stands-only-on-its-evidence — A claim that something is enforced, tested or delivered stands only on recorded evidence that would have caught it being false.

Ready. Every dependency is done, so majordomus plan start I3862 will be accepted.

Objective

Give rule project.portable-shell a case that exercises what it states. The case reads every shell unit the shell-lint gate lints and refuses an associative array, mapfile or readarray, and a GNU-only flag (stat -c, date -d, readlink -f, grep -P, sed -r, xargs -r, find -printf, cp --parents) unless the same line carries its BSD form after ||. Its scanner is first held to a fixture whose answer is known, so a scanner that stopped finding them cannot pass for a clean tree. test/mesh-lab/run, the one unit that broke the floor, keeps its scenario verdicts in one JSON object instead of two associative arrays. The rule names the new case beside case 08 and says what each holds, with shellcheck as the shell-lint gate's half.

Why

On 2026-10-21 a fact-check (I3089) found that the rule names test/cases/08_no_forbidden_constructs.sh as its test, while case 08 scans only for eval and network clients. The rule is blocking, and the proof graph counted it proven by a case that tests none of its constructs. The mesh lab's runner, started on the host with env bash, used declare -A, which bash 3.2 refuses.

Current state

No case reads the shell units for associative arrays, mapfile or GNU-only flags; test/mesh-lab/run declares two associative arrays; the rule names case 08 alone.

Desired state

Case 3480 refuses each construct the rule forbids in every shell unit and holds its scanner to a fixture; no shell unit breaks the floor; the rule names the cases that hold it and says what each holds.

Scope

  • .ai/repo/project/issues/I3862.yaml
  • .ai/repo/rules/project/portable-shell.v1.md
  • test/mesh-lab/run

Out of scope

  • Detecting every GNU-only flag: the case refuses the listed ones, and review holds the rest

Dependencies

None. This issue is a root of the graph.

Acceptance criteria

  • Case 3480 fails on a fixture carrying each forbidden construct and passes on the tree
  • No shell unit carries an associative array, mapfile, readarray or a listed GNU-only flag without its BSD form
  • Rule project.portable-shell names case 3480 as a test and says what case 08, case 3480 and the shell-lint gate each hold

Validation

  • bash test/run.sh 3480_the_shell_floor_is_bash_3_2_and_bsd_userland
  • scripts/ci/shell-lint
  • scripts/ci/rule-proof-check
  • majordomus plan validate

Evidence required

  • case_the_shell_floor_is_bash_3_2_and_bsd_userland

Evidence

None recorded. Every token above needs a command or an artifact behind it before this issue can be completed; narrative is refused.

Timeline

started
—
verified
—
completed
—

Those three fields, the evidence above and the state of the dependencies are all the status is made of. There is no status field to disagree with them.

Canonical record: .ai/repo/project/issues/I3862.yaml. Read it back with majordomus plan show I3862.

Searches the titles and descriptions of every page this site publishes.