Skip to content

test-stack-up: runtime command-line ownership proof, own runtime is not a port thief, tracked stale-process sweep (#160, #278) - #319

Closed
MendixMau wants to merge 3 commits into
masterfrom
fix/stack-up-ownership
Closed

MendixMau wants to merge 3 commits into
masterfrom
fix/stack-up-ownership

Conversation

@MendixMau

Copy link
Copy Markdown
Owner

#160: an app with no run --local handshake was always unverified

Problem. test-stack-up.sh had two ways to prove ownership: the .docker container label and, since #313, the run-local.json handshake. An app started by Studio Pro's Run Locally, or by a run --local that wrote no handshake, was found by scan and published as APP_OWNERSHIP=unverified. The harness then refused it unless APP_PORT was set by hand on every run, which is what happened in the field (requirements-driven build, 2026-09-25).

Fix. The scan now checks each Mendix-answering port's listener (lsof / ss / netstat). If the listener's command line runs runtimelauncher.jar on THIS project's deployment dir, the port is verified with APP_SOURCE=runtime. The deployment dirs checked are <root>/deployment, <root>/app/deployment, and the dir beside the real .mpr. A verified port is chosen ahead of an earlier unverified one. Freshness is reported as unknown because this script did not build that app. The command line is read from /proc/<pid>/cmdline on Linux (exact argv, resolved through symlinks and the process cwd), from ps -o args= on macOS, and from PowerShell CIM on Git Bash. When it can't be read, the port stays unverified, as before.

Helper dedup. port_holder / holder_text and the parent-pid tree walk move into project-bin/_common.sh as mxtk_port_holder, mxtk_holder_text and mxtk_pid_in_tree. mxtk_pid_args and mxtk_pid_runs_deployment are new there. The change is additive: nothing existing in _common.sh changed. Both scripts now source the moved helpers. If a project's bin/_common.sh is older than these scripts, the script exits 2 and names sync-project.sh, rather than dying mid-run on "command not found".

Refs #262 (follow-up to #313): the project's own runtime is not a port thief

Problem. An independent review of #313 found that check_run_local_ports never excluded this project's own process. A booting runtime binds its ports before it serves a page. In that window --check exited 4 ("INSTRUMENT BLOCKED") about its own java, where it used to say NOT READY (exit 1). --no-docker did the same while Studio Pro booted this project.

Fix. The holder of the port counts as ours if any of these is true:

  • it is the live pid in the handshake record, or a descendant of it;
  • its command line runs this project's deployment dir (which covers Studio Pro running this project).

In that case the script reports NOT READY and exits 1, naming the holder. A runtime whose command line names another deployment dir is always foreign. Any other holder still blocks with exit 4, which T7 and the new T11b cover. With a live record and a holder that can't be placed (unnamed, or Windows where Git Bash shows no parent pids), the holder is presumed ours.

#278 remainder: hung test shells, and what makes a run INVALID

Problem. Hung journey-runner and monitor shells from earlier retries ran for hours. They filled a trial licence's session cap, and a later run came out INVALID.

Fix.

  • test-stack-up.sh --track <pid> [label] records a process this project started in .claude/loop/bg-pids.tsv. Each line holds the pid, the epoch, the ps lstart start time and the label.
  • Every run lists tracked processes older than MXTK_STALE_HOURS (default 4) and prints --reap. --check is read-only and does not prune the record.
  • --reap stops the stale ones and their children, found by parent pid. It acts only on pids in the record and never matches by name or pattern. A pid whose start time no longer matches its record line has been reused by another process; it is dropped and never signalled.
  • Git Bash: --track and --reap say they are unsupported and do nothing.
  • skills/test-result-audit.md (an on-demand file, not baseline) gets one line naming the two causes of INVALID that sit outside the app: account lockout and the licence session cap.

Not wired yet: the producer. Nothing calls --track automatically. The obvious producer is project-tests/e2e/journey-runner.js, which another in-flight PR owns tonight. The proposed patch is in the hand-back: at start, the runner calls bin/test-stack-up.sh --track <process.pid> journey-runner, best-effort. Until that lands, an agent tracks its backgrounded runner with --track $!.

Checks

  • tests/wave2/test-stack-up-local.sh: 49/49 (5 consecutive clean runs). Against master's script, 16 of the new assertions fail (T8–T14).
    • The fixture drops pipefail, because printf "$OUT" | grep -q hit SIGPIPE intermittently on T2. The child-liveness check is zombie-aware.
  • tests/wave2/test-app-restart.sh: 48/48. This includes the new old-_common.sh guard case, which fails on master.
  • tests/wave2/test-common-windows.sh project-bin/_common.sh: 10/10.
  • Also clean: bin/check-scripts.sh, bin/check-portability.sh, bin/check-no-private-citations.sh, LEAKGUARD_ALLOW_NO_DENYLIST=1 bin/check-no-client-data.sh, bin/check-pr-discipline.sh origin/master. The added-lines denylist grep found nothing.
  • Routing was not touched.

Evidence and field-run status

Risks

  • Only the evidence changes for existing projects. A port gets verified only with the new command-line proof, and a holder is "ours" only when proven or presumed as above.
  • bg_report prints nothing unless a project has used --track.
  • APP_SOURCE=runtime is a new value. Consumers (config.js, page-audit.js) read only APP_OWNERSHIP.

Refs #160 (code complete, field run owed)
Refs #278 (producer wiring in journey-runner.js owed)
Refs #262 (follow-up to #313)

🤖 Generated with Claude Code

https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw


Generated by Claude Code

…ot a port thief, tracked stale-process sweep (#160, #278, refs #262)

- #160: a scanned Mendix port whose listener runs runtimelauncher.jar on this
  project's deployment dir (<root>/deployment or <root>/app/deployment) is
  APP_OWNERSHIP=verified, APP_SOURCE=runtime; freshness stays unknown. Linux
  /proc, macOS ps, Git Bash PowerShell; unreadable stays unverified.
- port_holder / holder_text / in-tree walk moved into _common.sh (additive) and
  sourced by both test-stack-up.sh and app-restart.sh; an older _common.sh stops
  with exit 2 and the sync remedy.
- #262 follow-up to #313: a port held by this project's own process (live
  recorded pid or descendant, or a runtime on this deployment dir, incl. Studio
  Pro running this project) is NOT READY, exit 1, not INSTRUMENT BLOCKED.
- #278: --track <pid> [label] records a background test process; every run
  lists tracked ones older than MXTK_STALE_HOURS; --reap stops them (pid record
  only, start-time checked). test-result-audit.md names the two outside causes
  of INVALID.

Fixtures: test-stack-up-local.sh T8-T14 (16 assertions fail on master),
test-app-restart.sh old-_common.sh guard case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
…unded presumption, sync remedy, stable lstart

1. DEPLOY_DIRS is an array; the joined-line branch (macOS ps / Windows CIM)
   matches a deployment dir whole (end, "/", or " -flag"), so a spaced path's
   fragment can no longer verify a foreign checkout.
2. An unnamed / Windows holder is presumed ours only while the run-local
   record is younger than BOOT_TIMEOUT; after that it blocks (exit 4).
3. The old-_common.sh guard names `sync-project.sh <root> --upgrade-bin _common.sh`.
4. pid_lstart runs under LC_ALL=C TZ=UTC0; --reap re-checks the start time
   right before each kill; --track refuses pid 0/1, itself and its caller.
5. Fixture comment corrected.

Fixtures: test-stack-up-local.sh T15 (spaced paths, forced joined-line
branch), T16 (presumption bound), T14 additions (pid 1, calling shell,
cross-TZ track) — 7 assertions fail on the previous head.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
…pid-1 refusal); --reap never signals pid <= 1

Confirmation review of PR #319: `--track 01` passed the string compare against
0/1/self/caller, and a later --reap would TERM it. T_PID is normalised with
$((10#...)) right after the digit check; bg_scan drops any pid <= 1 line and
--reap refuses one again before signalling (hand-edited records).
Fixture: --track 01 -> exit 2; an aged pid-1 line with init's real start
time is never signalled and is pruned (both fail on the previous head).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
MendixMau pushed a commit that referenced this pull request Oct 13, 2026
…hared port/process helpers; --track/--reap (closes #160; refs #278 #262)

Review: MERGE (review + fix round + 2 confirmations) — spaced paths can't verify a foreign checkout; presume-ours bounded by BOOT_TIMEOUT; pid 0/1/self refused incl. leading zeros.
MendixMau pushed a commit that referenced this pull request Oct 13, 2026
…MxTest after-startup; mxcli-run pid check (closes #244)

Conflict: tests/wave2/test-app-restart.sh header comment — kept both
blocks (#319's first). project-bin/app-restart.sh auto-merged: #319's
one-line in_our_tree via mxtk_pid_in_tree kept, #322's mxtest guard,
pid_is_mxcli_run, step 2a and exit 5 present.

Review: independent joint review (agent a1e0b45) of head 1cb70ad — MERGE.
Follow-ups: move duplicated mxtest_* helpers and pid_is_mxcli_run into
_common.sh; document exit 4/5 in dispatching.md; one ls|grep -q under
pipefail in test-test-scratch.sh. Fixtures: app-restart 64/0,
test-scratch 46/0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
MendixMau added a commit that referenced this pull request Oct 14, 2026
)

Every constituent PR was reviewed by a fresh agent that did not write it (CLAUDE.md "Merging a PR"); only MERGE verdicts joined. CI green on each constituent head and on this head (40814fc); full suites run on the branch.

Review lines (per merged PR):
- #315 context trim — MERGE; 360/385 removed lines found verbatim in their new homes, rest read by hand; baseline 79,997 -> 76,259.
- #316 independent-review + word-budget rules — MERGE-AFTER-FIX (3 rounds), fixes applied as specified.
- #318 page-fidelity displayed text — MERGE after fix round + confirmation.
- #317 interaction-model skill (#291 part 1) — MERGE after fix round + confirmation; no client names leaked.
- #321 PROOF-OF-LOOK per section; final-look not due before derived Stage 6 — MERGE after fix round + confirmation.
- #307 graph-sweep guard; per-BRD conformance ledgers — MERGE after two fix rounds + confirmation.
- #325 extraction-quality section; token-burn fixes — MERGE after fix round + confirmation.
- #310 e2e OQL by alias, negative-span liveness, APP-DOWN, #302 credential regression fix — MERGE after fix round + 3 confirmations.
- #323 inbox promotions — MERGE after fix round + confirmation.
- #308 lint gate BLIND on reverted stock rules — MERGE (FIRST-SEEN reverted) after two fix rounds + confirmation.
- #328 pipefail + grep -q flake — MERGE; 0/30 flakes vs 15/100 on master. Follow-up: run-all resolves bin/lib/ subjects (reviewed, MERGE).
- #319 test-stack-up command-line ownership proof — MERGE after fix round + 2 confirmations.
- #327 change-intake core + on-demand detail; CRLF-safe change log — MERGE after fix round + confirmation.
- #320 follow-ups to #290/#292; Dispatching split out — MERGE after fix round + confirmation.
- #326 status delivery rounds (existing-app mode) — MERGE after fix round + confirmation.
- #312 lint batch 4: SEC014, toolkit CONV009 — MERGE after review + 2 fix rounds + confirmation; field run on mxcli 0.26.
- #324 exec.sh change classes, shared stale-lock takeover, SEC014 note echo — MERGE (joint review); follow-up owed for a takeover race after a mid-takeover crash.
- #322 MxTest after-startup guard, mxcli-run pid check — MERGE (joint review).
- #314 doctor property-set exit code; sync announces its bound — MERGE (joint review).
- #330 review follow-ups (exit 4/5 docs, pipefail, NUL strip) — MERGE-AFTER-FIX, fixes applied; takeover re-check dropped.

Field runs are still owed for most instruments in this batch (stated in each PR).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
@MendixMau

Copy link
Copy Markdown
Owner Author

Landed on master as part of the integration squash #329 (commit 31e90ae), after independent review — its Review: line is in that commit message. Closing as superseded.


Generated by Claude Code

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