Skip to content

env-vars: the environment read outside the declared edge - #63

Draft
zmaril wants to merge 3 commits into
mainfrom
claude/sloth-env-vars-config-2g6g2t
Draft

zmaril wants to merge 3 commits into
mainfrom
claude/sloth-env-vars-config-2g6g2t

Conversation

@zmaril

@zmaril zmaril commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Runs beamte 0.2's env-read — code reading the process environment where nothing declares it — over every file straitjacket can parse. The finding is beamte's (PowderworksCode/beamte#21); this side owns the grammar, the parse, the severity, and the piece that is policy rather than fact: env-files, the files that are the configuration edge, where reads are licensed. It is theme-files for the environment. Everywhere else a read is an error — the rule is opt-in, and a repository that turned it on wants the read stopped, not mentioned.

Surface

  • env-vars = true in straitjacket.toml, or --env-vars. Opt-in for test-quality's reason: the first scan of a language downloads its grammar. Nine languages (beamte's list exactly, asserted in a test); Shell deliberately not among them, $VAR being the language's own variable model.
  • env-files = [...] names the declared edge; matching is file-size-exclude's, so one notion of "this path" covers both.
  • Cheap per-language markers (env::var, environ, process.env) prefilter, so a file that cannot contain a read is never parsed.
  • A file whose grammar cannot be fetched or that does not parse is reported not read, never clean.

Structure

The pack cache moves to src/pack.rs, shared, so two rules meeting the same language in one scan JIT its grammar once between them; finding/not-read formatting moves to rules/beamte_findings.rs so the two beamte hosts cannot drift. test-quality reads beamte's new Rule::scope and holds file-scoped rules out of its default selection — one read in a test file is one finding under one key — and test-rules rejects a file-scoped rule by name, pointing here.

The release ordering (why the publish dry-run is red)

beamte 0.2 is not on crates.io yet, so beamte = "0.2" resolves through a [patch.crates-io] git entry carrying a drop-me note. Every other gate is green locally — fmt, clippy -D warnings, 87 workspace tests (including a breadth test with one real environment read per language, through real packs), the musl release build, the self-scan, and the site's 96 docs-vs-manifest tests — but cargo publish --dry-run fails at manifest preparation: beamte ^0.2 has no registry candidate. That is the ordering, not a defect here: merge and publish beamte 0.2, delete the patch table (three lines), and the gate is whole.

First target: zmaril/sloth#46, where the rule found the scattered reads it was built for.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WrSzGnURZoupdEfVdwk9pg

An environment variable read mid-file is configuration no signature
admits to. beamte 0.2's env-read finds them structurally -- an
invocation or access of the language's environment surface, so a
mention in a comment or a string is not a finding -- and this rule runs
it over every file straitjacket can parse, in the nine languages the
surface table covers. Shell is deliberately not among them: $VAR is the
language's own variable model.

env-files names the files that ARE the configuration edge, where reads
are licensed -- theme-files for the environment. Everywhere else a read
is an error, not a property mapping: the rule is opt-in, and a
repository that turned it on wants the read stopped, not mentioned.
Enable with env-vars = true or --env-vars; opt-in for test-quality's
reason, that the first scan of a language downloads its grammar.

The pack cache moves to src/pack.rs, shared, so two rules meeting the
same language in one scan JIT its grammar once between them; the
finding and not-read formatting move to rules/beamte_findings.rs for
the same reason. test-quality now reads beamte's new Rule::scope and
holds file-scoped rules out of its default selection -- one read in a
test file is one finding under one key -- and test-rules rejects a
file-scoped rule by name, pointing at env-vars.

beamte 0.2 is not on crates.io yet, so the requirement resolves through
a [patch.crates-io] git entry carrying a drop-me note. Until the
release, cargo publish --dry-run fails at manifest preparation --
beamte ^0.2 has no registry candidate -- which is the release ordering,
not a defect here: publish beamte 0.2, delete the patch table, and the
gate is whole again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WrSzGnURZoupdEfVdwk9pg
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 30, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
straitjacket 7ae7b97 Commit Preview URL

Branch Preview URL
Aug 30 2026, 08:05 PM

zmaril commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

gate is red on the publish dry-run, and stays red until beamte 0.2 ships

What is failing. The gate job, at its last step, scripts/publish.sh --dry-run (logs):

error: failed to prepare local package for uploading
Caused by:
  failed to select a version for the requirement `beamte = "^0.2"`
  candidate versions found which didn't match: 0.1.0
  location searched: crates.io index

Every other step in that job passed first — fmt, clippy -D warnings, cargo test --workspace, shellcheck, and the musl release build (3m01s, clean). The other eleven checks are green: hawk, rules, action, site, vale, codespell, zizmor, shellcheck, stylelint, changes, and the Workers deploy.

Why it is not fixable in this PR. beamte = "0.2" is the correct requirement — env-read and Rule::scope are 0.2 API — and crates.io still only carries 0.1.0. The [patch.crates-io] table lets cargo build/test resolve against the branch, but cargo publish deliberately ignores patches: it resolves the manifest as a consumer would, which is the whole value of the check. So the dry-run is reporting a true fact about the registry, not a defect in this diff, and there is nothing to push here that would not either gut the feature (reverting to 0.1) or defeat the gate (skipping the step).

The fix, in order. No patch to apply on this branch:

  1. Merge PowderworksCode/beamte#21 (green) and publish beamte 0.2.0.
  2. Delete the [patch.crates-io] table at the foot of Cargo.toml — three lines and their comment. The requirement above it is already written for the registry and does not change.
  3. cargo update -p beamte and push; gate goes green with no other edit.

Not re-run: the failure is deterministic, so a second run would only reproduce it.


Generated by Claude Code

claude added 2 commits August 30, 2026 16:18
…-config-2g6g2t

# Conflicts:
#	CHANGELOG.md
#	src/main.rs
#	src/scanner.rs
beamte#21 merged and GitHub deleted the feature branch with it, so the
[patch.crates-io] entry named a ref that is gone. A patch cargo cannot
resolve fails the whole job at dependency resolution -- before fmt,
clippy or a single test -- which is a worse failure than the publish
dry-run this branch already expects.

The default branch carries the merged env-read, so the patch points
there and needs no ref to chase. It comes out entirely once 0.2.0 is on
crates.io; the requirement above it is already written for the registry.

Cargo.lock also picks up a windows-sys 0.59 -> 0.61 bump for several
Windows-only transitive dependencies. That is the resolver on a newer
toolchain rather than anything this change asks for, and it is left as
cargo wrote it: reverting the references by hand would orphan the 0.61
entry and break --locked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WrSzGnURZoupdEfVdwk9pg

zmaril commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

The gate failure is upstream, not in this diff

Eleven of twelve checks pass. gate fails on its last step, scripts/publish.sh --dry-run:

error: failed to prepare local package for uploading
Caused by:
  failed to select a version for the requirement `beamte = "^0.2"`
  candidate versions found which didn't match: 0.1.0

The [patch.crates-io] table in this PR is what lets cargo build and cargo test resolve beamte — and the musl release build in the same job passes, so the code is fine. But cargo publish ignores [patch] by design: a published crate cannot carry a patch table, so the dry run resolves beamte = "^0.2" against crates.io for real, and crates.io still only has 0.1.0.

The gate is right. straitjacket genuinely cannot be released until beamte 0.2 is on crates.io.

Why beamte 0.2.0 isn't there

v0.2.0 was tagged and the release ran, but its crate job failed:

publishing with the stored bootstrap token; delete it once trusted publishing is configured
   Uploading beamte v0.2.0
error: failed to publish beamte v0.2.0 to registry at https://crates.io
Caused by:
  the remote server responded with an error (status 403 Forbidden):
  this token does not have the required permissions to perform this action

verify passed and the package built and verified — the 403 is the upload itself. The stored CARGO_REGISTRY_TOKEN published 0.1.0 (creating the crate) but is refused for 0.2.0 (updating it), which is the crates.io token-scope split: a token holding only publish-new can create a crate and cannot publish further versions of it. Crate-scoping is ruled out, since the same token worked for 0.1.0, and an expired or revoked token would 401 rather than 403.

What unblocks it

Either fix is a beamte repository-settings change, so it isn't something this PR can carry:

  1. The path the workflow already wants. Add a Trusted Publisher for beamte on crates.io — owner PowderworksCode, repo beamte, workflow release.yml, environment crates-io — then delete the CARGO_REGISTRY_TOKEN secret. release.yml guards the auth action on if: env.HAS_STORED_TOKEN == 'false', so removing the secret is what switches it to short-lived OIDC tokens. Its own comment asks for exactly this.
  2. Or replace the secret with a token scoped to publish-update.

Then re-run the release: it takes workflow_dispatch with tag: v0.2.0, so the existing tag can be republished without a new one.

Once 0.2.0 is on crates.io, the [patch.crates-io] table here should be deleted — beamte = "0.2" above it is already written for the registry — and gate goes green with no other change.

I'm watching this PR and will drop the patch table and push as soon as the crate is published.


Generated by Claude Code

zmaril pushed a commit that referenced this pull request Aug 30, 2026
A `[patch.crates-io]` entry naming a branch stops resolving the moment
that branch is merged and deleted, and the failure lands on whoever
pushes next rather than on whoever merged. That is how #63's CI broke:
beamte's branch went away under a PR that had not changed, and cargo
could not resolve the dependency before a single check ran.

A merged commit stays reachable, so the rev survives the merge. The
table is temporary either way -- `beamte = "0.3"` above it is already
written for the registry, and this whole block goes when 0.3 publishes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WrSzGnURZoupdEfVdwk9pg

This branch has not been deployed

No deployments
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.

2 participants