Skip to content

ci: analyze Zi sources with zsh-lint in observe mode - #662

Merged
ss-o merged 1 commit into
mainfrom
feature-661-zsh-lint
Oct 19, 2026
Merged

ss-o merged 1 commit into
mainfrom
feature-661-zsh-lint

Conversation

@ss-o

@ss-o ss-o commented Oct 19, 2026

Copy link
Copy Markdown
Member

Description

Enroll Zi in the organization's shared zsh-lint workflow in observe mode.

  • Add zsh-lint.json (configuration schema version 2): kind tool, minimum Zsh 5.8.1 (the lowest version CI tests), identifier zi. zi.zsh and lib/zsh are sourced libraries; lib/zsh/git-process-output.zsh, lib/zsh/rpm2cpio.zsh, lib/zsh/single-line.zsh, lib/zcompile and scripts/*.zsh are standalone executables; lib/_zi is an autoloaded completion function; lib/templates/example-script is an autoload function.
  • Add .github/workflows/zsh-lint.yml, an observe-mode caller of the organization zsh-lint.yml workflow at main, with the analyzer pinned to v1.6.0 (f6341241c398fd2a784452e75895dafb06186bbc) and 11 literal paths. scripts/ also holds Python, so its Zsh files are listed one by one; a new scripts/*.zsh file needs a line here.

Observe mode reports findings without failing the job; parser errors, invalid configuration, incomplete reports and tool failures still fail it. The check is not part of the Promotion gate, and the native syntax, compilation and focused tests are unchanged.

Findings at main 926ba50

17 files, 0 parser errors, 0 errors, 0 warnings, 192 info, 4 hint. The analysis takes about 5 s locally: 733 parses, 618 of them for zi.zsh, with a maximum adapter depth of 55. Every finding was checked against the source, and the runtime-dependent true positives were confirmed with zsh -f on Zsh 5.9.2.

Rule Severity Count Verdict
quoting/unquoted-var Info 132 82 never empty, 44 intended drop, 3 true positives, 2 false positives, 1 wrong advice
security/eval Info 60 49 user-supplied code, 9 quoted code Zi generates, 2 true positives
style/prefer-double-brackets Hint 4 4 true positives

Judged sample:

  • Never empty: builtin cd -q ${ZI[PLUGINS_DIR]} at lib/zsh/side.zsh:232 (defaulted at zi.zsh:171), return $retval, and $url built from a literal https://github.com prefix at lib/zsh/install.zsh:500.
  • Intended drop: $opt_move $opt_move2 $opt_norm passed to ziextract at lib/zsh/install.zsh:1805, ${${progress:--q}:#1} at :732, and $3 at lib/zsh/autoload.zsh:2641, where snippet callers pass an empty $3 so hooks receive the snippet argument layout.
  • User-supplied code: the atinit, atload, atclone, atpull, reset and other ice values Zi evaluates by design (for example zi.zsh:1839). Generated code: the (q)-quoted stubs at zi.zsh:555, :567 and :575.
  • True positive, quoting/unquoted-var, zi.zsh:655: after a bare bindkey call, ${(Q)string} is empty and is dropped, so builtin bindkey lists every binding and prev records the last word of that listing. The other two, $origin_url at lib/zsh/autoload.zsh:803 and $attached_vol at lib/zsh/install.zsh:2062, are empty only when the command fails anyway.
  • True positive, security/eval: lib/zsh/additional.zsh:86 interpolates wrap ice names and the plugin identifiers into eval without (q), and lib/zsh/install.zsh:2366 lets eval split unquoted ZI[PLUGINS_DIR] path parts.
  • True positive, style/prefer-double-brackets: lib/_zi:290, :292 and :293, and test -d at lib/zsh/autoload.zsh:3206.
  • False positive: lib/zsh/install.zsh:2369 and zi.zsh:2176 use ${x:-word} whose default word contains literal text, so they can never be empty ([bug]: quoting/unquoted-var reports ${x:-word} whose default word has literal text zsh-lint#746).
  • Wrong advice: $___opt_map[$cmd] at zi.zsh:3040 is a single element, but the finding suggests "${___opt_map[@]}" ([bug]: quoting/unquoted-var gives array advice for a single subscripted element zsh-lint#747).

Unrelated to any finding, lib/zsh/install.zsh:252 passes >1 $tmpfile, so the fallback download writes standard output to a file named 1. This pull request does not change Zi sources; these items are recorded for separate follow-up.

Related issues

Closes #661

Type of change

  • fix: bug fix (non-breaking)
  • feat: new feature (non-breaking)
  • feat! / fix!: breaking change
  • perf: performance improvement
  • refactor: code change with no functional impact
  • docs: documentation only
  • test: test addition or correction
  • build: build system or dependency change
  • ci: CI/workflow change
  • style: formatting with no behavior change
  • chore: maintenance / dependency bump
  • revert: revert of an earlier change

Checklist

  • Work branches from current main and targets protected main
  • Full stable qualification passes on the current candidate; release authority is separate
  • Commit messages follow Conventional Commits format
  • I have read the contribution guidelines
  • Native syntax/compilation and affected functional regressions pass; exact commands and compatibility limits are recorded
  • Non-obvious Zsh behavior is checked against the organization scripting standard and relevant official manual sections
  • Documentation updated if needed

Verification:

  • The organization runner (automation/ci/zsh_lint_ci.py from z-shell/.github main) in observe mode, with this configuration, on this branch: passed, with the counts above. Its diagnostics are byte-identical to the judged run at main 926ba50.
  • An explicit zsh-lint --config zsh-lint.json run resolves every input to a configured source root.
  • zsh-lint-survey -judge agrees with zsh -f -n on 16 of 17 files. On lib/zsh/rpm2cpio.zsh, zsh -f -n prints redirection with no command because the no-exec pass skips an array assignment, but it exits 0 and zcompile accepts the file, so this is not a parser gap.
  • zsh -f tests/ci-registration.zsh and zsh -f tests/stable-qualification.zsh pass on Zsh 5.9.2. No Zsh source changes, so the rest of the suite is left to CI.
  • trunk check --no-fix --ci zsh-lint.json .github/workflows/zsh-lint.yml: no issues.
  • The hosted Zsh Lint check and the Promotion gate on this head are the remaining acceptance evidence.

Add zsh-lint.json for Zi's production Zsh sources and an observe-mode
caller of the organization zsh-lint workflow, pinned to zsh-lint v1.6.0.
The check is advisory and not part of the Promotion gate.

Closes #661
@ss-o ss-o moved this to In Progress in Z-shell Delivery Oct 19, 2026

@ss-o ss-o left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fallback review under ADR-0026: Copilot request not registered on 7599f21

Findings

No actionable findings.

Scope: the complete diff of 7599f21: the new zsh-lint.json and .github/workflows/zsh-lint.yml, against #661. No Zi source changes.

Note

This fallback review is not an approval to merge.

Validation

  • Passed: every check on this head, including the Promotion gate and Configured Zsh Lint in observe mode: 17 files, 0 parser errors, 0 errors, 0 warnings, 192 info, 4 hint. Its diagnostics.json is byte-identical to the judged local run at main 926ba50.
  • Passed, rerun independently: the organization runner in observe mode on this head (same counts), zsh -f tests/ci-registration.zsh, zsh -f tests/stable-qualification.zsh, Trunk on both files, the two analyzer reproductions (z-shell/zsh-lint#746, z-shell/zsh-lint#747) and the native probe behind the zi.zsh:655 claim.
  • Unavailable: a Copilot review; the request did not register on this head.
Checklist verdicts and supporting evidence
  • Configuration: correct. Schema version 2. Kind tool keeps the plugin lifecycle rules off manager internals. minimum_zsh 5.8.1 is the lowest version in the zsh-n.yml matrix. Each profile matches how the file is used: zi.zsh and lib/zsh/*.zsh are sourced; git-process-output.zsh, rpm2cpio.zsh and single-line.zsh run as commands (lib/zsh/autoload.zsh:719 and :3210, lib/zsh/install.zsh:530, :727, :745, :2076 and :2077); lib/_zi is #compdef zi; lib/templates/example-script is autoloaded by generated plugins (lib/zsh/autoload.zsh:2927). All 17 production Zsh files are covered; tests, fixtures and benchmarks are excluded.
  • Workflow: correct. The organization reusable workflow is referenced at @main under ADR-0042, and the analyzer is pinned by full SHA. The 11 literal paths resolve to configuration roots; the Zsh files in scripts/ are listed one by one because that directory also holds Python. Permissions are contents: read, the caller owns concurrency, and the path filters cover every analyzed path. The workflow is not a Promotion gate constituent and does not affect the test-registration or stable-qualification contracts.
  • Description: accurate. The per-rule counts match the runner output. The true-positive, false-positive and wrong-advice rows and a further sample of intentional rows were checked against the source.
  • Compatibility and lifecycle: no impact. No change to commands, ices, hooks, loading or the public contract manifest.

Limits and follow-up

The true positives in existing sources (zi.zsh:655, lib/zsh/additional.zsh:86, lib/zsh/install.zsh:2366, the four [ ] and test conditions) and the >1 $tmpfile redirection at lib/zsh/install.zsh:252 are outside this pull request and are recorded for separate follow-up. A new scripts/*.zsh file needs its own line in the workflow's paths.

@ss-o ss-o moved this from In Progress to In Review in Z-shell Delivery Oct 19, 2026
@ss-o
ss-o merged commit ae2dc8e into main Oct 19, 2026
31 checks passed
@github-project-automation github-project-automation Bot moved this from In Review to Done in Z-shell Delivery Oct 19, 2026
@ss-o
ss-o deleted the feature-661-zsh-lint branch October 19, 2026 22:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[chore]: analyze Zi with zsh-lint v1.6.0 in observe mode

1 participant