#!/usr/bin/env python3
"""Anchored builder: fp-driver-d2-r3.sh (ACCEPTED by doyle, AHLVTXUC) -> fp-driver-d2-r4.sh.

THE LAUNCH-IDENTITY CORRECTION, under doyle's three NRTFH236 constraints and the L0/L1
discriminator evidence (controls-out/20260913T100902Z-L):

  L0  negative control failed at exit 1 with its own positive control armed: the anchor
      predicate reads ANCHORED=YES unskewed (delta 0 ms) and NO with the recorded anchor
      skewed 3600s, so a pid-only check is visible as a defect rather than as agreement.
  L1a NATIVE direct-child identity ESTABLISHED. `Start-Process -PassThru` gave pid 53072,
      Win32 re-queried it by pid AND creation instant with delta 0 ms, the executable named
      was the SUBJECT (pwsh.exe, cmdline `-NoProfile -Command Start-Sleep -Seconds 8`) and
      not an intermediary, and the exit came back off the retained object.
  L1b an MSYS subject launched DIRECTLY anchors too (sleep.exe, delta 0 ms). The "msys stays
      PARTIAL by construction" caveat the design reserved is NOT needed.
  L7  pid 53072 was RECYCLED 24 seconds later. A pid-only re-query would have reported the
      subject SURVIVING. That is why every re-query here carries the creation anchor.

WHAT CHANGES (LAUNCH-IDENTITY-DESIGN.md r2, sha ac17e6d7):
  1. bounded() launches through fp-bin/launch_bounded.ps1. `timeout` LEAVES the native launch
     boundary; the launcher creates the subject, retains its handle, writes the identity it
     created, and enforces the bound (constraints 1 and 3).
  2. register_launched() reads that identity record. The ParentProcessId query and the
     descendant enumeration are GONE: they could not reach the subject (A7, A6,
     probe-a3-parent) and no scan may certify coverage anyway (constraint 2).
  3. measure_termination() reports the DIRECT subject's disposition and the whole operation
     SEPARATELY. coverage=COMPLETE is not emissible, so CONFIRMED_GONE is not either
     (constraint 2).
  4. capture_run_identities() loses its interposed `env` — the variable is exported around the
     call and reaches the probe by inheritance, so the intended executable IS the direct child
     (constraint 3).
  5. Every sentence the change falsified is CORRECTED, not left standing: the header's
     KILL_GRACE_S note, the four-point design comment, the BOUND_EXPIRED and BOUND_NOT_STARTED
     records, and the ledger's attribution count.
  6. The instrument gate takes launch_bounded.ps1 as a seventh dependency.

NO TERMINATION IS ADDED ANYWHERE. Tree kill is withdrawn from this step (design §3E): a
subject that outlives its bound is LEFT RUNNING and recorded UNREADABLE. That is a real and
deliberate consequence of holding termination, and it is stated in the driver's own output.

The source is READ, never written. The successor is not applied anywhere and no run consumes it.
"""
import hashlib
import pathlib
import sys

HERE = pathlib.Path(__file__).resolve().parent
SRC = HERE / "fp-driver-d2-r3.sh"
DST = HERE / "fp-driver-d2-r4.sh"
SRC_SHA = "48f1afcf620351d91e1e992f266b24a677d81148addb3b81c72a0639bd884f95"

raw = SRC.read_bytes()
got = hashlib.sha256(raw).hexdigest()
if len(sys.argv) > 1 and sys.argv[1] == "--pin":
    print(got)
    sys.exit(0)
if got != SRC_SHA:
    sys.exit(f"SOURCE_DRIFT: {SRC.name} is {got}, pin says {SRC_SHA}")
if DST.exists():
    sys.exit(f"DESTINATION_EXISTS: {DST.name} is some attempt's evidence; it is not overwritten")

CRLF = chr(13) + chr(10)
lines = raw.decode("utf-8").split(CRLF)


def one(needle, what):
    """The index of the ONLY line equal to `needle`. Anything else refuses the build."""
    hits = [i for i, l in enumerate(lines) if l == needle]
    if len(hits) != 1:
        sys.exit(f"ANCHOR_{'ABSENT' if not hits else 'AMBIGUOUS'}: {what} matched {len(hits)}")
    return hits[0]


def splice(start_line, end_line, new_text, what, end_inclusive=True):
    """Replace the region between two UNIQUE anchor lines with new_text (LF-written here)."""
    global lines
    a = one(start_line, what + " start")
    b = one(end_line, what + " end")
    if b < a:
        sys.exit(f"ANCHOR_ORDER: {what} end precedes start")
    lines = lines[:a] + new_text.split("\n") + lines[b + 1 if end_inclusive else b:]


def sub(old, new, what, count=1):
    """Exact single-line replacement."""
    global lines
    i = one(old, what)
    lines = lines[:i] + new.split("\n") + lines[i + 1:]


# ---------------------------------------------------------------------------
# 1. THE REGISTER AND THE TERMINATION MEASUREMENT — whole-block replacement.
# ---------------------------------------------------------------------------
REG = '''# THE REGISTER READS THE LAUNCHER'S OWN IDENTITY RECORD. It queries no parent link, and it
# enumerates no descendant.
#
# WHY THE QUERY IS GONE, measured. The predecessor read the backgrounded job's winpid out of
# msys `ps` and asked Win32 for THAT pid's children. On cygwin the pid bash hands back is the
# PRE-EXEC STUB's, so the only process that ever answered was the exec'd `timeout.exe`: A7's
# single kind=native row named the WRAPPER, A6 got no row at all, and
# disposition-20260913T092245Z measured five wrapper identities GONE while the subjects they
# bounded had no identity at all and stay UNREADABLE for good. A deeper walk down the same
# links inherits the same broken premise (doyle JDCJYANF).
#
# WHAT REPLACES IT. launch_bounded.ps1 CREATES the subject and writes the identity it created
# -- metadata only -- before it waits. The register copies that in. The identity comes from the
# act of launching, not from a query afterwards, and the launcher keeps the handle so the pid
# cannot be recycled under the record (doyle NRTFH236 #1).
#
# COVERAGE IS NEVER COMPLETE (doyle NRTFH236 #2). Nothing enforces containment, so no launch
# can establish that it has seen everything its subject started. A scan that found nothing
# would not raise this either -- that is the absence of a reading. COMPLETE is not emissible
# anywhere in this driver, and CONFIRMED_GONE therefore is not emissible either.
SUBJECT_STATE=NOT_MEASURED
register_launched() { # LABEL RECORD_FILE
  local label="$1" rec="$2" lf; lf=$(launch_file)
  LAUNCH_SEQ=$((LAUNCH_SEQ + 1))
  local now; now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
  echo "LAUNCHED seq=$LAUNCH_SEQ label=$label kind=launcher exe=[$BIN/launch_bounded.ps1] registered=$now" >> "$lf"
  if [ ! -s "$rec" ]; then
    echo "LAUNCHED seq=$LAUNCH_SEQ label=$label coverage=NONE reason=no-identity-record" >> "$lf"
    record "  LAUNCH($label) the launcher wrote no identity record: coverage=NONE. That is the ABSENCE OF A READING, and it is never the statement that nothing was launched."
    return 0
  fi
  cat "$rec" >> "$R/launch-records.txt" 2>/dev/null
  local subj; subj=$(grep -m1 '^subject pid=' "$rec" 2>/dev/null)
  if [ -z "$subj" ]; then
    echo "LAUNCHED seq=$LAUNCH_SEQ label=$label coverage=NONE reason=record-has-no-subject-row" >> "$lf"
    record "  LAUNCH($label) the identity record names no subject: coverage=NONE, and nothing about this launch can be re-queried."
    return 0
  fi
  echo "LAUNCHED seq=$LAUNCH_SEQ label=$label kind=subject ${subj#subject }" >> "$lf"
  local st; st=$(grep -m1 '^subject_state=' "$rec" 2>/dev/null)
  [ -n "$st" ] && echo "LAUNCHED seq=$LAUNCH_SEQ label=$label $st" >> "$lf"
  echo "LAUNCHED seq=$LAUNCH_SEQ label=$label coverage=PARTIAL reason=direct-subject-only-no-enforced-containment" >> "$lf"
  record "  LAUNCH($label) subject registered from the launcher's own identity record; coverage=PARTIAL -- the DIRECT child only, because nothing here enforces containment"
  return 0
}
# WHAT STOPPED, REPORTED AS TWO THINGS THAT ARE NEVER SUMMED (doyle NRTFH236 #2). The DIRECT
# subject's disposition is measured by pid AND creation instant. The WHOLE OPERATION stays
# ATTRIBUTION_INCOMPLETE while coverage is short of COMPLETE -- which, under this design, it
# always is. CONFIRMED_GONE is not emissible.
#
# THE ANCHOR IS NOT DECORATION: control L7 (controls-out/20260913T100902Z-L) caught pid 53072
# recycled 24 seconds after that subject exited. A pid-only re-query would have called the
# subject SURVIVING.
#
# THE RE-QUERY IS STILL BOUNDED BY `timeout`, DELIBERATELY. That is a bound on a MEASUREMENT
# whose identity is never registered; it is not the launch boundary, and nothing here can be
# mistaken for the subject.
measure_termination() { # LABEL SEQ DEADLINE -> sets TERMINATION and SUBJECT_STATE
  local label="$1" seq="$2" dl="$3" lf; lf=$(launch_file)
  TERMINATION=ATTRIBUTION_INCOMPLETE
  SUBJECT_STATE=UNREADABLE
  local rows; rows=$(grep "^LAUNCHED seq=$seq .* kind=subject pid=" "$lf" 2>/dev/null)
  if [ -z "$rows" ]; then
    record "  SUBJECT_STATE($label)=UNREADABLE -- no subject identity was registered for this launch, so nothing can be re-queried. This is NOT 'confirmed gone'."
    record "  TERMINATION($label)=ATTRIBUTION_INCOMPLETE"
    return
  fi
  local q_left=$(( dl - $(date +%s) ))
  if [ "$q_left" -le 3 ]; then
    record "  SUBJECT_STATE($label)=UNREADABLE -- no budget remained to re-query the registered identity. UNREADABLE is never CLEAR."
    record "  TERMINATION($label)=ATTRIBUTION_INCOMPLETE"
    return
  fi
  [ "$q_left" -gt 10 ] && q_left=10
  local live=0 gone=0 unread=0 p c now_c
  while IFS= read -r r; do
    p=$(echo "$r" | sed -n 's/.*[ ]pid=\\([0-9]*\\).*/\\1/p')
    c=$(echo "$r" | sed -n 's/.*[ ]created=\\([^ ]*\\).*/\\1/p')
    [ -n "$p" ] || continue
    if [ -z "$c" ] || [ "$c" = UNREADABLE ]; then
      unread=$((unread + 1))
      record "  SUBJECT_STATE($label) pid=$p carries NO creation anchor, so it cannot be re-queried. An unanchored pid is never reported gone."
      continue
    fi
    now_c=$(SPT_D2_P="$p" timeout -k 2 "$q_left" pwsh -NoProfile -Command '
      $p = [int]$env:SPT_D2_P
      try { $x = @(Get-CimInstance Win32_Process -Filter "ProcessId=$p" -ErrorAction Stop) }
      catch { "UNREADABLE"; exit 0 }
      if ($x.Count -eq 0) { "GONE"; exit 0 }
      try { $x[0].CreationDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ") } catch { "UNREADABLE" }
    ' 2>/dev/null | tr -d ' \\r\\n')
    case "$now_c" in
      GONE)          gone=$((gone + 1)) ;;
      UNREADABLE|'') unread=$((unread + 1)) ;;
      *)
        # COMPARED AT SECOND GRANULARITY. The launcher records the creation instant to 100ns
        # and this re-query reads the same field, but the two readers round the last digits
        # differently (L1a: ...8749085 recorded, ...8749080 re-queried). A pid recycled within
        # the same second is not a case this box produces -- L7's measured reuse took 24s --
        # and both raw values are written down, so the comparison can be re-derived.
        if [ "${now_c%.*}" = "${c%.*}" ]; then
          live=$((live + 1))
          record "  SUBJECT_STATE($label) SURVIVING pid=$p created=$c -- the bound elapsed and this process did not stop. NOTHING WAS TERMINATED: termination is held."
        else
          gone=$((gone + 1))
          record "  SUBJECT_STATE($label) pid=$p was REUSED (recorded created=$c, now created=$now_c): a different process answers on that pid, so ours is gone"
        fi ;;
    esac
  done <<EOF_ROWS
$rows
EOF_ROWS
  if [ "$live" -gt 0 ]; then SUBJECT_STATE=SURVIVING
  elif [ "$unread" -gt 0 ]; then SUBJECT_STATE=UNREADABLE
  else SUBJECT_STATE=GONE
  fi
  TERMINATION=ATTRIBUTION_INCOMPLETE
  record "  SUBJECT_STATE($label)=$SUBJECT_STATE surviving=$live gone=$gone unreadable=$unread (measured by pid PLUS creation instant, never by pid alone)"
  record "  TERMINATION($label)=ATTRIBUTION_INCOMPLETE -- that is the WHOLE OPERATION, and it stays incomplete because nothing enforces containment over what the subject may have started. The direct subject's disposition above is reported SEPARATELY and is never summed into a whole-operation claim."
}'''
# ---------------------------------------------------------------------------
# 0. winpid_of() IS REMOVED — its only consumer was the descendant query.
# ---------------------------------------------------------------------------
splice(
    "# The WINPID behind an msys pid. `ps` is an msys builtin-class probe over msys's own table:",
    "# ONE bounded CIM query for the children of a live wrapper. It runs WHILE the wrapper is alive",
    """# winpid_of() IS GONE, and not merely unused. Its only consumer was the descendant query, and
# the mapping it supplied -- an msys pid's winpid -- is the very reading that misattributed the
# subject: on cygwin the winpid bash hands back belongs to the PRE-EXEC STUB, so a
# ParentProcessId query fed from it could only ever reach the exec'd wrapper. The launcher now
# reports the identity it CREATED, so nothing needs the map, and leaving the function behind
# would leave the next author a tool whose answer looks authoritative and is not.""",
    "winpid_of removal",
    end_inclusive=False,
)

splice(
    "# ONE bounded CIM query for the children of a live wrapper. It runs WHILE the wrapper is alive",
    "# PRESERVE WHATEVER AN EXPIRED STEP WROTE. Completion unverified is not an absent document:",
    REG,
    "register/measure block",
    end_inclusive=False,
)

# ---------------------------------------------------------------------------
# 2. THE LAUNCH BOUNDARY inside bounded().
# ---------------------------------------------------------------------------
LAUNCH = '''  # THE LAUNCHER IS THE BOUNDARY (doyle NRTFH236 #3). No `timeout` and no `env` stand between
  # this driver and the process it means to run: launch_bounded.ps1 creates the subject
  # DIRECTLY, holds its handle for the child's whole life, writes the identity it created, and
  # enforces the bound itself. The environment reaches the subject by INHERITANCE -- a call site
  # that needs a variable exports it around this call.
  #
  # ARGUMENTS CROSS ON DISK, ONE PER LINE. An array handed through `pwsh -File` collapses into a
  # single string, which is how quoting defects are built. (Ceiling: an argument containing a
  # newline would not survive this protocol. No call site has one, and one would have to be
  # written deliberately.)
  local myseq=$((LAUNCH_SEQ + 1))
  local rec="$R/.launch-rec.$myseq" argf="$R/.launch-args.$myseq"
  rm -f "$rec" "$argf"
  local lexe="$1"; shift
  local lwin; lwin=$(win_exe "$lexe")
  if [ -z "$lwin" ]; then
    BOUNDED_STATE=SETUP_FAILED
    exits "${label}_LAUNCH=UNRESOLVABLE"
    record "LAUNCH_UNRESOLVABLE $label — the program [$lexe] does not resolve on PATH. The step is NOT RUN and NOTHING WAS LAUNCHED, which is a different statement from a step that ran and failed."
    return 91
  fi
  if [ $# -gt 0 ]; then printf '%s\\n' "$@" > "$argf"; else : > "$argf"; fi
  pwsh -NoProfile -File "$(cygpath -w "$BIN/launch_bounded.ps1")" \\
       -Label "$label" -Seconds "$left" \\
       -RecordFile "$(cygpath -w "$rec")" -ArgsFile "$(cygpath -w "$argf")" \\
       -Exe "$lwin" \\
       -OutFile "$(cygpath -w "$out")" -ErrFile "$(cygpath -w "$err")" \\
       > "$R/.launch-log.$myseq" 2>&1
  local rc=$?
  # THE LAUNCHER'S OWN STREAMS GO TO ITS OWN FILE. The SUBJECT's output is written by the
  # launcher straight into OUT and ERR, so anything the launcher itself says would otherwise
  # contaminate either the subject's capture or this driver's log (`record` tees to stdout --
  # r10's defect, and it cost IR-123 an unpassable gate).
  [ -s "$R/.launch-log.$myseq" ] && record "  LAUNCHER($label) wrote to .launch-log.$myseq: $(head -3 "$R/.launch-log.$myseq" | tr '\\n' ' ')"
  register_launched "$label" "$rec"'''
splice(
    '  timeout -k "$KILL_GRACE_S" "$left" "$@" > "$out" 2> "$err" &',
    '  wait "$mpid"; local rc=$?',
    LAUNCH,
    "bounded launch block",
)

# ---------------------------------------------------------------------------
# 3. win_exe(), immediately above bounded().
# ---------------------------------------------------------------------------
sub(
    "bounded() { # LABEL DEADLINE_EPOCH OUT ERR -- COMMAND...",
    """# THE EXECUTABLE MUST BE NAMED TO WINDOWS. A call site spells its program the way bash finds
# it; the launcher needs a Windows path. `command -v` resolves it against the SAME PATH bash
# would have used, so the program that runs is the program the call site named. An
# unresolvable program is refused AT THE CALL, where it is still true that nothing was
# launched -- not inside the launcher, where it would be an error with no identity behind it.
win_exe() { # PROGRAM -> WINDOWS PATH | ''
  local prog="$1" p
  p=$(command -v "$prog" 2>/dev/null) || return 1
  [ -n "$p" ] || return 1
  cygpath -w "$p" 2>/dev/null
}
bounded() { # LABEL DEADLINE_EPOCH OUT ERR -- COMMAND...""",
    "bounded header",
)

# ---------------------------------------------------------------------------
# 4-6. THE RECORDS THE CHANGE FALSIFIED.
# ---------------------------------------------------------------------------
sub(
    '    record "BOUND_NOT_STARTED $label — ${left}s would remain after the ${KILL_GRACE_S}s escalation reserve. The step is NOT RUN."',
    '    record "BOUND_NOT_STARTED $label — ${left}s would remain after the ${KILL_GRACE_S}s launcher reserve. The step is NOT RUN."',
    "BOUND_NOT_STARTED record",
)
sub(
    '    record "BOUND_EXPIRED $label after ${left}s (killer rc=$rc). The bound stopped THIS DRIVER\'S WAIT and attempted to stop the operation; what actually stopped is measured below."',
    '    record "BOUND_EXPIRED $label after ${left}s (launcher rc=$rc). The bound stopped THIS DRIVER\'S WAIT AND NOTHING ELSE: no termination is authorized here, so the subject MAY STILL BE RUNNING. What it is actually doing is measured below."',
    "BOUND_EXPIRED record",
)
sub(
    '    record "D2($point) NOT RUN — the remaining budget could not cover the ${KILL_GRACE_S}s escalation reserve. NOT CAPTURED is not \'not present\'."',
    '    record "D2($point) NOT RUN — the remaining budget could not cover the ${KILL_GRACE_S}s launcher reserve. NOT CAPTURED is not \'not present\'."',
    "D2 no-budget record",
)

# ---------------------------------------------------------------------------
# 7. THE HEADER'S KILL_GRACE_S NOTE.
# ---------------------------------------------------------------------------
splice(
    "#   KILL_GRACE_S      `timeout -k N` TERMs at the bound and KILLs N seconds later, so a bound",
    "#                     subtracts it, so TERM+KILL completes BY the deadline.",
    """#   KILL_GRACE_S      the seconds every bound holds back from its own deadline. It was named
#                     for `timeout -k N`'s TERM-then-KILL escalation, and NOTHING ESCALATES
#                     ANY MORE: no `timeout` stands on the launch boundary, and termination is
#                     held -- no kill, no tree kill. The reserve now covers the LAUNCHER'S OWN
#                     RETURN (writing the outcome into the identity record and reporting), so
#                     a bound still completes BY the deadline rather than past it, and a step
#                     that cannot cover it is NOT STARTED.""",
    "KILL_GRACE_S header note",
)

# ---------------------------------------------------------------------------
# 8. THE FOUR-POINT DESIGN COMMENT -- items 1 to 3.
# ---------------------------------------------------------------------------
splice(
    "#   1. the bound subtracts KILL_GRACE_S, so TERM-then-KILL completes BY the deadline rather",
    "#      are in no job object the kill reaches. A read-only command still leaves a process;",
    """#   1. the bound subtracts KILL_GRACE_S, so the LAUNCHER'S OWN RETURN completes BY the
#      deadline rather than past it, and a step that cannot cover it is NOT STARTED;
#   2. the subject is REGISTERED FROM THE ACT OF LAUNCHING IT. The register no longer asks
#      Win32 for a wrapper's children: that query could only ever reach the wrapper itself
#      (A7, A6, probe-a3-parent), because the winpid bash hands back is a pre-exec stub's.
#      The identity kept is the DIRECT child's, and it carries NO claim over anything that
#      child may itself have started;
#   3. on expiry the driver MEASURES the subject's disposition by pid AND creation instant,
#      and TERMINATES NOTHING. Tree kill is withdrawn until containment behaviour is
#      reviewable, so an expired step's subject MAY STILL BE RUNNING and is reported
#      SURVIVING rather than stopped. `timeout` sent TERM to a native Windows process that
#      owed it no POSIX answer, and whose descendants sat in no job object the kill reached;
#      dropping it removes a wrapper, not a guarantee;""",
    "four-point comment",
)

# ---------------------------------------------------------------------------
# 9. THE INTERPOSED `env` AT THE IDENTITY CAPTURE.
# ---------------------------------------------------------------------------
splice(
    '  bounded "identity_capture_$when" "$(cur_deadline)" "$R/run-identities.txt" "$R/run-identities.err" -- \\',
    '    env SPT_D2_HOME="$H" pwsh -NoProfile -Command \'',
    """  # THE INTERPOSED `env` IS GONE (doyle NRTFH236 #3). It was a whole extra process standing
  # between this driver and the probe, and with it in the way the launch boundary's direct
  # child was the INTERMEDIARY rather than the probe itself. The variable now reaches the probe
  # by INHERITANCE, which is what an environment is for, and the intended executable IS the
  # direct child. It is exported around this call only, and unset immediately after.
  export SPT_D2_HOME="$H"
  bounded "identity_capture_$when" "$(cur_deadline)" "$R/run-identities.txt" "$R/run-identities.err" -- \\
    pwsh -NoProfile -Command '""",
    "identity capture env",
)
sub(
    "  IDENT_CAPTURE_RC=$rc",
    """  IDENT_CAPTURE_RC=$rc
  unset SPT_D2_HOME""",
    "IDENT_CAPTURE_RC",
)

# ---------------------------------------------------------------------------
# 10. THE INSTRUMENT GATE -- seven dependencies.
# ---------------------------------------------------------------------------
splice(
    "for inst in census.ps1 runner-census.ps1 portfields.ps1 d1_render.py d2_capture.ps1 d2_validate.py; do",
    'record "INSTRUMENTS hashed into INSTRUMENTS.sha256 (6 files)"',
    """for inst in census.ps1 runner-census.ps1 portfields.ps1 d1_render.py d2_capture.ps1 d2_validate.py launch_bounded.ps1; do
  [ -f "$BIN/$inst" ] || die "instrument $BIN/$inst is missing — the seven dependencies must all exist before any runnable claim"
done
sha256sum "$BIN"/census.ps1 "$BIN"/runner-census.ps1 "$BIN"/portfields.ps1 "$BIN"/d1_render.py \\
          "$BIN"/d2_capture.ps1 "$BIN"/d2_validate.py "$BIN"/launch_bounded.ps1 \\
  > "$R/INSTRUMENTS.sha256" 2>"$R/instruments.err"
irc=$?
exits "instruments_sha_EXIT=$irc"
[ "$irc" -eq 0 ] || die "could not hash the instruments — directory placement is not provenance (C); the hashes must ride the evidence"
record "INSTRUMENTS hashed into INSTRUMENTS.sha256 (7 files)\"""",
    "instrument gate",
)

# ---------------------------------------------------------------------------
# 11. THE LEDGER'S ATTRIBUTION COUNT.
# ---------------------------------------------------------------------------
splice(
    "    local out_n; out_n=$(grep -c 'state=ATTRIBUTION_INCOMPLETE' \"$(launch_file)\" 2>/dev/null)",
    "    record \"LAUNCHED_IDENTITIES $(grep -c '^LAUNCHED ' \"$(launch_file)\" 2>/dev/null) rows, of which $out_n are ATTRIBUTION_INCOMPLETE — see launched-identities.txt. An incomplete attribution is NOT a process measured absent.\"",
    """    local subj_n cov_n
    subj_n=$(grep -c ' kind=subject pid=' "$(launch_file)" 2>/dev/null)
    cov_n=$(grep -c ' coverage=' "$(launch_file)" 2>/dev/null)
    case "$subj_n" in (''|*[!0-9]*) subj_n=0 ;; esac
    case "$cov_n" in (''|*[!0-9]*) cov_n=0 ;; esac
    record "LAUNCHED_IDENTITIES $(grep -c '^LAUNCHED ' "$(launch_file)" 2>/dev/null) rows: $subj_n DIRECT subjects across $cov_n launches, and every launch is coverage=PARTIAL or NONE — see launched-identities.txt. No launch in this driver can claim COMPLETE coverage of what it started, so no whole operation is reported CONFIRMED_GONE; a direct subject's own disposition is reported separately and is never summed into one.\"""",
    "ledger attribution count",
)

out = CRLF.join(lines)
DST.write_text(out, encoding="utf-8", newline="")
print(f"BUILT {DST.name} sha256={hashlib.sha256(DST.read_bytes()).hexdigest()}")
print(f"lines {len(raw.decode('utf-8').split(CRLF))} -> {len(lines)}")
