fix(scripts): address Copilot review round 1 on #2147

- pty.mjs: narrow the BSD argv set to {darwin, freebsd}. Grouping every
  BSD together was wrong — NetBSD's `script` takes the command through
  `-c`, so the argv form fails at `script` startup, which this smoke would
  then report as a TUI crash. Rather than add an unverified `-c` branch
  for netbsd/openbsd, they fall through to `null` and the smoke skips with
  the reason: a guessed invocation turns a clean skip into a false failure
  while looking like support.

- pty.test.mjs: "supported unless Windows" would fail `test:scripts` on
  aix/sunos/netbsd/openbsd, where a null wrapper is correct and the smoke
  skips by design. State the policy once (SUPPORTED_PLATFORMS) and branch
  on it, keeping the real-probe teeth that catch a `script` missing from
  PATH. Pin netbsd/openbsd/aix/sunos to null in scriptFlavorFor's tests
  so "unsupported" reads as the default, not a win32 special case.

- render-smoke.mjs: the @returns contract claimed a close guarantee the
  code does not give — both drain give-up branches resolve after only a
  warning, so output can be truncated and the child may still hold the
  work dir. Document the bounded fallback and name the required cleanup
  shape (removeSafe, not rmSync), since a caller "tightening" cleanup on
  the old wording would reintroduce #1801 exactly where it doesn't hold.

- AGENTS.md, clients/launcher/README.md: drop the pre-#2111 claim that the
  smokes build test-servers "on demand if it is missing". ensureTestServers
  rebuilds unconditionally, once per process — presence is not freshness.

- .github/copilot-instructions.md: mirror the survival rule, per the
  same-PR mirroring requirement. One bullet, generalized past this fix:
  a smoke must assert the thing survives, not that it started, and the
  started-then-died branch belongs in a scripts/lib test against a stub.

Signed-off-by: Cliff Hall <cliff@futurescale.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-26 21:20:52 -04:00
commit bd7297da88

View file

@ -56,8 +56,16 @@ export function outputTail(output, limit = 800) {
* teardown handle on every failure path.
* @param {(message: string) => void} [opts.warn]
* @returns {Promise<{ code: 0 | 1, message: string, output: string }>}
* Resolves only after the child's streams have closed, so `output` is
* complete and the caller may safely remove a directory the child was using.
* On the normal path, resolves only after the child's streams have `close`d —
* so `output` is complete and the caller may remove a directory the child was
* using without re-entering the #1801 ENOTEMPTY race.
*
* That is **bounded, not guaranteed**. Both drain deadlines below resolve
* *without* having observed `close`, after warning: the child may still hold
* the dir, and `output` may be truncated mid-line. Cleanup at the call site
* must therefore be best-effort (`removeSafe`, not `rmSync`) — the worst case
* is a warning and a leaked temp dir, never a red smoke. Do not write a
* caller that depends on the close having happened.
*/
export function runRenderSmoke({
command,