Skip to content

Verify releases from outside the tree, and extract the arrival-time solver - #15

Merged
SuperInstance merged 6 commits into
masterfrom
claude/init-1wgqn0
Aug 24, 2026
Merged

Verify releases from outside the tree, and extract the arrival-time solver#15
SuperInstance merged 6 commits into
masterfrom
claude/init-1wgqn0

Conversation

@SuperInstance

Copy link
Copy Markdown
Owner

Two commits, one about proving what ships and one about what the solver actually is.

plainsong spec called finding nothing a pass

It printed no specs found and exited 0. That is the exact shape of the fault it exists to catch: the spec files once sat outside the package, so every pip install shipped without them — and every install, and every CI job that ran plainsong spec, read that zero as a pass. The self-verification the whole design leans on was doing nothing, loudly enough to print a warning and quietly enough that no exit status moved.

It exits 1 now and says which of the two cases happened: an install missing its spec files, or a --tag nothing carries. This is a behaviour change — a script treating 0 as success against a spec-less install will now see a failure, which is the point.

A release is now verified from outside the tree

Everything in tests/ runs with the repository on sys.path, which is structurally blind to packaging. tools/verify_release.py never imports plainsong: it builds a wheel, installs it into a throwaway virtualenv outside the source tree, drives the console script from /tmp with PYTHONPATH stripped, then repeats against what is actually on PyPI — including a real JSON-RPC stdio session against the MCP server and a check that the sibling's loopback re-export is the compiler's own function.

CI gains a packaging job running --stage wheel, so a data file that stops being packaged fails a pull request rather than a release.

Two things it had to learn by being wrong first:

  • setuptools copies the package into build/lib and reuses whatever is already there, so a data file that has stopped being packaged still reaches the wheel from the last build that did include it. A broken package then verifies perfectly. The script clears build/ and *.egg-info first.
  • The packaging canary cannot trust an exit status alone (see above), so it requires that specs were found as well as that none failed.

Run against 1.4.0 on PyPI: 26 checks, 0 failures.

Extract the arrival-time solver as a domain-neutral core

A written time is an arrival time, and the solver works backwards to when each participant must act. None of that is about music — the equation is six lines of float arithmetic wearing instrument names. speech is actuation latency, p_center is systematic bias, and the reference/observed propagation split is "the conditions this was tuned for" against "the conditions it is in".

coordinate/ is that equation with the vocabulary removed, plus interval division and a medium that is a parameter rather than an assumption. One file, standard library only, Apache-2.0 so it can sit beside the robotics ecosystem, which is Apache-2.0 throughout.

It is staged here, not adopted. Nothing under plainsong/ imports it and it is not in the wheel. It lives here because this is the only place the proof can run; it belongs in its own repository.

The proof is the point

coordinate/test_equivalence.py drives coordinate and plainsong.perform.solve with the same inputs over ~4,000 combinations and requires bit-identical results — assertEqual, not assertAlmostEqual, because a reordered sum is a different implementation even when it is close, and close is what accumulates.

Reordering a single addition in solve_one — same terms, same mathematical value — fails 1,803 of those cases. So the extraction is inert by measurement rather than by reading.

It also pins the four claims that make it general rather than decorative: the effect lands where written at the reference point and does not away from it, spread is zero at the tuning point and positive elsewhere, and intent survives compensation while lead does not.

Checks

  • 693 tests pass; spec 7/7; check 6,336 files / 4 warnings; fingerprint 6,321 files unchanged
  • ruff check plainsong tests tools coordinate clean
  • Wheel built and installed into a clean venv outside the tree: spec passes, a real compile produces arrangement.notes=28, and the DNS-rebinding guard is correct in the shipped package

Generated by Claude Code

claude added 6 commits August 23, 2026 21:29
`plainsong spec` printed "no specs found" and exited 0. That is the exact
shape of the fault it exists to catch: the spec files once sat outside the
package, so every pip install shipped without them, and every install and CI
job that ran `plainsong spec` read that zero as a pass. The self-verification
the design leans on was doing nothing, loudly enough to warn and quietly
enough that no exit status moved. It exits 1 now and says which of the two
cases happened.

Found while writing tools/verify_release.py, which exists because everything
in tests/ runs with the repository on sys.path and is therefore blind to
packaging. It builds a wheel, installs it into a throwaway venv outside the
source tree, drives the console script from /tmp with PYTHONPATH stripped,
then does the same against PyPI -- including a real JSON-RPC stdio session
against the MCP server and an identity check that the sibling's loopback
re-export is the compiler's own function.

Two things it had to learn, both by being wrong first. setuptools reuses
whatever is already in build/lib, so a data file that has stopped being
packaged still reaches the wheel from the last build that included it; the
script clears build/ and *.egg-info first. And the packaging canary cannot
trust an exit status alone, so it now requires that specs were found as well
as that none failed.

CI gains a packaging job running --stage wheel, so a data file that stops
being packaged fails a pull request rather than a release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBAjxy7cD6DzJ72NX8TJEc
A written time is an arrival time, and the solver works backwards from it to
when each participant must act. That idea is the one genuinely novel thing in
this repository and none of it is about music: the equation is six lines of
float arithmetic wearing instrument names. `speech` is actuation latency,
`p_center` is systematic bias, and the reference/observed propagation split is
"the conditions this was tuned for" against "the conditions it is in".

coordinate/ is that equation with the music vocabulary removed, plus interval
division and a medium that is a parameter rather than an assumption. One file,
standard library only, Apache-2.0 so it can sit beside the robotics ecosystem,
which is Apache-2.0 throughout. It is staged here because this is where the
proof can run; it belongs in its own repository and nothing under plainsong/
imports it.

The proof is what matters. test_equivalence.py drives coordinate and
plainsong.perform.solve with the same inputs over ~4,000 combinations and
requires bit-identical results -- assertEqual, not assertAlmostEqual, because a
reordered sum is a different implementation even when it is close, and close is
what accumulates. Reordering a single addition in solve_one, same terms and
same mathematical value, fails 1,803 of those cases. So the extraction is inert
by measurement rather than by reading.

It also pins the four claims that make the thing general rather than decorative:
the effect lands where written at the reference point and does not away from it,
spread is zero at the tuning point and positive elsewhere, and intent survives
compensation while lead does not.

One test assertion was wrong on the first run and is worth keeping the lesson
from: accumulating a twelfth twelve times lands on exactly 1.0, while a seventh
does not. Which divisors drift is not guessable, so the test now measures them
instead of asserting a favourite example.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBAjxy7cD6DzJ72NX8TJEc
This file says its rules are faults that were actually paid for, which only
works if the file is true. Each of these was checked by running the thing, not
by reading around it.

`EbMaj7`, `G7alt` and `CM7` were described as spellings the chord parser does
not accept, "still open". All three parse. The entry is kept, because the
lesson generalises and the fix does not: the warning on an unrecognised token
is what found them, and before it an unreadable chord and a deliberate rest
were the same silence with the compiler reporting ok for both.

The songbook's bar-count warnings were given as 2. There is 1 -- the Hungarian
Rhapsody's `time: 2/4 (Lassan) then 4/4 (Friska)`, a human annotation the metre
field cannot express, left alone deliberately because changing the metre would
change the music. Naming it is more useful than counting it.

"Every command takes --json" is true only for the global position. Written
after the subcommand, argparse refuses the whole invocation with
`unrecognized arguments: --json`. A reader following that sentence literally
gets an error, which is how tools/verify_release.py came to report two false
failures. The totals also live under `arrangement`, so the note count is
`arrangement.notes` rather than a top-level key.

Verified unchanged: songbook 3,824 charts, fakebook archive 2,484, 27 MCP
tools. 693 tests pass, check reports 6,336 files, and the corpus fingerprint is
untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBAjxy7cD6DzJ72NX8TJEc
I chose Apache-2.0 for the patent grant and to match the robotics ecosystem.
The stated priority is distribution over credit, and that inverts the trade.

Apache-2.0's obligations run against exactly this file's purpose. It is one
file meant to be copied next to whatever needs it -- onto a Pi beside a
steering pump, with no packaging step -- and Apache-2.0 asks that vendored copy
to carry a NOTICE and to state that files were changed. MIT asks it to keep one
paragraph. When the goal is that the code travels, the licence with fewer
conditions on travelling is the right one.

It also makes the stack one licence rather than two. plainsong is MIT; a single
licence across the ecosystem means no compatibility matrix for anyone
assembling pieces, and MIT is the text every legal review already has a stamp
for. Apache-2.0 and GPL-3.0 can both still consume MIT, so nothing downstream
is foreclosed -- including MAGDA.

The patent grant is the real thing given up. It is worth less here than it
looks: this is arithmetic rather than a mechanism, and Apache's grant protects
users from *contributors'* patents, which matters most in projects with many
corporate contributors. This has none yet.

If attribution should be dropped entirely, 0BSD is the next step and is a
one-file change from here. MIT is the more conservative choice and the more
widely recognised one, so it is where this starts.
The lessons from this week were living in commit messages, which the next
person does not read. docs/verification.md collects them, deliberately written
so nothing in it is about music -- the same faults turned up in five sibling
repositories and the file should be portable to any of them.

Six rules, each with the incident that paid for it. Success is not evidence:
`plainsong spec` printed "no specs found" and exited 0, and a sibling's CI
suppresses its own build with `|| true`, so neither has ever demonstrated
anything. A guard you cannot fail is not a guard: two TimeGrid guards were
redundant and no mutation could fail a test until one was removed. Two copies
drift into the same bug, not different ones -- both loopback copies accepted
`127.evil.example` and both mangled `[::1]`. Verify by doing: PyPI's API
reported a stale version twice, a suite on sys.path cannot see a packaging bug,
setuptools reuses build/lib so a broken package verifies perfectly, and a pipe
eats the exit code. A name is not a description and a README is not a fact.
Report what happened, including when the two failures in your own verification
run turn out to be your harness rather than the product.

CLAUDE.md points at it from a new section and its command list now carries
verify_release.py beside the demo differential, both being checks the test
suite structurally cannot perform.
plainsong-mcp 1.0.0 is on PyPI, so for the first time there is somewhere to
send people. The command still works and will until 2.0; it now warns and names
the replacement.

The duplication has already cost twice. A DNS-rebinding fix existed in this copy
and not the sibling for months -- in the copy people pip install for MCP. Then
the same eight lines got the same two things wrong in both, because the second
was a copy of the first. Neither repository could notice either time.

The notice goes to stderr, and that is not a detail. In stdio mode stdout *is*
the protocol: a deprecation line printed there would desynchronise every client,
turning a courtesy into an outage. PLAINSONG_NO_DEPRECATION=1 silences it for
anyone scripting against the old command.

The rule is enforced rather than remembered. TestDeprecationNoticeStaysOffTheWire
drives a real subprocess and parses every line of its stdout as JSON, so a future
edit that prints the notice to the wrong stream fails the suite instead of
breaking clients quietly.
@SuperInstance
SuperInstance merged commit dacdb32 into master Aug 24, 2026
2 of 17 checks passed
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