punctilio (n.): precise observance of formalities.
Pretty good at making your text pretty. The most feature-complete and reliable English typography package. punctilio transforms plain ascii into typographically correct Unicode, even across html element boundaries. Try it live at turntrout.com/punctilio.
Smart quotes · Em / en dashes · Ellipses · Math symbols · Legal symbols · Arrows · Primes · Fractions · Superscripts · Ligatures · Non-breaking spaces · html-aware · Markdown support · Bri’ish, German, and French localisation support
import { transform } from "punctilio";transform(`"It's a beautiful thing, the destruction of words..." -- 1984`);// → “It’s a beautiful thing, the destruction of words…”—1984
punctilio accepts three input formats: text, Markdown, and html. Use it as a library, a CLI, a pre-commit hook, or—for a zero-friction install in any project that already runs Prettier—a Prettier plugin.
I open source my website infrastructure and article edit histories at alexander-turner/TurnTrout.com. I license the repository under CC by-sa 4.0, which means you can share and adapt the site as long as you provide attribution and distribute any derivative works under the same license.
When I started writing in 2018, I didn’t include alt text. Over the years, over 500 un-alt’ed images piled up. These (mostly) aren’t simple images of geese or sunsets. Most of my images are technical, from graphs of experimental results to hand-drawn AI alignment comics. Describing these assets was a major slog, so I turned to automation.
To implement accessibility best practices, I needed alt text that didn’t describe the image so much as communicate the information the image is supposed to communicate. None of the scattershot AI projects I found met the bar, so I wrote my own package.
alt-text-llm is an AI-powered tool for generating and managing alt text in Markdown files. Originally developed for this website, alt-text-llm streamlines the process of making web content accessible. The package detects assets missing alt text, suggests context-aware descriptions, and provides an interactive reviewing interface in the terminal.
Generating alt text for maze diagrams from Understanding and Controlling a Maze-solving Policy Network.alt-text-llm displays the surrounding text (above the image), the image itself in the terminal using imgcat, and the llm-generated alt suggestion. The user interactively edits or approves the text.
In the end, I generated over 550 high-quality alt-text suggestions for about $12.50 using Gemini 2.5 Pro. With alt-text-llm, I addressed hundreds and hundreds of alt-less images: detecting them; describing them; reviewing them; and lastly applying my finalized alts to the original Markdown files. turntrout.com is now friendlier to the millions of people who browse the web with the help of screen readers.
Dataset contamination is bad for several reasons. Most obviously, when benchmarks are included in AI training data, those benchmarks no longer measure generalization—the AI may have been directly taught the answers. Even more concerningly, if your data promote negative “stereotypes” about AIs, they might become self-fulfilling prophecies, training future models to exhibit those same behaviors.
In the Claude 4 system card, Anthropic revealed that approximately 250,000 transcripts from their alignment faking paper had been scraped from the public web and included in their pretraining data. This caused an early model to hallucinate details from the paper’s fictional scenarios, forcing Anthropic to implement unique mitigations. Speculatively, this kind of misalignment data could degrade the alignment of any models trained thereafter.1
Data scraping practices are a serious problem. The tool we are currently releasing will not stop state-of-the-art actors. Since I wanted to at least mitigate the problem, I put out a bounty for a simple, open source tool to harden data against scraping. The tool is now ready: easy-dataset-share. In less than 30 minutes and at a cost of $0, you can deploy a download portal with basic protections against scrapers, serving a canary-tagged dataset with modest protections against AI training.
easy-dataset-share will not stop sophisticated scrapers
Sophisticated scraping operations can bypass Cloudflare Turnstile for about $0.001 cents per trial (via e.g. CapSolver). The robots.txt and Terms of Service are not technically binding and rely on the good faith of the user, although the ToS does provide limited legal deterrence. Canary strings can be stripped from documents. Overall, this tool is just a first step towards mitigating dataset contamination. We later discuss improvements which might protect against sophisticated actors.
One command to set up your shell, editor, and secret management.
My .dotfiles repository provides comprehensive development environment setup. With this command, I quickly personalize any shell—even if I’m just visiting with ssh for a few hours.
Fish shell with autocomplete, syntax highlighting, and the tide theme,
neovim via LazyVim, providing a full ide experience,
tmux with automatic session saving and restoration,
envchain for hardware-encrypted secret management via macOS Secure Enclave or Linux gnome-keyring—no more plaintext api keys in configuration files,
Open source AI tool setup,
autojump for quick directory navigation,
Reversible file deletion by default via trash-put instead of rm,
git aliases and other productivity shortcuts, and—drum roll—
goosesay, because every terminal needs more geese.
______________________________________ / Find out just what any people will \ | quietly submit to and you have the | | exact measure of the injustice and | | wrong which will be imposed on them. | \ --- Frederick Douglass / -------------------------------------- \ \ \ ___ .´ ""-⹁ _.-´) e _ '⹁ '-===.<_.-´ '⹁ \ \ \ ; \ ; \ _ | '⹁__..--"" ""-._ _.´) / ""-´ _> : -´/ ; .__< __) \ '._ .__.-' .-´ '⹁_ '-⹁__.-´ / '-⹁__/ ⹁ _.´ ____< /'⹁__/_."" .´.----´ | | .´ / | | ´´-/ ___| ; <_ / `.'´
Each time I open the fish shell, a rainbow goose blurts out an interesting phrase. I spent several hours to achieve this modern luxury.
if status is-interactive fortune 5% computers 5% linuxcookie 2% startrek 88% wisdom | cowsay -f ~/.dotfiles/apps/goose.cow | lolcat -S 6end
The way this works is that:
I sample a saying by calling the fortune command,
I pipe the saying into goosesay (my variant of the cow in the original cowsay),
The lolcat command splays the text ’cross the rainbow.
The original subfont traced font usage from scratch on every page. That took almost two hours per deploy. My fork groups pages by their css and only traces one representative per group, extracting just the text from the rest (for my site: 382 ⭢ 5 css traces). Those remaining traces run in parallel across worker threads.
alexander-turner/claude-automation-template packages my automation workflows into a reusable starting point for any project using Claude Code. The template is designed so that adopting repos get improvements automatically via the sync workflow—fix a bug in the template, and every downstream project picks it up.
When handling dangerous materials, a chemist doesn’t yolo it barehanded on the open bench. They reach through a glovebox to do their work.
Right now, you’re probably barehanding AI, tossing it into a shell onto your machine with access to your credentials. At most, you’re shielded by a single prompted “auto-mode” classifier. Doing better is hard and while there are a few papers on best practices, it’s not clear how to do it properly and you have experiments to run.
After all, what’s the chance that something bad happens?
glovebox is a sealed enclosure that only gives the agent what it needs to do your work. Tap-tap-tap glovebox and press Enter to spin up a hardware-isolated, allowlist-firewalled microVM, employing input / output sanitization to strip injections and tricks the AI might play on you, with an AI monitor with a red-alert ability to push-notify your phone and halt the AI until you return. The goal is a minimal-friction secure experience that gets the job done.
Disclaimer: I’m an AI professional but not a security professional. I welcome issues and PRs.
Make your CI confess what it’s hiding. A pack of fast, offline pre-commit
lints that catch two kinds of lie a green check can hide:
Honesty lies: the pipeline reports success even though the real work
failed—a failing command’s exit code gets hidden by a pipe—or a required
check never reports at all and the PR hangs forever.
Identity lies: a base image or downloaded file is pinned to a mutable
name (a tag, a bare url) that can change under you, so the bytes you run
aren’t provably the bytes you reviewed.
Catches a pipeline where a failing command’s exit code is hidden by the last command in the pipe. Example: pytest | tee log reports tee’s success even if pytest crashed, so CI goes green. Fires when a run: step uses a shell that doesn’t set pipefail.
check-exit-suppression
Catches cmd || true, which throws away a command’s failure but keeps its output, so a broken step still reports success. Example: a cleanup step that failed to release a resource looks like it worked.
check-stderr-suppression
Catches cmd 2>/dev/null, which throws away error messages. When the command fails you get a bare non-zero exit and no clue why. Example: docker compose up 2>/dev/null hides the reason the container wouldn’t start.
check-substitution-exit-swallow
Catches a loop fed by jq/yq whose exit code is ignored, so a parse error looks like “nothing to do.” Example: done < <(jq …) or jq … | while read—if jq chokes on a renamed key or bad input it exits non-zero, the loop runs zero times, and the failure goes unnoticed. Limited to jq/yq. Opt out with # allow-substitution-exit: <reason>.
check-pr-paths
Catches a required check that never runs and leaves the PR stuck at “Expected—Waiting” forever, so it can never merge. A paths:, paths-ignore:, or branches: filter on pull_request can skip the whole workflow silently. Example: a stacked PR whose base isn’t main gets skipped by a branch filter.
check-pipefail-grep-pipe
Catches cmd | grep -q … under pipefail: grep exits as soon as it finds a match, which kills the producer with sigpipe and surfaces as exit 141 (looks like “no match”). Example: a check that confirms a secret was removed wrongly reports it gone once the listing is large enough to fill the pipe buffer.
| check-folded-scalar-comment | Catches a line written as a # comment inside a folded (>/>-) yaml block scalar. A folded scalar has no comment syntax, so the line is folded into the value—and when that value is an argument string something shell-splits, the # starts a shell comment and every argument after it is discarded. Example: a claude_args: >- block gains an explanatory comment, and the --allowedTools/--add-dir flags below it never reach the CLI—the diff reads as though the scoping were in force while it is absent. Only judges a folded block that also carries a --leading line (an argument string, not prose); literal | scalars keep newlines and are out of scope. Opt out with # allow-folded-scalar-comment: <reason>. |
| check-gh-slurp-jq | Catches a gh api call gh refuses to run: --slurp alongside --jq/--template, or --slurp with no --paginate. Both are rejected while gh parses its arguments, before any request goes out, so the call exits non-zero on every run—it has never worked. Worse than a loud bug: a site that swallows the failure reads as a permanent green, and one that doesn’t reds a scheduled job 100% of the time. Example: a pagination helper written with --slurp --jq '.[]' that has silently returned nothing since the day it was added. Reads backslash-continued calls whole, so --jq on a later line still counts. Opt out with # allow-gh-slurp-jq: <reason>. |
Catches a Docker base image pinned to a tag the registry can quietly re-point to different bytes. Example: FROM node:22 today may not be the same image tomorrow. Requires a @sha256: digest so the image you reviewed is the image CI builds.
check-pinned-downloads
Catches downloading a binary and running it with no checksum or signature check, so a tampered release or hacked mirror can swap it. Also flags one-line installers like curl -fsSL … | sudo sh, which pipe unverified bytes straight into a shell.
check-provenance-repo-url
Catches a package.json (or pyproject.toml) whose repository url still points at the repo it was forked from. Example: a fork’s first npm publish --provenance fails with E422 … Failed to validate repository information because the url names the upstream, not this fork. Compares the declared repository url against your origin remote (never Homepage). A mismatch has no opt-out—forks must fix their url. Repos with no origin remote are skipped.
Catches the “pwn-request” hole, where a pull_request or pull_request_target job checks out the PR’s own code and runs with write permissions or secrets. Example: an outside contributor’s code then runs with your repo’s credentials and can steal your secrets. Also catches the same job checking out an author-chosen base ref—a PR’s base is a branch the author picks, so a job that stages “the base branch” can be pointed at a branch carrying a rewritten build script while the PR’s own diff stays clean. Narrowing the base to `main
check-untrusted-exec
Catches the same job actually executing the checked-out pull-request code while secrets are live—a local composite action (uses: ./…, whose manifest GitHub reads from the workspace), a package-manager script (pnpm build, npm run test, make release, npx …, whose body comes from the checked-out package.json/Makefile), or a workspace-relative path (bash ./scripts/x.sh). Example: the PR rewrites package.json’s build script, and your release pat is in the environment when it runs. Reaches jobs check-trusted-base cannot see—a matrix-derived head ref, an unpinned workflow_run head—and, deliberately, a job whose # trusted-base-ok reason claims it “only runs the trusted base copy” while the entrypoint itself still comes out of the workspace. Staging scripts into $RUNNER_TEMP is not a defence: an earlier attacker step in the same job owns $GITHUB_ENV/$GITHUB_PATH and that directory. Opt out with # untrusted-exec-ok: <reason> in the job block.
| check-unscoped-tool-grant | Catches a Claude Code tool grant (--allowedTools, an *ALLOWED_TOOLS env var, an allowed_tools: input) that is unscoped or inert. A file tool named with no path—Read, Grep, Glob, Write, Edit—is a whole-tool grant applied after the per-path working-directory check and overriding its verdict, so an --add-dir beside it suppresses a prompt rather than building a jail. And a path rule spelled under any name but Read or Edit is a rule nothing consults: Write(//out.json) denies the write it appears to grant (Edit(...) is the spelling that confines Edit, Write, MultiEdit and NotebookEdit; an absolute path needs two leading slashes). Established by probing the real CLI headless against a stub api, each case against a matched denying control—undocumented internals, so the docstring names the version and tells you to re-probe on a much newer CLI. Opt out of the unscoped half with # allow-unscoped-read-grant: <reason>/# allow-unscoped-write-grant: <reason>—worth it when the same job grants bare Bash, since a redirect writes anything regardless. The inert half has no opt-out: a rule nothing consults is never the right thing to write. |
Catches a required check left stuck at “Expected—Waiting” when a gate job skips all the real work jobs. Assumes you use a decide-job plus an always() reporter job.
check-required-reporter
Catches a new always() reporter that is green but was never added to the branch-protection required list, so it doesn’t actually gate anything. Assumes the required list is mirrored from these annotations.
check-job-timeout
Catches a job with no timeout-minutes, which inherits GitHub’s 360-minute default. Example: a hung test or a network fetch with no deadline can hold a shared runner for six hours. Requires every job to set its own limit; jobs that call a reusable workflow (uses:) are exempt. Opt out with # allow-no-timeout: <reason>.
check-inline-run-length
Catches long inline run: scripts that ship unchecked, because shellcheck / shfmt / shellharden only inspect standalone .sh files, not inline blocks. A long inline script can hide bugs like unquoted variables or a missing pipefail.
check-concurrency
Catches a concurrency: block missing cancel-in-progress, which defaults to false. New pushes then queue behind stale runs instead of cancelling them.
check-static-concurrency
Catches a workflow-level concurrency.group with no per-ref key (no github.ref/head_ref). A run from another branch can then cancel this one’s pending run before any job starts, so its always() reporter never fires and a required check hangs forever.
check-pending-cancel-concurrency
Catches a config that turns a required check red for no real reason. If on.pull_request.types includes types beyond opened / synchronize / reopened (e.g. labeled—a Dependabot PR is born with labels), several runs queue on the same commit; a per-PR concurrency.group then lets GitHub cancel one same-commit run, and its always() reporter reports “cancelled” (a red X with no actual failure). cancel-in-progress can’t help. Fix: drop the group or key it on github.run_id. Opt out with # pending-cancel-ok.
check-requires-concurrency
Catches a pull_request/pull_request_target workflow with noconcurrency: block at all, so every push to a PR starts a second full run instead of cancelling the old one—wasting a limited runner pool. (check-concurrency only checks a block that exists; this one requires the block.) Satisfied by a block at the workflow or job level. Opt out with # concurrency-not-required.
check-externalized-markers
Catches a workflow guard that scans inline run: for a required marker but goes blind when that command moves into a .github/scripts/*.sh file or a composite action. Example: a guard that requires fetch-depth: 0 whenever a history-rewrite command runs stops seeing the command once it’s in a script, and passes even though the requirement is now unmet.
check-path-gate-deps
Catches a gated job that skips—and its always() reporter goes green—on the exact PR that changed a file the job needs, because the decide job’s path filters left out a composite action or .github/scripts/ helper. Checks that every gated job’s dependencies (composites, sourced scripts one hop deep, and # gate-deps:-declared paths) are covered by the filters. Suppress one dependency with # path-gate-ok: <dep> <reason>.
check-failure-notifier-coverage
Catches a new push / schedule workflow that fails silently because it was never added to the failure notifier’s on.workflow_run.workflows list (workflow_run has no wildcard, so the list is hand-maintained). The notifier is found by shape—a workflow triggered on.workflow_run that names a notification sink—never by filename, and finding none is a failure, not a silent pass. Every listed name must match a real workflow, and the list must cover every push / schedule workflow whose failure reaches nobody else—not the whole tree: one that self-notifies on failure, that also runs on pull_request (its red is already a check on the PR), or that carries a reasoned # cron-alert: false is somebody else’s responsibility, and demanding it here would double-page or overrule the opt-out. Watching more than that minimum is allowed. Prints the corrected block on mismatch. --allow-no-notifier passes when no notifier exists at all; --notifier FILENAME names the file the fail-closed message should suggest (default ci-failure-notify.yaml); --notifier-pattern REGEX (repeatable) teaches it a house sink; --require-alert-priority also fails a step calling a local composite action that defaults priority: without stating one, since an omitted priority silently means that action’s maximum.
check-token-fallback
Catches a token that silently switches identity, like token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }}. The day someone sets PAT, pushes start using a different identity—which can break permissions in surprising ways. Flags any secrets.A || secrets.B in a token position (a token:/github-token: input, or a GITHUB_TOKEN/GH_TOKEN env var). Opt out with # token-fallback-ok: <reason> when the switch is intentional.
check-workflow-secret-names
Catches a misspelled or renamed secret reference. A wrong secrets.* name just evaluates to empty, so the feature quietly degrades with no error. Example: reading secrets.ANTHROPIC_API_KEY when the real secret is GH_ACTION_ANTHROPIC_API_KEY. Requires every secrets.*/vars.* name under .github/ to match the checked-in .github/workflow-secrets.txt allowlist (GITHUB_TOKEN is implicit). Prints the corrected file on mismatch.
check-pin-comment-truth
Catches wrong or inconsistent version comments on sha-pinned actions—the comment is the only human-readable part of a pin. Example: the same actions/checkout@<sha> is labeled # v6 in one place and # v7.0.0 in another, so at most one is true. Requires every sha-pinned uses: to carry a well-formed # v<number> comment, and one sha to use one comment string across the repo. No network lookups. Opt out with # pin-comment-ok.
check-stderr-merge-parse
Catches parsing a stream that merged stderr into stdout with 2>&1, where a warning line can be mistaken for real output. Example: an npm warning becomes “the version” and every release aborts on the bogus value. Flags a 2>&1 capture piped into a parser (head/tail/grep/awk/cut/sed/jq/sort/wc) or used in a [[ … ]]/(( … )) comparison. A plain out=$(cmd 2>&1) used only for echo/printf (diagnostics) is fine. Opt out with # stderr-merge-ok: <reason>.
check-echo-fallback
Catches $(cmd || echo "…"), which turns a failure into a normal-looking value that later code trusts. Example: a release step reads the literal string error as the version. Flags || echo/|| printf inside command substitutions and as bare statements. A fallback that writes to stderr and aborts (|| { echo … >&2; exit 1; }) is real recovery and passes. Opt out with # echo-fallback-ok: <reason>.
check-lockstep-pins
Catches two pins that are supposed to stay in sync but are linked only by a comment. Example: a .pre-commit-config.yamlrev: and a workflow’s pip install git+…@<sha> that must name the same release. Config-driven: each --pair FILE1 REGEX1 FILE2 REGEX2 (one capture group each) must match exactly once per file and the two captures must be equal—zero or multiple matches is a hard error. Not in the check-tier2 aggregate (it needs per-repo args); enable it on its own.
Catches a committed symlink pointing at an absolute path (/Users/you/...), which works only on the author’s machine and breaks everywhere else.
check-unnamed-regex-groups
Catches a re.* pattern that uses a plain ( ) group, which forces brittle position-based match handling. Named groups (?P<name>…) are clearer and survive edits.
check-global-stdio-swap
Catches code that reassigns the process-global sys.stdout to capture output. Under concurrency, calls then clobber each other’s output.
check-claude-model
Catches a claude-code-action step with no --model, which rides the action’s expensive default tier and can bill for Opus without you meaning to.
check-drift-guards
Catches a “these copies must agree” (drift-guard) test that never says why a single source of truth isn’t feasible—so the duplication it polices keeps drifting anyway. Requires @pytest.mark.drift_guard("<why no SSOT is feasible>") on any Python test that reads as a drift guard, so the judgement is reviewed, not implied. JS / TS / shell suites (*.test.mjs, bash tests) can’t use the decorator, so a phrase check covers them—annotate a flagged line with drift-guard-ok: <why no SSOT is feasible>.
check-graceful-handwave
Catches vague claims like “fails gracefully” in docs or comments—they promise nothing (which input? which exit code?), so no one can tell if the behaviour is real. Scans prose (Markdown / rst) line by line and code comments only. Opt out by stating what actually happens: allow-graceful: <what happens>. Pass --prose to scan a plain text file (e.g. a PR body) line by line.
check-historical-comments
Catches a comment describing the past (“renamed from X”, “now uses Y”). The reader can’t see the old code, so the note was never verifiable and rots into a lie. Bans only words with no present-tense reading. Opt out (e.g. a reader of a legacy on-disk format) with # allow-history: <reason>.
check-doc-line-refs
Catches a doc that cites source by line number, which points at whatever happens to live there after the next refactor. Bans <file>.<ext>:<N> and (L<N>) cites in Markdown (fenced code blocks and any CHANGELOG.md are skipped). Cite a function, section, or anchor instead, or suppress with ``.
check-flag-arity
Catches a CLI flag that reads its value without checking one was passed, so it dies with $2: unbound variable instead of a clean “—branch needs a value.” Example: a --branch) X="$2"; shift 2 arm passed as the last argument. Flags any case arm for a -x/--xxx/--xxx=* option that reads $2/shift 2 with no guard that both comes first and actually bails ([[ $#-ge-2]] || die, ${2:?…}, or a need_val/need_arg helper). A bare [[ $#-ge-2-1]] whose result is discarded, or a guard after the read, doesn’t count. Suppress with # flag-arity-ok: <why>.
check-secret-file-perms
Catches a credential file (*token*, *.pem, *npmrc*, …) created world-readable and only chmod 600’d a few lines later—a co-tenant can read it in that window. Flags a secret-named create (>/>>, touch, tee, non-private install) tightened by a later chmod 0?[46]00 on the same path within ~3 lines. A umask 077 or install -m 600 up front is accepted, and a create with no nearby chmod isn’t flagged. Suppress with # secret-perms-ok: <reason>.
check-case-default
Catches a shell case with no default arm, so an unexpected value matches nothing and the script runs on. Example: an unknown bump-type leaves NEW_VERSION unset and the release continues on garbage. Requires a bare *) (or (*)/* )/ a x|*) alternative) default arm on every case … esac; globs like *.txt) or --*) don’t count. Opt out with # case-default-ok: <reason> on the case line.
check-cron-comment
Catches a schedule comment that contradicts its cron. Example: the comment says “daily” but the cron runs weekly, so the job runs 1/7th as often as everyone thinks. Pairs a cadence word (hourly/daily/weekly/monthly/every N minutes|hours|days) in a comment on or within 3 lines above a cron: line with the expression, and fails only on a clear contradiction—lists, ranges, and exotic crons always pass. Opt out with # cron-comment-ok.
check-cron-alert-coverage
Catches a scheduled workflow whose failures reach nobody. A cron has no PR surface—no check run, no reviewer—so its red lands in an Actions tab nobody opens and a guard that has been failing for six weeks looks exactly like one that has been passing. By default flags only the two lies: a malformed # cron-alert: marker, and a notification step sitting behind a success-only gate (if: success(), a job gated needs.x.result == 'success'), which reads as coverage in review and fires exactly never. Pass --require-alert to also demand every scheduled workflow route failures to a human—by a step gated if: failure()/.result != 'success', or by a failure notifier in the tree listing it in on.workflow_run.workflows (the same predicate check-failure-notifier-coverage uses, so the two never demand opposite things of one workflow). A PR trigger does not count here: a cron fire has no pull request. --notifier-pattern REGEX (repeatable) teaches it a house sink on top of the built-in ones (ntfy, Slack, PagerDuty, gh issue create, …). Opt out with # cron-alert: false # <reason> on the schedule: (or push:) key.
check-cron-staleness-optout
Catches the cron that produces no failure because it produces nothing—GitHub disables a dormant repo’s schedules after 60 days, and a startup_failure/skipped/cancelled run is not a failed run, so a failure notifier stays correctly silent while the guard has been dead for two months. Enforces the # cron-stale: false # <reason> marker discipline (# cron-alert: false deliberately does not satisfy it: its reason argues a failed fire self-heals next cycle, which presumes there is a next cycle). --watchdog-workflow FILENAME names the runtime watcher and requires that file to exist under .github/workflows/ and itself be schedule-triggered; --require-stale-marker demands a marker on every scheduled workflow, narrowed to the watchdog alone when a valid one is declared. Without flags only a present-but-malformed marker is reported.
check-toolchain-skips
Catches a test that skips itself when a tool is missing (skipif(shutil.which("node") is None)), which silently drops all coverage of the guarded scripts on a runner without that tool while the suite stays green. Flags pytest.mark.skipif/pytest.importorskip conditions that probe for a binary (shutil.which, which(, find_executable) with no CI guard—in CI the skip must instead fail: shutil.which("node") is None and not os.environ.get("CI"). Only test files are scanned. Opt out with # toolchain-skip-ok: <reason>.
check-env-symmetry
Catches a half-finished env-var rename: the var was changed where it’s set but not where it’s read (or vice-versa), so the reader just sees an unset value and falls back to a default. Scans the whole tracked tree for every var matching a --prefix (e.g. GLOVEBOX_) and flags any that is written-but-never-read or read-but-never-written. Dynamically-built names are skipped; an out-of-band var opts out with # env-symmetry-ok: <VARNAME> <reason>. Needs args: [--prefix, <PREFIX>]; not part of a tier aggregate.
check-test-predicate-shadow
Catches a shell test that redefines a production pure predicate—a function whose body only tests and returns. Stubbing a dependency (a logger, as_root, a network call) is legitimate: there is a side effect to intercept. A predicate has none, so redefining one substitutes the test’s own copy of the logic and asserts against that copy—usually a weaker one: a valid_host_port stub matching ^[0-9]+$ accepts the leading-zero ports the shipped ^[1-9][0-9]{0,4}$ exists to reject, so the regression it covers can never fail it. Purity is decided on the real bash grammar, so anything richer than a flat [[ … ]]/(( … ))/return/true/false chain reads as impure and a dependency stub is never flagged. Source the real definition, or opt out with # predicate-shadow-ok: <reason>. A violation is a pair, so the passed file list only ever narrows the scan: the production side always self-discovers via git ls-files, and a passed production file widens the test side to the whole tree.
check-stray-tool-markup
Catches an agent’s leaked file-authoring scaffolding—a bare closing content/invoke tag (or an antml:-prefixed variant) committed onto its own line in a doc, where it renders as literal garbage that only a human caught. Flags a line that is entirely a stray tool-call tag (</invoke>, an opening invoke/parameter tag, <function_calls>, a bare closing content tag); inline mentions, inline-code spans, and fenced code blocks are never flagged. Suppress a genuine case with allow-stray-markup: <reason> on the line above.
Most prompt-injection tools run a classifier over the text and hope it
generalizes. This library targets a narrower, verifiable claim: the
specific byte-level channels—invisible Unicode, ansi escapes, human-hidden
Html, confusable glyphs, exfil-shaped urls—that let an attacker smuggle a
payload the operator can’t see but the model still reads. Every layer is a
deterministic transform you can unit-test with equality assertions.
import { sanitize } from "agent-input-sanitizer";// Layer 1 (invisible chars + ANSI), zero heavy deps:const { cleaned, found, warnings } = await sanitize(untrustedText);// Opt into the HTML layers for web ingress (lazy-loads ~200 ms of deps):const result = await sanitize(pageSource, { html: true });
sanitize never throws and never silently drops content—any change comes with
at least one warnings entry. found names the neutralized category codes
(e.g. ["cf-format", "hidden-html"]); cleaned is the safe text, with
placeholders where hidden html was spliced out.
My site uses Mermaid diagrams for compact, searchable, and dynamically styled graphics. However, the a11y accessibility checker revealed a “bigly” problem. When a page contains multiple diagrams, some element IDs collide (like those for arrowhead markers and node containers). Thus, calling url(#arrowhead) in the third diagram might bind to a marker defined in the first. Arrowheads vanish, click handlers fire on the wrong diagram, and css styles corrupt.
The issue had been reported repeatedly since 2020 (#1,318,#3,267,#3,433,#4,346) with no comprehensive fix in sight. In February 2026, I aimed Claude Code at this problem. My PR 7,410 prefixed every element ID with its diagram’s unique svg container ID, making collisions impossible. I also ensured that all future diagram types generate unique IDs across diagrams.
KaTeX’s TypeScript codebase carried dozens of escape-hatch as any casts left over from its earlier Flow-to-TypeScript migration. Each one silenced a type error instead of fixing it, so the compiler couldn’t catch bugs in those spots. PR 4,171 replaced these with proper checks and tighter type definitions, then enables a lint rule so new any casts can’t sneak back in.
About MeI’m TurnTrout, but the United States government insists on calling me “Alexander Matt Turner.” I like writing and learning about lots of stuff.