Skip to content

Setlist 1.0.9 (edition v1.6)

Choose a tag to compare

@AlexCiortan AlexCiortan released this 31 Jul 15:08
· 5 commits to main since this release

If you use Setlist on a Mac and you are on 1.0.8, update now. On macOS,
1.0.8's commit gate and close gate allowed everything. They did not crash and
they printed no error. They simply permitted every commit and every merge they
exist to check, silently, from the moment 1.0.8 was installed.

Linux and WSL were never affected. 1.0.7 and every release before it were never
affected on any platform. If you are not on a Mac, this release changes nothing
you will notice, and taking it is still the right move.

What broke, and why the platform decided it

Both gates normalise the command they are handed through a small awk program
before deciding whether the command is one they govern. In 1.0.8 that program
ended with }\' where it should have ended with }'.

GNU awk and mawk, which is what Linux ships, accept the trailing backslash
without a word. The BWK "one true awk" that macOS ships as /usr/bin/awk does
not: it reports a syntax error, exits 2, and writes nothing at all to standard
output. So the normalised command came back EMPTY. The test for "is this a
command I govern" then matched nothing, and each gate concluded, reasonably on
its own terms, that it had nothing to do and got out of the way.

The shape is worth naming because this project has now hit it twice in two
releases. An input failed to materialise, the empty result was indistinguishable
from "nothing here to govern", and absence read as permission. In 1.0.8 the
dependency that failed was jq, which was installed but could not run. Here it
was awk, which ran perfectly well and was simply STRICTER than the one the
code had been written against. Both gates now state the rule in the file, next
to the code it governs: the lexer's awk program must not end in a backslash, and
a gate whose lexer can fail must not treat lexer failure as a clean parse.

What changed

Two files, two bytes, plus the comments that explain them. No new command
spellings are recognised, no gate semantics moved, and no checks were added or
removed. Everything 1.0.8 closed stays closed, and it now closes on macOS too.

How this escaped, stated plainly

1.0.8's evidence was real. It was also platform-scoped, and the release notes
did not say so.

Every mechanical leg behind 1.0.8 ran under Linux with GNU awk, which is the one
environment where this defect cannot be observed. The macOS leg was known to be
missing at publish time. It was recorded as a debt to watch after the push
rather than a gate to pass before it, and it went red on the first run: 169 of
451 assertions failed on macos-latest while the Linux leg was green.

Two things change as a result. The macOS CI leg is now a BLOCKING pre-publish
gate, so no future release can be pushed to the default branch before a real Mac
has run the suite against the exact bytes being published. And the suite is run
locally under bash 3.2.57 with BWK awk, the same implementations macOS ships, as
part of the ordinary evidence set rather than as an optional extra.

What to do if you were running 1.0.8 on a Mac

Update, then treat any work that reached your trunk while 1.0.8 was installed as
unverified by the gates. The gates were not the only thing standing between you
and a bad merge, and in normal use /setlist:checkpoint runs the same close
checks before you ever reach the gate, so a normal session was still checked. It
is the backstop that was missing, not the whole process.

If you want to know for certain what reached the trunk unchecked, the trunk
audit reads history rather than commands and is the designed tool for exactly
this question:

bash scripts/trunk-audit.sh

Known open issues in this release

Five things about this release are disclosed here rather than left for a reader
to discover, because each is something a careful adopter would want to know. The
first four carry forward from 1.0.8 unchanged in substance.

1. This release was not reviewed by an adversarial pass against its exact
bytes.
That was true of 1.0.8 for a reason given at the time (each fix changed
the bytes the previous review had read, a loop with no exit), and it is true of
1.0.9 for a simpler one: this is a two-byte platform repair, and the argument
for a scoped gate is the same one used for 1.0.1 and 1.0.2. What stands behind
these bytes is the full mechanical evidence set, now run twice, once under Linux
and once under bash 3.2.57 with BWK awk.

2. A stale remote-tracking ref can deny an ordinary merge. If you rename a
spec branch (git branch -m spec/0004-thing feature/thing) while a
remote-tracking ref for the old name still exists, the close gate denies
git merge --no-ff feature/thing with CG-SPEC-MISSING. It is a false denial
rather than a way past the gate, so it is disclosed rather than fixed. The
remedy is one command:

git update-ref -d refs/remotes/origin/spec/0004-thing

3. Findings from the last two adversarial reviews remain open and are not
secret.
They are MAJOR and MINOR rather than blocking: none of them puts
unreviewed code on the trunk. Several are defects in the project's own checking
tools rather than in the gates you install.

4. The gates need a WORKING jq, not merely an installed one. A jq that
exists but fails to run (a broken dynamic link, a killed process, a wrong
architecture) used to make two gates allow silently. That was fixed in 1.0.8:
all four hooks run jq rather than just locating it, and each distinguishes
"missing", "present but not runnable" and "could not parse this input" so the
message names what to fix.

5. The gates are still only verified on the platforms that are actually
tested.
That is now Linux and macOS on every push, plus a local bash 3.2 and
BWK awk run before each release. It is not a claim about every shell, every awk
or every BSD. This release exists because a platform difference nobody had
tested for turned two gates off, and the honest statement is that the set of
tested platforms is a list, not a proof.

The standing framing has not changed and is worth repeating: these hooks are a
backstop against ordinary mistakes, not a sandbox. Anything that can run a shell
can reach the trunk, and the gates have never claimed otherwise. A merge
performed in a forge's web UI is not reached by them at all. The trunk audit
(scripts/trunk-audit.sh, opt-in) reads history rather than commands and is the
designed catch for that whole family.

If you already have a project running Setlist

Run /setlist:upgrade once after updating the plugin. This release changes hook
bytes but not the settings wiring, so the refresh is hook files only.

/plugin marketplace update setlist

If a Claude Code session was open when you ran that update, restart it before
upgrading.