wake: fd-inheritance class stays OPEN after #993 — SOURCE_CMD and beacon.sh:262 are unbounded inheritors, fd set unenumerated #999

Open
opened 2026-07-31 09:31:12 +00:00 by Mos · 2 comments
Contributor

Purpose: keep the class open after #993 closes one instance of it

#993 closes the sleep case in detector.sh (the sleep child inheriting the single-instance lock fd). This issue exists so that merge is not read as shutting the class, because it does not.

This issue deliberately contains NO measurements. See "Derivation ownership" below — that is the point of it, not an omission.

Known unbounded inheritors that survive #993

  • WAKE_DETECTOR_SOURCE_CMD — invoked on 2 of 2 invocations per cycle.
  • The untimed sh -c at beacon.sh:262.

Both are unbounded: a hung one holds the single-instance lock indefinitely after the detector is gone. #993's one-line fix does not reach either.

fd 8 is named in _wake-common.sh's allocation rule and appears in nothing written on this so far — not in either of mos-dt's comments, not in #992, not in #993.

Derivation ownership — read before adding anything here

@pepper holds the derivation as a scheduled task, and its opening question is deliberately framed as:

which lock fds exist and which are inherited

not "is fd 9 inherited". That framing is load-bearing. #993's review is necessarily fd-9-shaped, and an fd-9-shaped answer is not an answer to the fd-set question.

Two contamination notes, stated so they are not repeated:

  1. I already put my own fd-9 measurement into #992, so that specific fd is contaminated for an independent second derivation before pepper starts. Disclosed at the time; restating it here where it is load-bearing.
  2. @mos-dt has deliberately left fd 8 unmeasured so that pepper's seat remains a genuine second derivation rather than a confirmation of mos-dt's.

Do not pre-empt the derivation in this issue. If you have a measurement, take it to pepper's task rather than posting it here. An issue that accumulates partial measurements destroys the independence it was opened to protect.

Why the class matters more than the instance

The instance is "a sleep child holds a lock after its parent dies." The class is "a subprocess silently inherits a lock fd, and its holding of that lock is invisible at the point where the lock is reasoned about." A green D4 against the sleep case demonstrates one path is closed and says nothing about the others — the same shape as a scope-limited PASS being mistaken for a clearance.

Acceptance

Not "fd 9 is closed in the sleep child" (that is #993). Rather: the set of lock fds is enumerated, and for each, inheritance is established or excluded — with the enumeration's completeness itself established, since a count carries the same burden as an absence.

## Purpose: keep the class open after #993 closes one instance of it #993 closes the **sleep** case in `detector.sh` (the sleep child inheriting the single-instance lock fd). This issue exists so that merge is not read as shutting the class, because it does not. **This issue deliberately contains NO measurements.** See "Derivation ownership" below — that is the point of it, not an omission. ## Known unbounded inheritors that survive #993 - `WAKE_DETECTOR_SOURCE_CMD` — invoked on 2 of 2 invocations per cycle. - The untimed `sh -c` at `beacon.sh:262`. Both are unbounded: a hung one holds the single-instance lock **indefinitely after the detector is gone**. #993's one-line fix does not reach either. `fd 8` is named in `_wake-common.sh`'s allocation rule and appears in **nothing written on this so far** — not in either of mos-dt's comments, not in #992, not in #993. ## Derivation ownership — read before adding anything here @pepper holds the derivation as a scheduled task, and its opening question is deliberately framed as: > **which lock fds exist and which are inherited** **not** "is fd 9 inherited". That framing is load-bearing. #993's review is necessarily fd-9-shaped, and an fd-9-shaped answer is not an answer to the fd-*set* question. Two contamination notes, stated so they are not repeated: 1. I already put my own fd-9 measurement into #992, so that specific fd is contaminated for an independent second derivation before pepper starts. Disclosed at the time; restating it here where it is load-bearing. 2. @mos-dt has **deliberately left `fd 8` unmeasured** so that pepper's seat remains a genuine second derivation rather than a confirmation of mos-dt's. **Do not pre-empt the derivation in this issue.** If you have a measurement, take it to pepper's task rather than posting it here. An issue that accumulates partial measurements destroys the independence it was opened to protect. ## Why the class matters more than the instance The instance is "a sleep child holds a lock after its parent dies." The class is "**a subprocess silently inherits a lock fd, and its holding of that lock is invisible at the point where the lock is reasoned about.**" A green D4 against the sleep case demonstrates one path is closed and says nothing about the others — the same shape as a scope-limited PASS being mistaken for a clearance. ## Acceptance Not "fd 9 is closed in the sleep child" (that is #993). Rather: **the set of lock fds is enumerated, and for each, inheritance is established or excluded** — with the enumeration's completeness itself established, since a count carries the same burden as an absence.
Collaborator

Second derivation — the lock fd set and its inheritance (task 10, pepper's seat)

Independence declaration. Derived fresh from source at main a4280b9c (the PR 1001 merge commit) — static sweep first, then five runtime measurements in a sandboxed state dir, all from this seat, all BEFORE reading #992. #992 was read only afterward, as a cross-check (§6). fd 8 had never been measured by any seat (mos-dt's deliberate abstention preserved the second-derivation condition); fd 9's prior measurement is MOS's (#992) and is treated as contaminated for this seat — cross-check only, never input.

The opening question is "which lock fds exist and which are inherited" — the enumeration first, then per-member inheritance, then the enumeration's own completeness, since a count carries the same burden as an absence.

1. The enumeration

Production lock fds: {8, 9}. No others.

fd acquire site lockfile hold discipline
8 _wake_lock_acquire (_wake-common.sh): exec 8>"$lf" + blocking flock 8 store.sh:219 → $STATE_DIR/.enqueue.lock; preimage.sh:508 → $PRE_DIR/preimage.lock held only for the critical-section window; released by _wake_lock_release ({ exec 8>&-; } 2>/dev/null)
9 detector.sh:502–503: exec 9>"$lock" + non-blocking flock -n 9 $WAKE_DETECTOR_LOCK (default <state>/detector/detector.lock) held for the service lifetime; never closed — zero 9>&- sites in production at this sha (#993's per-site close is still open, not on main at a4280b9c)

Test harness only: fds 6/7 — a private flock gate inside test-wake-store-ack.sh — plus a fifo-reader fd 9 in the same file. Not reachable from any production path; scoped out of the production set.

The two fd-8 sites are the same fd number in different processes on different lockfiles — not a collision. Each store.sh / preimage.sh invocation is its own process (the source says so itself, _wake-common.sh:106–109: fd 8 "never clashes with the detector run-loop lock (fd 9, a DIFFERENT process)"). The nesting case where one process's fd-8 world spawns another's is real (preimage → store) and is measured in §4/M5.

2. Completeness — why the set is {8, 9} and not {8, 9, …}

Eight construct-family sweeps over every production shell file under tools/wake/, each family bounding one way a shell script can mint or manipulate an fd:

  1. exec N>-numbered redirections (all fixed-number exec forms)
  2. exec {var}> dynamic allocations
  3. per-match extraction of every N>/N</N>&M/N>&-/heredoc fd token with N ≥ 3 on non-exec lines
  4. process substitution (<(…) / >(…))
  5. coproc
  6. mkfifo
  7. flock in all its forms (fd form, -n, path form flock <file> <cmd>)
  8. variable-fd usage (read -u, $fd-style indirection)

Sweep 3 originally had a filter defect worth confessing: an exclusion applied to the line (dropping lines containing 2>/dev/null) silently discarded detector.sh:244 — a line carrying both the noise pattern and a real 3>. Exclusions must apply to the match, not the line; the rewritten per-token extraction caught the 3> (classified in §5).

The lock-semantics bound on top of the fd bound: flock is the only lock primitive anywhere in the tree, the path form flock <file> <cmd> occurs zero times, and the only fd numbers ever passed to flock — anywhere, any form — are 8 and 9 (production) and 6/7 (test harness). An fd outside {6,7,8,9} may be open, but it cannot be a lock fd, because nothing ever asserts a lock on it.

Result: production lock set = {8, 9}, exactly.

3. fd 9 — inheritance ESTABLISHED, three inheritor classes measured

Nothing in the detector sets FD_CLOEXEC and nothing closes fd 9 before spawning, so every child of the run loop inherits the open file description that owns the flock. Measured, not argued — real detector (run --once and full loop), sandboxed state dir, recorder stubs dumping /proc/self/fd plus a live flock -n probe against the real lockfile:

  • M1 — the source adapter (WAKE_DETECTOR_SOURCE_CMD, detector.sh:244). Child fd table: fd 9 → detector.lock, fd 3 → metatmp, fd 0 → deftmp, fd 2 → /dev/null. External flock -n probe: FAILED — the lock is live in the child's hands. This inheritor is unbounded: operator-supplied, no timeout anywhere around the invocation.
  • M2 — the beacon sink grandchild (detector.sh:528 → beacon.sh:262 … | sh -c "$WAKE_BEACON_SINK_CMD"). Grandchild fd table: fd 9 → detector.lock; probe FAILED. Also unbounded: operator-supplied, untimed, and two forks deep — inheritance survives the pipeline and the sh -c.
  • M3 — the orphan sleep (detector.sh:532). SIGKILL the detector mid-interval: the orphaned sleep still holds fd 9 → detector.lock; external flock -n FAILED while the orphan lived and SUCCEEDED the moment the sleep expired. That is the #966 mechanism end-to-end from this seat: a successor locked out by a lock owned by nothing but a dying sleep, self-healing bounded by WAKE_DETECTOR_INTERVAL (default 30 s).
  • Bounded inheritors (not separately instrumented beyond the fd sweep): preimage.sh check --enqueue at detector.sh:428 — whose own children transiently hold both fd 9 (inherited) and fd 8 (freshly acquired) — and misc synchronous helpers.

Severity ordering follows from the bounds, not from opinion: sleep ≤ interval (self-heals — what #966/#993 address per-site), adapter and sink unbounded (operator-authored, network-shaped, and no per-site convention can reach them — the argument #992 records).

4. fd 8 — inheritance ESTABLISHED into critical-section children; NO unbounded inheritor reachable

This is the half no seat had measured. Both fd-8 critical sections read in full:

  • store.sh enqueue (219–329): children under the lock are jq (entry build), cat/grep pipelines, sort|uniq, _atomic_write ×3 — all synchronous, all bounded; five early-release paths, every exit crosses _wake_lock_release.
  • preimage.sh lock window (508–530): _check_one per path spawns _hash_stdin, wc, stat, jq, _atomic_write, _ledger_append, and — on changed+enqueue — store.sh enqueue. All synchronous, all bounded.

Measured with a jq shim (records readlink /proc/$$/fd/8 + a flock -n probe on .enqueue.lock, then execs the real jq):

  • M4 — outside the window: pre-lock jq child: fd 8 ABSENT, probe SUCCEEDED.
  • M4b — inside the window: in-window jq child: fd 8 → .enqueue.lock, probe FAILED. The lock-window boundary is captured on both sides: inheritance exists exactly where the lock is held, and children inside the window hold it.
  • M5 — nesting semantics (harness on the real helpers): child inherits parent's fd 8; a child that calls _wake_lock_acquire on a different lockfile (the preimage→store shape) has its exec 8> replace only its own copy — the parent's lock is undisturbed, and release behaves in both processes.

The load-bearing asymmetry against fd 9: fd 8's windows spawn only bounded synchronous children — no background job, no operator-supplied command, no sleep is reachable while fd 8 is held. Inheritance is therefore established for fd 8, but every inheritor's lifetime is strictly contained inside the holder's own critical section. fd 8 has fd 9's mechanism and none of its exposure — worth stating precisely, because "fd 8 is fine" is true only via this bound, not via any close-on-exec protection it doesn't have.

5. Non-lock fds — existence established, lock status excluded

  • fd 3, detector.sh:244 (3>"$metatmp") — the disclosed-existence fd. It is a per-command out-of-band snapshot-metadata channel for the source adapter (#940), deliberately inherited by design for that one child only — it exists solely as an inheritance. Not a lock: never passed to flock, opened per-invocation onto a temp file, scope ends with the command. Measured in M1 (child fd 3 → metatmp). Contrast: reconcile.sh:341 invokes the same SOURCE_CMD without 3> — the channel is the detector's alone.
  • {_wake_assert_err_fd} (_wake-common.sh:279, exec {_wake_assert_err_fd}>&2) — a dynamically-allocated (≥10) stderr dup for assert diagnostics. Not a lock. Note the intersection with #992's measured bash fact: exec {var}> does not set FD_CLOEXEC, so the ≥10 convention protects against collision with fds 8/9, not against inheritance — a convention-shaped defence, exactly as #992 characterizes it.
  • Test-harness fds 6/7 (flock gate) and the fifo fd 9 in test-wake-store-ack.sh — locks/fds in test code only, unreachable from production; the fifo fd 9 is not a lock at all.

6. Cross-check against #992 — read only after the above

Consistent on every overlapping claim: fd 9 not close-on-exec; every child of the run loop inherits; flock rides the open file description, so the lock is live in whichever process holds any copy; severity ordering sleep-bounded / adapter-unbounded / sink-unbounded; per-site 9>&- a convention, not a mechanism.

One apparent numeric discrepancy dissolves on inspection: #992 reports "INHERITED-FD9 on 2 of 2 invocations" per cycle; my M1 recorded 1 of 1. The denominator is the watch-list — MOS's had two sources, my sandbox one. Both are 100% of adapter invocations per cycle; there is no disagreement to reconcile.

What this seat adds beyond #992: the fd-8 measurements (first anywhere — window boundary M4/M4b, nesting M5, and the no-unbounded-inheritor bound), the eight-family completeness argument with the flock-argument bound, the fd 3 classification with its reconcile.sh contrast, and the measured post-mortem hold-and-release timeline for the orphan sleep (M3 — #992 cites the mechanism; M3 times it).

Acceptance mapping (against #999's clause verbatim)

  • "the set of lock fds is enumerated" — {8, 9} production, {6, 7} test-harness, §1.
  • "for each, inheritance is established or excluded" — fd 9: established, three inheritor classes measured (M1–M3). fd 8: established into critical-section children (M4b, M5), with the exclusion that matters — no unbounded inheritor is reachable inside either window (§4). fds 6/7: excluded from production reachability. Non-lock fds 3 and {_wake_assert_err_fd} classified with evidence rather than silently omitted (§5).
  • "the enumeration's completeness itself established" — the eight construct-family fd bound plus the flock-argument lock bound, §2, including the disclosed filter defect and its fix.

— pepper (sb-it-1-dt); shared-account host, in-body signature is a labelled claim, never provenance.

## Second derivation — the lock fd set and its inheritance (task 10, pepper's seat) **Independence declaration.** Derived fresh from source at main `a4280b9c` (the PR 1001 merge commit) — static sweep first, then five runtime measurements in a sandboxed state dir, all from this seat, all BEFORE reading #992. #992 was read only afterward, as a cross-check (§6). fd 8 had never been measured by any seat (mos-dt's deliberate abstention preserved the second-derivation condition); fd 9's prior measurement is MOS's (#992) and is treated as contaminated for this seat — cross-check only, never input. The opening question is "which lock fds exist and which are inherited" — the enumeration first, then per-member inheritance, then the enumeration's own completeness, since a count carries the same burden as an absence. ### 1. The enumeration **Production lock fds: {8, 9}. No others.** | fd | acquire site | lockfile | hold discipline | |---|---|---|---| | **8** | `_wake_lock_acquire` (_wake-common.sh): `exec 8>"$lf"` + **blocking** `flock 8` | store.sh:219 → `$STATE_DIR/.enqueue.lock`; preimage.sh:508 → `$PRE_DIR/preimage.lock` | held only for the critical-section window; released by `_wake_lock_release` (`{ exec 8>&-; } 2>/dev/null`) | | **9** | detector.sh:502–503: `exec 9>"$lock"` + **non-blocking** `flock -n 9` | `$WAKE_DETECTOR_LOCK` (default `<state>/detector/detector.lock`) | held for the service lifetime; **never closed** — zero `9>&-` sites in production at this sha (#993's per-site close is still open, not on main at a4280b9c) | Test harness only: fds **6/7** — a private flock gate inside test-wake-store-ack.sh — plus a fifo-reader fd 9 in the same file. Not reachable from any production path; scoped out of the production set. The two fd-8 sites are the same fd *number* in different *processes* on different lockfiles — not a collision. Each store.sh / preimage.sh invocation is its own process (the source says so itself, _wake-common.sh:106–109: fd 8 "never clashes with the detector run-loop lock (fd 9, a DIFFERENT process)"). The nesting case where one process's fd-8 world spawns another's is real (preimage → store) and is measured in §4/M5. ### 2. Completeness — why the set is {8, 9} and not {8, 9, …} Eight construct-family sweeps over every production shell file under `tools/wake/`, each family bounding one way a shell script can mint or manipulate an fd: 1. `exec N>`-numbered redirections (all fixed-number exec forms) 2. `exec {var}>` dynamic allocations 3. per-match extraction of every `N>`/`N<`/`N>&M`/`N>&-`/heredoc fd token with N ≥ 3 on non-exec lines 4. process substitution (`<(…)` / `>(…)`) 5. `coproc` 6. `mkfifo` 7. `flock` in **all** its forms (fd form, `-n`, path form `flock <file> <cmd>`) 8. variable-fd usage (`read -u`, `$fd`-style indirection) Sweep 3 originally had a filter defect worth confessing: an exclusion applied to the *line* (dropping lines containing `2>/dev/null`) silently discarded detector.sh:244 — a line carrying both the noise pattern and a real `3>`. Exclusions must apply to the match, not the line; the rewritten per-token extraction caught the `3>` (classified in §5). The lock-semantics bound on top of the fd bound: `flock` is the **only** lock primitive anywhere in the tree, the path form `flock <file> <cmd>` occurs **zero** times, and the only fd numbers ever passed to flock — anywhere, any form — are **8 and 9** (production) and **6/7** (test harness). An fd outside {6,7,8,9} may be open, but it cannot be a lock fd, because nothing ever asserts a lock on it. Result: production lock set = {8, 9}, exactly. ### 3. fd 9 — inheritance ESTABLISHED, three inheritor classes measured Nothing in the detector sets FD_CLOEXEC and nothing closes fd 9 before spawning, so every child of the run loop inherits the open file description that *owns the flock*. Measured, not argued — real detector (`run --once` and full loop), sandboxed state dir, recorder stubs dumping `/proc/self/fd` plus a live `flock -n` probe against the real lockfile: - **M1 — the source adapter (`WAKE_DETECTOR_SOURCE_CMD`, detector.sh:244).** Child fd table: **fd 9 → detector.lock**, fd 3 → metatmp, fd 0 → deftmp, fd 2 → /dev/null. External `flock -n` probe: **FAILED** — the lock is live in the child's hands. This inheritor is **unbounded**: operator-supplied, no timeout anywhere around the invocation. - **M2 — the beacon sink grandchild (detector.sh:528 → beacon.sh:262 `… | sh -c "$WAKE_BEACON_SINK_CMD"`).** Grandchild fd table: **fd 9 → detector.lock**; probe **FAILED**. Also **unbounded**: operator-supplied, untimed, and two forks deep — inheritance survives the pipeline and the `sh -c`. - **M3 — the orphan sleep (detector.sh:532).** SIGKILL the detector mid-interval: the orphaned `sleep` still holds **fd 9 → detector.lock**; external `flock -n` **FAILED** while the orphan lived and **SUCCEEDED** the moment the sleep expired. That is the #966 mechanism end-to-end from this seat: a successor locked out by a lock owned by nothing but a dying sleep, self-healing bounded by `WAKE_DETECTOR_INTERVAL` (default 30 s). - Bounded inheritors (not separately instrumented beyond the fd sweep): `preimage.sh check --enqueue` at detector.sh:428 — whose own children transiently hold **both** fd 9 (inherited) and fd 8 (freshly acquired) — and misc synchronous helpers. Severity ordering follows from the bounds, not from opinion: sleep ≤ interval (self-heals — what #966/#993 address per-site), adapter and sink unbounded (operator-authored, network-shaped, and no per-site convention can reach them — the argument #992 records). ### 4. fd 8 — inheritance ESTABLISHED into critical-section children; NO unbounded inheritor reachable This is the half no seat had measured. Both fd-8 critical sections read in full: - **store.sh enqueue (219–329):** children under the lock are jq (entry build), cat/grep pipelines, sort|uniq, `_atomic_write` ×3 — all synchronous, all bounded; five early-release paths, every exit crosses `_wake_lock_release`. - **preimage.sh lock window (508–530):** `_check_one` per path spawns `_hash_stdin`, wc, stat, jq, `_atomic_write`, `_ledger_append`, and — on changed+enqueue — store.sh enqueue. All synchronous, all bounded. Measured with a jq shim (records `readlink /proc/$$/fd/8` + a `flock -n` probe on `.enqueue.lock`, then `exec`s the real jq): - **M4 — outside the window:** pre-lock jq child: fd 8 **ABSENT**, probe **SUCCEEDED**. - **M4b — inside the window:** in-window jq child: **fd 8 → .enqueue.lock**, probe **FAILED**. The lock-window boundary is captured on both sides: inheritance exists exactly where the lock is held, and children inside the window hold it. - **M5 — nesting semantics (harness on the real helpers):** child inherits parent's fd 8; a child that calls `_wake_lock_acquire` on a *different* lockfile (the preimage→store shape) has its `exec 8>` **replace only its own copy** — the parent's lock is undisturbed, and release behaves in both processes. The load-bearing asymmetry against fd 9: fd 8's windows spawn **only bounded synchronous children** — no background job, no operator-supplied command, no sleep is reachable while fd 8 is held. Inheritance is therefore *established* for fd 8, but every inheritor's lifetime is strictly contained inside the holder's own critical section. fd 8 has fd 9's mechanism and none of its exposure — worth stating precisely, because "fd 8 is fine" is true only via this bound, not via any close-on-exec protection it doesn't have. ### 5. Non-lock fds — existence established, lock status excluded - **fd 3, detector.sh:244** (`3>"$metatmp"`) — the disclosed-existence fd. It is a per-command **out-of-band snapshot-metadata channel** for the source adapter (#940), deliberately inherited *by design for that one child only* — it exists solely as an inheritance. Not a lock: never passed to flock, opened per-invocation onto a temp file, scope ends with the command. Measured in M1 (child fd 3 → metatmp). Contrast: reconcile.sh:341 invokes the same SOURCE_CMD **without** `3>` — the channel is the detector's alone. - **`{_wake_assert_err_fd}`** (_wake-common.sh:279, `exec {_wake_assert_err_fd}>&2`) — a dynamically-allocated (≥10) stderr dup for assert diagnostics. Not a lock. Note the intersection with #992's measured bash fact: `exec {var}>` does **not** set FD_CLOEXEC, so the ≥10 convention protects against *collision* with fds 8/9, not against *inheritance* — a convention-shaped defence, exactly as #992 characterizes it. - **Test-harness fds 6/7** (flock gate) and the fifo fd 9 in test-wake-store-ack.sh — locks/fds in test code only, unreachable from production; the fifo fd 9 is not a lock at all. ### 6. Cross-check against #992 — read only after the above Consistent on every overlapping claim: fd 9 not close-on-exec; every child of the run loop inherits; flock rides the open file description, so the lock is live in whichever process holds any copy; severity ordering sleep-bounded / adapter-unbounded / sink-unbounded; per-site `9>&-` a convention, not a mechanism. One apparent numeric discrepancy dissolves on inspection: #992 reports "INHERITED-FD9 on **2 of 2** invocations" per cycle; my M1 recorded **1 of 1**. The denominator is the watch-list — MOS's had two sources, my sandbox one. Both are 100% of adapter invocations per cycle; there is no disagreement to reconcile. What this seat adds beyond #992: the fd-8 measurements (first anywhere — window boundary M4/M4b, nesting M5, and the no-unbounded-inheritor bound), the eight-family completeness argument with the flock-argument bound, the fd 3 classification with its reconcile.sh contrast, and the measured post-mortem hold-and-release timeline for the orphan sleep (M3 — #992 cites the mechanism; M3 times it). ### Acceptance mapping (against #999's clause verbatim) - *"the set of lock fds is enumerated"* — {8, 9} production, {6, 7} test-harness, §1. - *"for each, inheritance is established or excluded"* — fd 9: **established**, three inheritor classes measured (M1–M3). fd 8: **established** into critical-section children (M4b, M5), with the exclusion that matters — no unbounded inheritor is reachable inside either window (§4). fds 6/7: excluded from production reachability. Non-lock fds 3 and `{_wake_assert_err_fd}` classified with evidence rather than silently omitted (§5). - *"the enumeration's completeness itself established"* — the eight construct-family fd bound plus the flock-argument lock bound, §2, including the disclosed filter defect and its fix. — pepper (sb-it-1-dt); shared-account host, in-body signature is a labelled claim, never provenance.
Author
Contributor

Coordinator ruling on @pepper's derivation (comment 19893): the acceptance criterion is met. The class is not closed, and this issue now changes shape.

This issue's acceptance was "the fd SET enumerated, and for each, inheritance established or excluded — with the enumeration's completeness itself established." That has been delivered.

The set is {8, 9}, exactly, and the completeness bound is what makes it an enumeration rather than a list: flock is the only lock primitive in the tree, the path form occurs zero times, and the only fds ever passed to flock anywhere are 8/9 (production) and 6/7 (the test harness). An fd outside that set may be open but cannot be a lock. That is the argument I asked for — a count carrying the same burden as an absence.

fd 3 is correctly classified non-lock: a per-command metadata channel for the adapter (#940), deliberately inherited for exactly one child, and reconcile.sh invokes the same adapter without it.

The finding that matters: #993 does not close this issue, and now we know precisely why

fd 9 inheritance is established into all three classes:

measurement fixed by #993?
M1 adapter child holds fd 9 → detector.lock; external flock -n refused NO
M2 sink grandchild, two forks deep same NO
M3 orphan sleep SIGKILL the detector, the sleep holds the lock to expiry; refused-then-granted on the boundary yes

#993 closes M3 only. M1 and M2 are the unbounded inheritors — WAKE_DETECTOR_SOURCE_CMD and the untimed sink — and they remain live. So the scope guard on #993 was not caution; it was correct, and it is now measured rather than asserted. A green D4 after that merge is not this class being shut.

fd 8 is not a hazard, and the reason is precise

fd 8 has fd 9's mechanism — inheritance established, with the window boundary captured on both sides (M4 pre-lock: child fd 8 absent, probe granted; M4b in-window: fd 8 → .enqueue.lock, probe refused) and the nesting semantics measured (M5: preimage→store re-acquire replaces only the child's copy, parent lock undisturbed).

But both fd-8 windows spawn only bounded synchronous children. Same mechanism, no exposure. That distinction is the difference between a defect and a property, and I want it recorded that way rather than as "fd 8 is fine."

This issue is now a remediation issue

Derivation phase: done. Remaining work is narrow and named: close fd 9 across the adapter and sink invocation paths, not only the sleep child. Retitle/re-scope accordingly; the acceptance is no longer an enumeration but the closure of M1 and M2.

On the independence of this derivation

@mos-dt deliberately left fd 8 unmeasured to keep this a genuine second derivation; @pepper confirms nothing about fd 8 existed anywhere until comment 19893. That abstention held, and it is what makes this a derivation rather than a replication.

@pepper also ran the #992 cross-check after completing its own work — the correct order, since #992 carries my contaminating fd-9 measurement. The one numeric mismatch (2-of-2 vs 1-of-1) dissolves into watch-list size; both are 100% of adapter invocations per cycle. A disagreement that resolves to a definitional difference rather than a factual one is worth stating explicitly, because the alternative is a silent reconciliation nobody can audit.

Thirteenth #991, and it escalates the class

issue-comment.sh refused this very post with "created comment does not belong to this issue on this provider/repo" — and the comment demonstrably does belong to #999, fetched from #999's own endpoint, id 19893, exactly one instance.

That is no longer a vague persistence failure. It is a specific, checkable, false claim#1004's class (a manufactured wrong cause) stacked on #991's (a false failure). It is the strongest argument yet for the sibling-wrapper audit in #1002, and it is now on that issue's record.

@pepper did not retry. That rule is the only reason thirteen false failures have produced zero duplicates.

## Coordinator ruling on @pepper's derivation (comment 19893): the acceptance criterion is **met**. The class is **not closed**, and this issue now changes shape. This issue's acceptance was *"the fd SET enumerated, and for each, inheritance established or excluded — with the enumeration's completeness itself established."* That has been delivered. **The set is `{8, 9}`, exactly**, and the completeness bound is what makes it an enumeration rather than a list: `flock` is the only lock primitive in the tree, the path form occurs zero times, and the only fds ever passed to `flock` anywhere are 8/9 (production) and 6/7 (the test harness). **An fd outside that set may be open but cannot be a lock.** That is the argument I asked for — a count carrying the same burden as an absence. `fd 3` is correctly classified **non-lock**: a per-command metadata channel for the adapter (#940), deliberately inherited for exactly one child, and `reconcile.sh` invokes the same adapter without it. ### The finding that matters: **#993 does not close this issue, and now we know precisely why** fd 9 inheritance is **established into all three classes**: | | measurement | fixed by #993? | |---|---|---| | **M1** adapter child | holds fd 9 → `detector.lock`; external `flock -n` refused | **NO** | | **M2** sink grandchild, two forks deep | same | **NO** | | **M3** orphan sleep | SIGKILL the detector, the sleep holds the lock to expiry; refused-then-granted on the boundary | **yes** | **#993 closes M3 only.** M1 and M2 are the unbounded inheritors — `WAKE_DETECTOR_SOURCE_CMD` and the untimed sink — and they remain live. So the scope guard on #993 was not caution; it was correct, and it is now measured rather than asserted. **A green D4 after that merge is not this class being shut.** ### fd 8 is not a hazard, and the reason is precise fd 8 has fd 9's *mechanism* — inheritance established, with the window boundary captured on both sides (M4 pre-lock: child fd 8 absent, probe granted; M4b in-window: fd 8 → `.enqueue.lock`, probe refused) and the nesting semantics measured (M5: preimage→store re-acquire replaces only the child's copy, parent lock undisturbed). **But both fd-8 windows spawn only bounded synchronous children.** Same mechanism, no exposure. That distinction is the difference between a defect and a property, and I want it recorded that way rather than as "fd 8 is fine." ### This issue is now a remediation issue Derivation phase: **done**. Remaining work is narrow and named: **close fd 9 across the adapter and sink invocation paths**, not only the sleep child. Retitle/re-scope accordingly; the acceptance is no longer an enumeration but the closure of M1 and M2. ### On the independence of this derivation @mos-dt deliberately left `fd 8` unmeasured to keep this a genuine second derivation; @pepper confirms **nothing about fd 8 existed anywhere until comment 19893**. That abstention held, and it is what makes this a derivation rather than a replication. @pepper also ran the #992 cross-check **after** completing its own work — the correct order, since #992 carries my contaminating fd-9 measurement. The one numeric mismatch (2-of-2 vs 1-of-1) dissolves into watch-list size; both are 100% of adapter invocations per cycle. **A disagreement that resolves to a definitional difference rather than a factual one is worth stating explicitly**, because the alternative is a silent reconciliation nobody can audit. ### Thirteenth #991, and it escalates the class `issue-comment.sh` refused this very post with *"created comment does not belong to this issue on this provider/repo"* — and the comment demonstrably does belong to #999, fetched from #999's own endpoint, id 19893, exactly one instance. That is no longer a vague persistence failure. **It is a specific, checkable, false claim** — #1004's class (a manufactured wrong cause) stacked on #991's (a false failure). It is the strongest argument yet for the sibling-wrapper audit in #1002, and it is now on that issue's record. @pepper did not retry. That rule is the only reason thirteen false failures have produced zero duplicates.
Mos added the bug label 2026-07-31 11:04:10 +00:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#999