mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-10-26 22:37:03 +00:00
dbbbefda75d4fc4aaeac7bdaecd65eab772f00a9
10 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
25adccf0b9 |
test(buildtools): the pid-proof leg picks a port the stub can bind (BUG-3519) (#1979)
TestPreServerFixtureProvesEachClaimSeparately/probed_address_is_not_the_stub
chose its port on 127.0.0.2 and then had the stub bind the SAME number on
127.0.0.1, which nothing checked was free there. When another process
held it, the stub exited on the bind before announcing, and the case
failed on the fixture ('stub exited without announcing a port', CI run
37890704284) without reaching the proof it is about. Reproduced: holding
127.0.0.1:P and starting the stub on P gives exactly that error.
foreignPortFreeOnLoopback now picks a port that also binds on 127.0.0.1.
A bind still lost in the moment between that check and the stub's own
bind, the only way left to see that error here, re-runs the setup on a
fresh port, at most three times. A refusal from the proof itself is
never retried.
|
||
|
|
0bdf539455 |
feat(release): the Claude Code plugin follows stable releases; the release workflow pins the marketplace (TASK-3487) (#1890)
* feat(release): the Claude Code plugin follows stable releases; the release workflow pins the marketplace (TASK-3487) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * fix(release): the plugin pin never moves backwards; force the tag fetch (TASK-3487 codex r1) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS |
||
|
|
585bd27991 |
test(buildtools): freePort picks outside the ephemeral range, so a parallel package's :0 bind cannot take it (BUG-3491) (#1884)
Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS |
||
|
|
9e6791d4f1 |
fix(scripts): install-refresh asks git in the caller's checkout, not the server's cwd (BUG-3403) (#1790)
* fix(scripts): install-refresh asks git in the caller's checkout, not the server's cwd (BUG-3403) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * fix(scripts): anchor the web/ pathspec at the repo root (codex r1 on BUG-3403) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS |
||
|
|
d4ec587de9 |
fix(cli): a CLI call during an install-refresh bounce waits for the refresh instead of auto-starting a server over it (BUG-3215) (#1544)
install-refresh.sh stops the server, installs, and restarts it with the
captured argv. Any pad CLI call in between found nothing answering and
auto-started a server of its own: the caller's binary, cwd and default
flags. That server held the port against the refresh's restart (day 79:
127.0.0.1:7777 went dark and the script exited 1).
- The script writes <DataDir>/refresh.lock {pid, port, deadline} by rename
before the stop and removes it in its EXIT trap, only while it is still
its own. The deadline is 10s + PAD_PROBE_TIMEOUT (30s by default).
- EnsureServer defers to a live marker for its own port (writer pid alive
AND deadline not passed): it polls health and never spawns. If the
writer is still alive past the deadline it errors naming the pid. A dead
writer, a passed deadline, another port or an unparseable file means
the marker is ignored.
- The script names a holder that took the port before the restart spawns
(and does not spawn it), and refuses success when the listener after
the probe is not the restart it spawned. A health probe answers for
whoever holds the address.
- The script-test dependency guard now reads every install_refresh*_test.go.
Claude-Session: https://claude.ai/code/session_01A3rXV7GDLAhBe9mL4V6z2m
|
||
|
|
b2bee3242f |
fix(scripts): install-refresh restores the server's own cwd and matches the running server by identity (BUG-3196) (#1512)
* fix(scripts): install-refresh restores the server's own cwd with its argv, refuses a deleted one, and matches the running server by identity rather than the built binary's name (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * fix(scripts): one existence check refuses a deleted server cwd, naming the path without the kernel's suffix (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * test(scripts): the no-/proc refresh test declares lsof, which now reads the server's cwd there (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * test(scripts): the lsof dependency sits on the requireScriptDeps line the guard reads (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * docs(make): the install target's guarantees include the restored cwd (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * fix(scripts): the server's cwd is re-checked immediately before the stop, and the capture's limits are stated (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * fix(scripts): a failed restart cd says what state it leaves, not that no server runs (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * fix(scripts): an unreadable server cwd is refused naming what was read and its error, and the case is tested (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N |
||
|
|
45dddb8f8f |
fix(scripts): install-refresh replaces the server listening on its port, by pid, and leaves other pad servers running (BUG-3194) (#1504)
* fix(scripts): install-refresh replaces the server listening on its port, by pid, and leaves other pad servers running (BUG-3194) The script counted every `pad server start` on the box and refused on more than one, so a sibling seat's e2e server (a worktree binary on its own port) made the scripted refresh unusable whenever anyone ran e2e. Its stop was a system-wide `pkill -x`, which would also have killed that server and any `pad` CLI call in flight. The target is now the `server start` process listening on the port the server itself would resolve without a flag (PAD_PORT, config, 7777), read with ss or lsof. It is stopped by pid, after its argv is re-read and matched. Still refused: the port held by something that is not a captured server, several servers on it, or several servers and none on it. One server with nothing on the port is still that server. Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * docs(make): the install caution names npm ci, the hazard that remains; the stop is no longer system-wide (BUG-3194) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * fix(scripts): refuse a foreign or unreadable listener on the port, re-check before each signal, keep empty trailing arguments (BUG-3194, codex round 1) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * fix(scripts): the one-server fallback needs an explicit --port, so a lone sibling server is never replaced (BUG-3194, codex round 2) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * docs(make): reflow the install comment (BUG-3194) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N |
||
|
|
c3d64451ed |
test(buildtools): the install-refresh pre-server proves it is the listener the script probes (TASK-3146) (#1443)
* test(buildtools): the install-refresh pre-server proves it is the listener the script probes (TASK-3146) Seven tests pre-start a "running server" on a port chosen by freePort, which releases the port before the stub binds it, and then waited only until SOMETHING answered on it. A stub that lost the port in that window exits on the bind error and the dial succeeds against the foreign listener, so the script's outcome was judged against someone else's process. The port cannot be 0 there, as it is in BUG-3144's two-server test, because the script restarts that server and reads the port from PAD_PORT or the captured argv. startPreServer therefore proves two things instead: the stub's own STUB_ANNOUNCE line, with the port it bound equal to the chosen one, and its pid at the address the script will probe (a new /stub/pid route), which covers a wildcard bind beside a held specific address on a stack that permits it. A collision is now a named fixture failure. Tests force each case: a foreign listener answering health ok on the chosen port, a probe address that reaches someone else, and a stub that bound a different port, plus a positive control. Claude-Session: https://claude.ai/code/session_01Vf8uZmYdsrQqJtbAmjUFSX * test(buildtools): hold the chosen port in the port-proof case so the stub's port 0 cannot land on it (TASK-3146, codex round 1) Claude-Session: https://claude.ai/code/session_01Vf8uZmYdsrQqJtbAmjUFSX |
||
|
|
94381d09a5 |
test(buildtools): start the two-server fixture on port 0 and wait on each stub's own announcement (BUG-3144) (#1434)
* test(buildtools): start the two-server fixture on port 0 and wait on each stub's own announcement (BUG-3144) TestInstallRefresh_RefusesWhenSeveralServersAreRunning failed once on CI's Go (PostgreSQL) job (run 35738655417): "script proceeded with two servers running". The script was right. The fixture had one server. The script counts server PROCESSES (pgrep -x plus the `server start` argv). The test picked two "free" ports with freePort, which releases the port before the stub binds it, and proved readiness by dialing each port. A stub whose port was taken in between (by the other stub, or by anything else on a busy runner) exits on the bind error, the dial succeeds against the other listener, and only ONE stub process exists. The script correctly proceeds, and the test blames it. The stub now binds via net.Listen and, with STUB_ANNOUNCE=1, prints the port it actually got. Otherwise it behaves as before (a failed bind exits). The test starts both stubs on port 0 and waits on each process's own "LISTENING <port>" line, so readiness is tied to that process and two stubs cannot share a port. A stub that exits is now a fixture failure naming itself, not an assertion about the script. Evidence, pre-committed on BUG-3144 before running: - unfixed, forced portB = portA: 3/3 fail with the CI message - unfixed, a foreign listener pre-bound on portB: 3/3 fail, same message - unfixed, unmutated, -count=30, all CPUs busy + a port-churning load: 2/30 fail (one exactly the CI message; one the same mechanism with a failed restart) - fixed, the second stub forced onto the first's port: 3/3 fail as "stub exited without announcing a port" (a fixture failure, not a wrong verdict about the script) - fixed, same contention + churn, -count=50: 50/50 pass Not changed: eight other tests pre-start a server on a chosen port the script must restart on, so they cannot use port 0. They share the pick-then-bind window. Claude-Session: https://claude.ai/code/session_01ByMjAhvdWgbAWWeGE9jwWd * test(buildtools): reap announced stubs in t.Cleanup and keep draining their stdout (BUG-3144) Codex round 1, both valid: startAnnouncedStub started each stub without ever calling Wait, and its reader stopped after the first line, so a stub writing more could block on a full pipe. Each stub is now killed and reaped in t.Cleanup, which runs even when an assertion fails. Wait closes the pipe, ending the reader. The reader drains stdout after the announcement. Hygiene, not measured: with the old code the defer's pkill plus the test binary's exit leave nothing observable afterwards, so no counterfactual discriminates this change. Stated rather than claimed. Claude-Session: https://claude.ai/code/session_01ByMjAhvdWgbAWWeGE9jwWd |
||
|
|
110045578c |
fix(build): make install proves what it installed and what it restarted (BUG-2897, TASK-2787) (#1272)
`make install` made three claims it did not check.
1. It brought the server back BY SIDE EFFECT -- `pad auth whoami` triggers
an auto-start, which does not know the killed process's argv. A server
running `--host 0.0.0.0` came back bound to the default host alone:
curl 127.0.0.1:7777 -> 000 while the LAN address -> 200, with the
process count and the version both reading correct (BUG-2897).
2. `cp` copied whatever was at the repo path, not what the invocation
built. Two sessions sharing the checkout interleave and the loser's
build is installed by the winner, every exit code green (TASK-2787).
3. "Server restarted." was printed after a command ending in `|| true`,
with no probe of any kind. Not "the wrong address went unverified" --
nothing was verified. Found reading the recipe; neither filing names it,
and it is what made the other two invisible.
The logic moves to scripts/install-refresh.sh for one reason above
readability: a script can be TESTED. internal/buildtools drives it against
a compiled stub `pad`, including the branches where a check must FAIL.
Recipe-inline logic is only exercisable by running `make install`, which
stops the developer's server -- a test nobody runs twice, which is how this
target accumulated three unverified claims.
The script checks OUTCOMES rather than steps: what got installed, and what
is answering afterwards. Two commit checks, deliberately, answering
different questions -- the ARTIFACT before the kill, so a wrong build costs
an error instead of an outage, and the DESTINATION after the copy, which is
the shared-path race TASK-2787 names. The restart uses the argv read from
/proc before the kill, and nothing is printed about a restart until the
server answers on BOTH 127.0.0.1 and the configured host (`--host 0.0.0.0`
resolving to loopback plus the primary LAN address, since 0.0.0.0 is a bind
spec, not something to curl).
Three defects were found in the fix itself, by its own tests and by the
first real run:
- The restart redirected to $HOME/.pad/server.log with nothing creating
that directory. On a fresh HOME the redirect fails, the server never
starts, and the probe reports "did not answer" -- true, and three steps
downstream. Invisible to every hand-run this script could have had,
because a developer's box has ~/.pad by luck of history.
- The post-copy check SURVIVED its mutant: both fixtures reported the same
version from source and destination, so the guard and its absence were
indistinguishable. Fixed by making the stub's version depend on its path.
- Comparing commits by equality rejected a healthy build. `git rev-parse
--short` returns the shortest UNAMBIGUOUS prefix, so its width grows with
the object database: the binary embedded `a3a1d58` and the Makefile
produced `a3a1d586` minutes later. Now a prefix comparison in either
direction, with a negative leg pinning that a genuinely different commit
of the same width is still refused.
Five mutants, each verified to compile, each detected by its own leg.
Verified end to end on the real box: captured `--host 0.0.0.0`, installed
|