Ctrl K
ComunidadGuías…

deploy/app-env-conformance.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/**
 * Framework-gap conformance for every app.env that ships outside this repo.
 *
 * The deploy/ framework gained six keys' worth of generality in #1108 (health
 * contract, DATA_DIR ownership, unit extras, the source build step, per-app
 * binary file lists, a bounded stop) plus the ADR §4/§5 app.env keys that #1110
 * (ziptask, DIST=binary) and #1111 (subagentix, DIST=source) each shipped. The
 * framework's OWN suite proved the new code against synaptomind's app.env, which
 * sets NONE of them — every new key takes its `:=` default there, so a green
 * `bun test ./deploy/` says the defaults are intact and nothing about whether a
 * real app can use the feature.
 *
 * THIS file is that missing half. It takes each app's SHIPPED app.env, sandboxed
 * into a temp tree, and drives the real install.sh against it. Nothing is
 * hand-fed to the framework: the keys under test come from the file the repo
 * ships, and the only edits are the paths (INSTALL_DIR / DATA_DIR / RUN_DIR /
 * UNIT_FILE / HOOKS_DIR / EXEC_START), which is what makes it a sandbox and not
 * a re-specification.
 *
 * See ./app-targets for how targets are named (DEPLOY_APP_ENVS) and why a
 * requested-but-unresolved target fails the run instead of skipping it.
 *
 * SANDBOXING, and the two traps this file is built around:
 *
 *  1. `load_app_env()` SOURCES app.env, so a plain assignment in it beats the
 *     process environment. Both new app.envs carry `RUN_DIR=""`, and
 *     resolve_target_user() then derives the operator's REAL ~/.ziptask from
 *     `getent passwd` — the #1101 incident, where a fixture overwrote the live
 *     app.env while every assertion stayed green. So the sandbox is written INTO
 *     the app.env copy (sandboxAppEnvText), assertSandboxed() refuses any
 *     override outside the fixture root, and guardRealStateDir() fingerprints
 *     the real state dir for the whole run.
 *  2. `sudo` has its own secure PATH, so a PATH stub does NOT intercept it.
 *     A "sandboxed" demo once restarted the live service five times that way
 *     (AGENTS.md §8). sudo/systemctl are therefore stubbed for real here, and
 *     nothing privileged runs.
 */

import { describe, expect, test, afterEach } from 'bun:test'
import { spawnSync } from 'node:child_process'
import {
  chmodSync,
  cpSync,
  mkdirSync,
  readFileSync,
  rmSync,
  statSync,
  writeFileSync
} from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { readAppEnvValues, sandboxAppEnvText } from './app-env-targets'
import { checkCoverage, guardProductionPaths, resolveTargets } from './app-targets'
import { guardRealStateDir, installCleanup, isolatedEnv, mkTempTree } from './tmp-fixtures'

installCleanup()
guardRealStateDir()

const DEPLOY_DIR = import.meta.dir
const LIB = join(DEPLOY_DIR, 'lib', 'common.sh')
const TARGETS = resolveTargets()
// Containment for the TARGETS, not just for this repo's own app: guardRealStateDir()
// watches ~/.synaptomind, which says nothing about ~/.ziptask or ~/.subagentix.
// See guardProductionPaths for why the check lives at process exit and why the
// live tracker's own data directory is excluded from the watch list.
guardProductionPaths(TARGETS.resolved)

/** POSIX single-quoting for a bash heredoc body. */
function q(value: string): string {
  return `'${value.replace(/'/g, `'\\''`)}'`
}

/**
 * The /health body both new apps actually answer with: a bare `{"ok":true}`,
 * no `status`, no `version` (ADR §3d).
 *
 * Named rather than written inline because it is a JSON document inside a shell
 * single-quoted string inside a TypeScript single-quoted string — three quoting
 * layers, and a mis-quoted one produces a parse error at best and a silently
 * different body at worst.
 */
const HEALTHY_BODY = '{"ok":true}'
/** The same contract with the ok-value flipped: must fail, or the gate is vacuous. */
const UNHEALTHY_BODY = '{"ok":false}'

/**
 * Replace ONE assignment line in an app.env text, appending it when absent.
 *
 * The function replacement is not incidental: `line` is test data that may hold
 * `$` (a glob, a value under test), and `String.replace` would read a `$pattern`
 * in it. The caller is responsible for the line being safe to SOURCE — see
 * renderUnit's rawAppEnvLines, which exists only because the fixture's own
 * quote() refuses a double quote in an override.
 */
function replaceAssignment(text: string, key: string, line: string): string {
  const re = new RegExp(`^${key}=`, 'm')
  return re.test(text) ? text.replace(re, () => line) : `${text}\n${line}\n`
}

/**
 * unit_value_defect called DIRECTLY, with the value as an ARGVUMENT.
 *
 * `bash -c <script> <name> <value>` puts the value in $1, so it never passes
 * through any shell text: what the guard is asked about is exactly what the test
 * wrote. That distinction is the whole reason this helper exists — finding #3
 * ("a double quote in a UNIT_EXTRA_ENV pair renders rc=0") came from a probe that
 * wrote the value RAW into the script, where `UNIT_EXTRA_ENV=FOO=ba"r` is a bash
 * SYNTAX error: the sourcing shell aborted on that line, the key kept its default
 * empty, and the render then succeeded on nothing. Returns what the guard PRINTS
 * ('' when it finds no defect), not an exit status — unit_value_defect reports
 * through its output.
 */
function unitValueDefect(value: string): string {
  const res = spawnSync(
    'bash',
    ['-c', `. ${q(LIB)}\nunit_value_defect "$1"\n`, 'unit-value', value],
    {
      encoding: 'utf8',
      timeout: 30_000
    }
  )
  return (res.stdout ?? '').trim()
}

let ROOT = ''
afterEach(() => {
  if (ROOT) rmSync(ROOT, { recursive: true, force: true })
  ROOT = ''
})

// ── Fixture ─────────────────────────────────────────────────────────────────

/**
 * A copy of the FRAMEWORK (install.sh + lib/common.sh) with the target's real
 * app.env dropped in and every path redirected into a temp tree.
 *
 * The framework scripts come from HERE, not from the target's vendored copy: the
 * gaps under test live in this repo's copy, and a test of "does the framework
 * support X" run against a vendored copy would prove the vendor copied the
 * feature rather than that the feature works. The target contributes app.env —
 * which is the artifact these tests are actually about — plus nothing else.
 */
function seedTarget(
  target: { label: string; appEnvPath: string },
  opts: { env?: Record<string, string> } = {}
) {
  const root = ensureRoot(`conform-${target.label}-`)
  const deployDir = join(root, 'deploy')
  cpSync(DEPLOY_DIR, deployDir, {
    recursive: true,
    filter: src =>
      !src.endsWith('.test.ts') &&
      !src.endsWith('app-targets.ts') &&
      !src.endsWith('app-env-targets.ts')
  })
  const installDir = join(root, 'opt')
  const dataDir = join(root, 'data')
  const runDir = join(root, 'run')
  const unitFile = join(root, 'unit', `${target.label}.service`)
  mkdirSync(join(root, 'unit'), { recursive: true })

  const overrides: Record<string, string> = {
    INSTALL_DIR: installDir,
    DATA_DIR: dataDir,
    RUN_DIR: runDir,
    HOOKS_DIR: join(runDir, 'hooks'),
    EXEC_START: `${installDir}/bin`,
    ...opts.env
  }
  // Every path this suite redirects must land inside the fixture root. A value
  // copied verbatim out of a shipped app.env (/opt/ziptask, /var/lib/…) would
  // otherwise pass this helper and write to the host.
  assertOverridesInside(overrides, root)
  writeFileSync(join(deployDir, 'app.env'), sandboxAppEnvText(target.appEnvPath, overrides))

  return {
    deployDir,
    installDir,
    dataDir,
    runDir,
    unitFile,
    appEnvPath: join(deployDir, 'app.env')
  }
}

/**
 * The one temp tree a test may use, created on FIRST use.
 *
 * Lazy rather than created in an afterEach-able top-level statement because the
 * helpers that build the fixture (a git origin, a release dir, the stub bin) all
 * need the same root and are called in an order that varies per test. Every one
 * of them goes through this, so a fixture cannot accidentally build part of
 * itself outside the tree the sweep removes — which is what happened on the first
 * run: `git init` in the repo root, three untracked dirs left behind.
 */
function ensureRoot(prefix = 'deploy-conform-'): string {
  if (!ROOT) ROOT = mkTempTree(prefix)
  return ROOT
}

const SANDBOX_PATH_KEYS = ['INSTALL_DIR', 'DATA_DIR', 'RUN_DIR', 'HOOKS_DIR', 'EXEC_START'] as const

function assertOverridesInside(overrides: Record<string, string>, root: string): void {
  for (const key of SANDBOX_PATH_KEYS) {
    const value = overrides[key]
    if (!value) continue
    if (!value.startsWith(`${root}/`)) {
      throw new Error(`sandbox override ${key}=${value} is outside the fixture root ${root}`)
    }
  }
}

/**
 * sudo + systemctl stubs that LOG and do nothing.
 *
 * `execSudo` is off by default: a logging sudo writes nothing, which is what
 * keeps the "nothing outside the fixture changed" claim cheap. A test that needs
 * a file to actually appear turns it on, and the stub then refuses any path
 * outside the fixture root rather than trusting its caller.
 */
function seedPrivStubs(root: string, opts: { execSudo?: boolean; chownSnapshot?: boolean } = {}) {
  const stubsDir = join(root, 'stubs')
  const log = join(root, 'privileged.log')
  mkdirSync(stubsDir, { recursive: true })
  writeFileSync(log, '')
  for (const name of ['sudo', 'systemctl']) {
    const lines = [
      '#!/usr/bin/env bash',
      'printf \'%s\' "$0" >> "$STUB_PRIV_LOG"',
      'for a in "$@"; do printf \' %s\' "$a" >> "$STUB_PRIV_LOG"; done',
      'printf \'\\n\' >> "$STUB_PRIV_LOG"'
    ]
    if (name === 'systemctl') {
      lines.push('[ "$1" = "is-system-running" ] && echo running')
    } else if (opts.execSudo) {
      lines.push(
        'for a in "$@"; do',
        '  case "$a" in',
        '    /*)',
        // Allowed: the fixture root (where the unit lives) AND the host tmpdir,
        // because install.sh renders the unit into `mktemp -d` and then `cp`s it
        // into the staging file beside the unit. Without the tmpdir arm that cp is
        // REFUSED (exit 99), and the run reports `cannot write the replacement
        // into …/unit` followed by "Service: skipped" — a fixture defect that
        // reads exactly like a framework defect.
        `      case "$a" in ${root}/*|"${tmpdir()}"/*) ;; *) echo "STUB: refused path $a" >&2; exit 99 ;; esac`,
        '      ;;',
        '  esac',
        'done',
        'case "${1:-}" in',
        // chown is here because it IS the command under test: apply_ownership
        // issues it through run_root, and a stub that refused it would make
        // every "cannot chown" assertion pass for the wrong reason — the
        // refusal would be the stub's, not the unprivileged install's. The
        // refusal path has its own stub (refuseChownInSudoStub).
        '  touch|chmod|cp|mv|rm|install|systemctl|mkdir|ln|chown) ;;',
        '  *) echo "STUB: refused command ${1:-}" >&2; exit 99 ;;',
        'esac'
      )
      if (opts.chownSnapshot) {
        // What was in each chowned directory AT THE MOMENT the chown ran, as one
        // `LS <dir>:a,b,c` line per absolute argument. The ordering claim of
        // apply_run_dir_ownership — "it runs after the helper installers, or it
        // chowns an empty directory" — is only observable from inside the run: a
        // log of calls cannot tell a chown of a not-yet-created directory from a
        // chown of an empty one, and both are the defect.
        lines.push(
          'case "${1:-}" in',
          '  chown)',
          '    for d in "$@"; do',
          '      case "$d" in',
          '        /*)',
          '          printf \'LS %s:\' "$d" >> "$STUB_PRIV_LOG"',
          '          ls -1 "$d" 2>/dev/null | tr \'\\n\' \',\' >> "$STUB_PRIV_LOG"',
          '          printf \'\\n\' >> "$STUB_PRIV_LOG"',
          '          ;;',
          '      esac',
          '    done',
          '    ;;',
          'esac'
        )
      }
      lines.push('exec "$@"')
    } else {
      lines.push(':', 'exit 0')
    }
    lines.push('exit 0')
    const path = join(stubsDir, name)
    writeFileSync(path, lines.join('\n'))
    chmodSync(path, 0o755)
  }
  return { stubsDir, log }
}

function privCalls(log: string): string[] {
  const raw = readFileSync(log, 'utf8').trim()
  return raw ? raw.split('\n') : []
}

/**
 * A `stat` stub that reports a DIFFERENT owner for the fixture trees.
 *
 * WHY THIS IS NEEDED. apply_ownership() skips a chown that would change nothing
 * (`install.sh:407` compares `stat -c '%U:%G'` against TARGET_USER:TARGET_GROUP
 * and `continue`s when they match). A fixture tree created by the test runner is
 * owned by the runner — who is also TARGET_USER — so the shipped code correctly
 * issues NO chown at all, and an assertion that a chown was issued would fail
 * against correct behaviour.
 *
 * Making the fixture tree look root-owned is what puts the run on the branch the
 * gap added. Only the `%U:%G` format is intercepted: `%a` (mode) is used by the
 * unit writer to preserve an operator's 600, and forwarding everything else keeps
 * that path real. A stub that answered `%a` with a fixed value would silently
 * change what mode the installed unit gets.
 */
function seedForeignOwnerStat(stubsDir: string): void {
  const path = join(stubsDir, 'stat')
  writeFileSync(
    path,
    [
      '#!/usr/bin/env bash',
      'for a in "$@"; do',
      '  if [ "$a" = "%U:%G" ]; then printf \'root:root\\n\'; exit 0; fi',
      'done',
      // Everything else (notably `-L -c %a`) is the REAL stat.
      `exec /usr/bin/stat "$@"`
    ].join('\n')
  )
  chmodSync(path, 0o755)
}

/**
 * Rewrite the sudo stub so every `chown` fails, as an unprivileged install does.
 *
 * The logging line is kept, so a test can still see that chown was ATTEMPTED —
 * otherwise "no warning" and "never tried" would be indistinguishable, which is
 * exactly what would make the non-fatal assertion below vacuous.
 */
function refuseChownInSudoStub(stubsDir: string): void {
  const stub = join(stubsDir, 'sudo')
  writeFileSync(
    stub,
    [
      '#!/usr/bin/env bash',
      'printf \'%s\' "$0" >> "$STUB_PRIV_LOG"',
      'for a in "$@"; do printf \' %s\' "$a" >> "$STUB_PRIV_LOG"; done',
      'printf \'\\n\' >> "$STUB_PRIV_LOG"',
      'case "${1:-}" in chown) echo "chown: cannot change ownership: Operation not permitted" >&2; exit 1 ;; esac',
      'exit 0'
    ].join('\n')
  )
  chmodSync(stub, 0o755)
}

/**
 * A curl stub that answers BOTH kinds of request an install makes:
 *   - the release asset (a `file://` URL) → copies the fixture tarball to the
 *     `-o` target, because `binary_stage_payload` downloads it with real curl and
 *     then extracts with real tar;
 *   - the health URL → prints `body`.
 *
 * Both branches are needed. A stub that answered only the health URL made the
 * binary install fail at `tar: cannot open … No such file` — a fixture defect
 * that looked exactly like a framework defect, and cost a full round of
 * debugging before the log line showed it.
 */
function seedHealthStub(
  stubsDir: string,
  body: string,
  opts: { log?: string; asset?: string } = {}
) {
  const path = join(stubsDir, 'curl')
  const lines = [
    '#!/usr/bin/env bash',
    'url=""; out=""; prev=""',
    'for a in "$@"; do',
    '  case "$a" in *://*) url="$a" ;; esac',
    '  if [ "$prev" = "-o" ]; then out="$a"; fi',
    '  prev="$a"',
    'done'
  ]
  if (opts.log) lines.push(`printf '%s\\n' "$url" >> ${q(opts.log)}`)
  lines.push('case "$url" in')
  if (opts.asset) {
    lines.push(
      `  file://*) cp ${q(opts.asset)} "\$out"; exit \$? ;;`,
      `  *) printf '%s' ${q(body)}; exit 0 ;;`,
      'esac'
    )
  } else {
    lines.push(`  *) printf '%s' ${q(body)}; exit 0 ;;`, 'esac')
  }
  writeFileSync(path, lines.join('\n'))
  chmodSync(path, 0o755)
}

/**
 * Run install.sh out of the sandboxed deploy tree.
 *
 * `--no-service` is never passed: the unit-rendering and the ordering assertions
 * below depend on the service phase actually running. What keeps that safe is
 * the stub layer, not a flag — sudo and systemctl are stubs (a PATH stub does
 * NOT cover sudo, whose secure_path is its own; see the header), UNIT_FILE points
 * inside the fixture, and guardRealStateDir() fails the run if the operator's
 * real state dir moved.
 */
function runInstall(
  fx: ReturnType<typeof seedTarget>,
  stubsDir: string,
  privLog: string,
  args: string[] = [],
  extra: Record<string, string> = {}
) {
  return spawnSync('bash', [join(fx.deployDir, 'install.sh'), ...args], {
    encoding: 'utf8',
    env: isolatedEnv(ROOT, {
      PATH: `${stubsDir}:${process.env.PATH}`,
      STUB_PRIV_LOG: privLog,
      APP_ENV_URL: '',
      LIB_RAW_URL: '',
      // UNIT_FILE is the TARGET's, not isolatedEnv()'s. That helper pins
      // `…/unit/synaptomind.service` because the framework suite is
      // synaptomind's; app.env cannot carry the key either (no app.env declares
      // it — it is an environment override by design, install.sh:499), so a
      // target's install would otherwise write a unit named after THIS repo's
      // app and every service assertion would read a file the target never had.
      // Observed directly: the ziptask run reported
      // `not replaced: …/unit/synaptomind.service` and then "Service: skipped".
      UNIT_FILE: fx.unitFile,
      // A short health budget: the curl stub answers on the first poll, and a
      // test that reaches a FAILING gate should fail on the assertion rather
      // than on the shipped 60 s wait.
      HEALTH_TIMEOUT: '2',
      HEALTH_CONFIRM_TIMEOUT: '1',
      ...extra
    }),
    cwd: ROOT,
    timeout: 90_000
  })
}

// ══════════════════════════════════════════════════════════════════════════════
//  Gap coverage — always runs, against whatever DEPLOY_APP_ENVS named
// ══════════════════════════════════════════════════════════════════════════════

/**
 * Labels of the targets some test below actually asserts against.
 *
 * Recorded at DECLARATION time, not inside a `test()` body. The coverage check
 * runs FIRST, and every `exercised.push()` that lived inside a test body was
 * therefore still empty when it ran — so it failed with "named ziptask,
 * subagentix but no test exercised it" on a suite where every target WAS
 * exercised. The check is about which targets this file registers suites for,
 * which is a property of the file, not of run order.
 */
const exercised: string[] = []

function declareExercised(targets: readonly { label: string }[]): void {
  for (const t of targets) if (!exercised.includes(t.label)) exercised.push(t.label)
}

describe('deploy/app-targets — coverage of the requested targets', () => {
  test('every target DEPLOY_APP_ENVS named was actually exercised below', () => {
    // Placed first so a target that never gets exercised is reported by the
    // suite itself rather than by whoever reads the skip count.
    checkCoverage(TARGETS, exercised)
    if (TARGETS.requested.length === 0) {
      expect(TARGETS.resolved).toEqual([])
      return
    }
    expect(TARGETS.resolved.length).toBeGreaterThan(0)
  })

  test('unresolved entries are only tolerated for a genuinely absent app', () => {
    // A DEPLOY_APP_ENVS path that exists but is not a deploy/ tree is a typo and
    // must fail; one that does not exist at all is "this machine has no such
    // app", which is reported here so it is never mistaken for a pass.
    for (const entry of TARGETS.unresolved) {
      expect(entry).toMatch(/\(not an absolute path\)|\(no app\.env\)|\(no install\.sh\)/)
    }
  })
})

// ── Per-target: the keys the app.env must carry, and the paths it must not ───

describe('deploy/app.env — the shipped key contract', () => {
  for (const target of TARGETS.resolved) {
    declareExercised(TARGETS.resolved)
    test(`${target.label}: declares the keys its mode depends on, all inside a sandbox`, () => {
      const values = readAppEnvValues(target.appEnvPath)
      const binary = values.DIST === 'binary'
      const source = values.DIST === 'source'

      // The mode itself, first: every assertion below reads differently
      // depending on it, so a wrong DIST must not silently pick a branch.
      expect(binary || source, `${target.label} DIST is ${JSON.stringify(values.DIST)}`).toBe(true)

      // Identity + the health contract. These three are what let an app whose
      // /health is `{"ok":true}` pass the gate at all (ADR §3d); without them the
      // install fails at its OWN health check on every run.
      expect(values.APP_NAME).toBe(target.label)
      expect(
        values.HEALTH_STATUS_FIELD,
        'HEALTH_STATUS_FIELD must not be blank — blank falls back to "status"'
      ).not.toBe('')
      expect(values.HEALTH_OK_VALUES).not.toBe('')
      expect(
        values.HEALTH_VERSION_FIELD,
        'the key must be PRESENT: empty disables the version check'
      ).toBe('')

      // Canonical layout (ADR §4/§5): code under /opt/<app>, data under
      // /var/lib/<app>, state under ~/.<app>. These are the HOST paths the
      // shipped app.env names; the sandbox rewrites them, so asserting them here
      // is what proves the rewrite is necessary and not cosmetic.
      expect(values.INSTALL_DIR).toBe(`/opt/${target.label}`)
      expect(values.DATA_DIR).toBe(`/var/lib/${target.label}`)
      expect(values.RUN_DIR, 'an empty RUN_DIR is the documented "derive from HOME" form').toBe('')

      if (source) {
        // The source build pair. INSTALL_FLAGS must not carry --production when
        // a build is configured: a build needs devDependencies, and installing
        // production-only deps first makes it fail on a missing bundler.
        expect(values.INSTALL_FLAGS).not.toContain('--production')
        expect(values.BUILD_CMD, 'a source app that ships no BUILD_CMD cannot build').not.toBe('')
        expect(Number(values.BUILD_TIMEOUT)).toBeGreaterThan(0)
      }

      if (binary) {
        // The per-app binary lists. Without them the payload check looks for
        // synaptomind's own file names, which #1110 measured as a hard abort:
        // "release payload is incomplete: missing synaptomind".
        for (const key of [
          'BINARY_SUPPORTED_PLATFORMS',
          'BINARY_REQUIRED_FILES',
          'BINARY_ROLLBACK_FILES',
          'BINARY_SWAP_ORDER'
        ]) {
          expect(values[key], `${target.label} ${key} is required for DIST=binary`).toBeTruthy()
        }
        for (const key of ['BINARY_REQUIRED_FILES', 'BINARY_ROLLBACK_FILES', 'BINARY_SWAP_ORDER']) {
          for (const file of values[key].split(/\s+/)) {
            expect(file, `${key} names ${target.label}'s own artefact`).toBe(target.label)
          }
        }
        // Single-quoted on purpose, so render_template() still has a ${TAG} to
        // substitute. A double-quoted value is expanded at SOURCE time and the
        // asset pattern arrives already collapsed.
        expect(values.ASSET_PATTERN).toContain('${TAG}')
        expect(values.ASSET_PATTERN).toContain('${APP_NAME}')
      }
    })
  }
})

// ── Gap (a): the health contract, per app.env ───────────────────────────────
//
// Driven against the real lib/common.sh with the target's real health keys,
// and the bodies those keys are FOR: `{"ok":true}` for the two new apps. Each
// variant is paired with a negative control, because a health gate that accepts
// everything is the vacuous pass these keys exist to prevent.

describe('deploy/health contract — one gate, several contracts', () => {
  /** wait_health against a scripted body, with the target's own health keys. */
  function runGate(
    target: { label: string; appEnvPath: string },
    body: string,
    opts: { keys?: Record<string, string>; expected?: string } = {}
  ) {
    const dir = mkTempTree('gate-')
    // The sandboxed app.env goes to a FILE and is sourced from there. Inlining
    // the text into the script (`. "$text"`) sources it as a FILENAME, which
    // fails with "No such file or directory" — and then every key silently keeps
    // its framework default. That is the worst shape of bug this file could have:
    // the "the app's keys pass {"ok":true}" test would have been asserting the
    // DEFAULTS, and the negative control below would have been the only thing
    // standing between a broken suite and a green one.
    const appEnvFile = join(dir, 'app.env')
    writeFileSync(
      appEnvFile,
      sandboxAppEnvText(target.appEnvPath, {
        INSTALL_DIR: join(dir, 'opt'),
        DATA_DIR: join(dir, 'data'),
        RUN_DIR: join(dir, 'run'),
        ...opts.keys
      })
    )
    const script = `
. ${q(LIB)}
. ${q(appEnvFile)}
APP_NAME=${q(target.label)}
TARGET_USER=$(id -un)
url_get() { printf '%s' ${q(body)}; return 0; }
wait_health "http://127.0.0.1:1/health" ${q(opts.expected ?? '')} 1
__rc=$?
# '-<unset>', NOT ':-<unset>': an empty HEALTH_FAILURE is the SUCCESS value, and
# the ':' form substitutes for empty too — so a passing gate would read as
# "<unset>" and the pass/fail pair could not be told apart.
printf 'HEALTH_FAILURE=%s\\n' "\${HEALTH_FAILURE-<unset>}"
exit $__rc
`
    try {
      const res = spawnSync('bash', ['-c', script], { encoding: 'utf8', timeout: 60_000 })
      return {
        status: res.status,
        stdout: res.stdout,
        stderr: res.stderr,
        // A source that fails (a missing file, a bad key) leaves no marker at
        // all, which must be distinguishable from the empty SUCCESS value.
        failure: /HEALTH_FAILURE=(.*)/.exec(res.stdout)?.[1] ?? '<no marker>'
      }
    } finally {
      rmSync(dir, { recursive: true, force: true })
    }
  }

  for (const target of TARGETS.resolved) {
    declareExercised(TARGETS.resolved)
    test(`${target.label}: {"ok":true} passes the gate, with ONE version-disabled warning`, () => {
      const gate = runGate(target, HEALTHY_BODY)
      expect(gate.status, gate.stdout + gate.stderr).toBe(0)
      expect(gate.failure).toBe('')
      // The warning is the whole point of the empty HEALTH_VERSION_FIELD: a
      // missing contract reported by silence is a vacuous pass. Exactly ONE, so
      // an operator sees a stated caveat rather than a wall of noise.
      const warnings = gate.stderr.split('\n').filter(l => l.includes('version check is DISABLED'))
      expect(warnings).toHaveLength(1)
      expect(warnings[0]).toContain(target.label)
    })

    test(`${target.label}: {"ok":false} is rejected as a contract failure`, () => {
      const gate = runGate(target, UNHEALTHY_BODY)
      expect(gate.status).toBe(1)
      expect(gate.failure).toBe('contract')
    })

    test(`${target.label}: the framework DEFAULTS reject {"ok":true} — the keys are load-bearing`, () => {
      // Negative control. Under synaptomind's defaults (status / ok degraded /
      // version) the very same body is a CONTRACT failure: the app's three keys
      // are what make this cutover possible, not decoration.
      const gate = runGate(target, HEALTHY_BODY, {
        keys: {
          HEALTH_STATUS_FIELD: 'status',
          HEALTH_OK_VALUES: 'ok degraded',
          HEALTH_VERSION_FIELD: 'version'
        }
      })
      expect(gate.status).toBe(1)
      expect(gate.failure).toBe('contract')
    })

    test(`${target.label}: a wrong ok-value under the app's own keys still fails`, () => {
      // The other half of non-vacuity: not "any JSON passes", but "the declared
      // value passes and a different one does not".
      const gate = runGate(target, '{"ok":"yes"}')
      expect(gate.status).toBe(1)
      expect(gate.failure).toBe('contract')
    })

    test(`${target.label}: an unreadable contract is refused BEFORE the payload is fetched`, () => {
      // A health field name that is not a plain JSON key is interpolated into a
      // sed pattern; the gate refuses it rather than reading whatever that
      // pattern matches.
      // The bad value goes into the SANDBOXED app.env, not the process
      // environment: load_app_env() SOURCES app.env, so an env override would be
      // discarded by the very file that carries the app's real keys.
      const fx = seedTarget(target, { env: { HEALTH_STATUS_FIELD: 'a|b*c' } })
      const { stubsDir, log } = seedPrivStubs(ensureRoot())
      seedHealthStub(stubsDir, HEALTHY_BODY)
      const res = runInstall(fx, stubsDir, log, [])
      expect(res.status, res.stdout + res.stderr).not.toBe(0)
      expect(res.stderr + res.stdout).toContain('HEALTH_STATUS_FIELD')
      expect(res.stderr + res.stdout).toMatch(/not a JSON key/)
      // And it refused before writing anything: require_health_contract is
      // called from main() before the payload is fetched, which is the whole
      // point of doing it there — the host is still untouched when the refusal
      // lands.
      expect(existsSyncSafe(fx.installDir), 'INSTALL_DIR exists, so the payload was fetched').toBe(
        false
      )
    })
  }
})

// ── Gap (b): INSTALL_DIR / DATA_DIR ownership ───────────────────────────────
//
// Before apply_ownership(), `grep -n chown deploy/*.sh` returned one hit and it
// was a WARNING STRING — nothing in the framework applied ownership at all. The
// failure it prevents is invisible to the health gate: a root-owned DATA_DIR
// makes the service's first WRITE fail, long after the check passed.

describe('deploy/apply_ownership — both trees, before the service starts', () => {
  for (const target of TARGETS.resolved) {
    declareExercised(TARGETS.resolved)
    /**
     * A full install for the target's own mode: a real git origin for
     * DIST=source, a real release tarball for DIST=binary, `bun` stubbed only
     * where a build would otherwise install a node_modules.
     *
     * Everything below the payload (seed, data dir, ownership, unit, start) is
     * what these tests are about, so the fixture has to genuinely REACH it — a
     * `--no-service` run that stopped at the clone would report no chown and the
     * assertion would be measuring the fixture rather than the framework.
     */
    function fullInstall(
      opts: { execSudo?: boolean; refuseChown?: boolean; chownSnapshot?: boolean } = {}
    ) {
      const values = readAppEnvValues(target.appEnvPath)
      const binary = values.DIST === 'binary'
      const origin = binary ? null : seedGitOrigin(target.label)
      const release = binary ? seedReleaseDir(target.label) : null
      const env: Record<string, string> = binary
        ? { RELEASES_BASE: `file://${release!.dir}`, RELEASE_API: '' }
        : { REPO_URL: origin!.url, CHECKOUT_POLICY: 'stable' }
      const fx = seedTarget(target, { env })
      const { stubsDir, log } = seedPrivStubs(ensureRoot(), {
        execSudo: opts.execSudo,
        chownSnapshot: opts.chownSnapshot
      })
      // A root-owned-looking tree, so apply_ownership takes the branch it added
      // instead of correctly skipping a no-op chown.
      seedForeignOwnerStat(stubsDir)
      if (opts.refuseChown) refuseChownInSudoStub(stubsDir)
      if (!binary) seedBunStub(stubsDir, join(ensureRoot(), 'bun.log'))
      // For a binary install the SAME curl stub must serve the asset too, or the
      // run dies in tar before reaching anything these tests are about.
      seedHealthStub(stubsDir, HEALTHY_BODY, {
        asset: release
          ? join(release.dir, release.tag, `${target.label}-${release.tag}-linux-x86_64.tar.gz`)
          : undefined
      })
      const args = binary ? ['--version', release!.tag] : ['--version', origin!.tag]
      const res = runInstall(fx, stubsDir, log, args)
      return { res, log, stubsDir, origin, release, binary, fx }
    }

    test(`${target.label}: chowns INSTALL_DIR and DATA_DIR to the unit's user`, () => {
      const { res, log } = fullInstall({ execSudo: true })
      expect(res.status, res.stdout + res.stderr).toBe(0)

      const calls = privCalls(log)
      const chowns = calls.filter(c => c.includes('chown') && c.includes('-R'))
      expect(chowns.length, `no recursive chown in:\n${calls.join('\n')}`).toBeGreaterThanOrEqual(2)
      // Both trees, and named by path — the whole point of the gap, since
      // DATA_DIR is where the service writes.
      for (const dir of [join(ROOT, 'opt'), join(ROOT, 'data')]) {
        expect(
          chowns.some(c => c.includes(` ${dir}`)),
          `no chown for ${dir} in:\n${calls.join('\n')}`
        ).toBe(true)
      }
      // To the user the unit runs as, not root and not an empty field.
      const me = process.env.USER ?? 'root'
      for (const call of chowns) expect(call).toContain(`${me}:`)
    })

    test(`${target.label}: ownership is applied BEFORE the unit is started`, () => {
      const { res, log } = fullInstall({ execSudo: true })
      expect(res.status, res.stdout + res.stderr).toBe(0)
      const calls = privCalls(log)
      const lastChown = calls.findLastIndex(c => c.includes('chown') && c.includes('-R'))
      const firstStart = calls.findIndex(c => c.includes('systemctl') && c.includes(' restart '))
      expect(lastChown, 'no chown was issued at all').toBeGreaterThanOrEqual(0)
      expect(
        firstStart,
        `no systemctl restart in:\n${calls.join('\n')}\n--- install output ---\n${res.stdout}\n${res.stderr}`
      ).toBeGreaterThan(lastChown)
    })

    test(`${target.label}: a chown that FAILS warns and the install still finishes`, () => {
      // NOT FATAL by decision (ADR §3c): a --dir install under the caller's own
      // home has nothing to chown, and aborting there would turn an ownership
      // cosmetic into a failed install. The refusal is injected at the stub, so
      // everything else about the run is the ordering that ships.
      const { res } = fullInstall({ refuseChown: true })
      expect(res.status, res.stdout + res.stderr).toBe(0)
      expect(res.stdout).toContain('Done.')
      // The mismatch is named, with the consequence and a fix — not swallowed.
      expect(res.stderr).toContain('cannot chown')
      expect(res.stderr).toMatch(/EACCES/)
      expect(res.stderr).toMatch(/fix: sudo chown/)
    })

    test(`${target.label}: chowns RUN_DIR, scripts/ and hooks/ AFTER the helper installers wrote them`, () => {
      // The RUN_DIR half of the gap (#1113): the pre-update hook runs as
      // TARGET_USER and reads ${RUN_DIR}/scripts/app.env, which is installed
      // mode 600 — so a root-owned one is unreadable to exactly the process that
      // takes the pre-update database backup, and the backup fails silently.
      //
      // The ORDERING is the part that cannot be asserted from a call log: a chown
      // of a directory that does not exist yet and a chown of an empty one look
      // identical there. So the sudo stub records what was in each chowned
      // directory at the moment it was chowned (chownSnapshot), and the helpers
      // must already be in place — otherwise apply_run_dir_ownership is
      // chowning an empty tree and the fix is decoration.
      const { res, log, fx } = fullInstall({ execSudo: true, chownSnapshot: true })
      expect(res.status, res.stdout + res.stderr).toBe(0)
      const calls = privCalls(log)
      const chowns = calls.filter(c => c.includes('chown') && c.includes('-R'))
      for (const dir of [fx.runDir, join(fx.runDir, 'scripts'), join(fx.runDir, 'hooks')]) {
        expect(
          chowns.some(c => c.includes(` ${dir}`)),
          `no chown for ${dir} in:\n${calls.join('\n')}`
        ).toBe(true)
      }
      // Scoped: RUN_DIR may also hold app state the framework does not own, so a
      // blind `chown -R ${RUN_DIR}` would silently take that too.
      const owned = chowns.map(c => c.trim().split(/\s+/).pop())
      expect(owned.filter(p => p?.startsWith(fx.runDir)).sort()).toEqual(
        [fx.runDir, join(fx.runDir, 'hooks'), join(fx.runDir, 'scripts')].sort()
      )
      // And the files were already there when the chown ran.
      const snapshot = (dir: string) => calls.find(c => c.startsWith(`LS ${dir}:`))
      const scripts = snapshot(join(fx.runDir, 'scripts'))
      expect(
        scripts,
        `no snapshot of ${join(fx.runDir, 'scripts')} in:\n${calls.join('\n')}`
      ).toBeDefined()
      for (const helper of ['app.env', 'common.sh', 'update.sh', 'updater.sh', 'uninstall.sh']) {
        expect(scripts, `${helper} was not installed yet when scripts/ was chowned`).toContain(
          helper
        )
      }
      const hooks = snapshot(join(fx.runDir, 'hooks'))
      expect(
        hooks,
        `no snapshot of ${join(fx.runDir, 'hooks')} in:\n${calls.join('\n')}`
      ).toContain('pre-update')
      // The mode-600 app.env is the whole reason this pass exists.
      expect(statSync(join(fx.runDir, 'scripts', 'app.env')).mode & 0o777).toBe(0o600)
    })
  }
})

/**
 * A `file://` release directory holding one asset, for a DIST=binary target.
 *
 * The payload carries the app's OWN artefact name, so a pass here means the
 * per-app BINARY_* list is honoured rather than that the framework happened to
 * find synaptomind's names. `root` is explicit (not the shared fixture root)
 * because the BINARY_* staging tests need an isolated release tree of their own.
 */
function seedReleaseDirIn(root: string, label: string): { dir: string; tag: string } {
  const tag = 'v9.9.9-test'
  const version = '9.9.9-test'
  const asset = `${label}-${tag}-linux-x86_64.tar.gz`
  const topDir = `${label}-${version}-linux-x86_64`
  const stage = join(root, '.payload', topDir)
  mkdirSync(stage, { recursive: true })
  writeFileSync(join(stage, label), `#!/bin/sh\necho "${label} ${version}"\n`)
  chmodSync(join(stage, label), 0o755)
  const dir = join(root, 'releases')
  mkdirSync(join(dir, tag), { recursive: true })
  const tar = spawnSync(
    'tar',
    ['-czf', join(dir, tag, asset), '-C', join(root, '.payload'), topDir],
    {
      encoding: 'utf8'
    }
  )
  expect(tar.status, tar.stderr).toBe(0)
  return { dir, tag }
}

/** seedReleaseDirIn against the shared fixture root. */
function seedReleaseDir(label: string): { dir: string; tag: string } {
  return seedReleaseDirIn(ensureRoot(), label)
}

// ── Gap (c): the unit extras ────────────────────────────────────────────────
//
// StateDirectory= / EnvironmentFile= / Environment= are rendered ONLY when the
// key is non-empty, and every substituted value goes through assert_unit_value.
// The guard is the interesting half: a value systemd's parser would fold is
// DROPPED IN SILENCE, so the app runs without the variable and nothing says so.
//
// The unit extras are also the one place where the guard runs on a SPLIT value:
// UNIT_EXTRA_ENV is a space-separated blob of "K=V" pairs, and since #1117
// render_systemd_unit splits it on a space BY HAND rather than through an
// unquoted `for` (which split on IFS first, so no pair could ever contain a line
// break and the newline arm of unit_value_defect was unreachable). The split is
// the part under test below, because "the value is refused" and "the value was
// shredded into several valid ones" are the same green result unless the message
// and the pair count are both asserted.

describe('deploy/unit extras — rendering and rejection', () => {
  /**
   * render_systemd_unit against a sandboxed app.env, returning the body.
   *
   * `cwd` is the FIXTURE's directory, never the repo root. Nothing here depends on
   * it until the pathname-expansion case, and that case is precisely a claim about
   * which directory the render reads — a render run in the repo root would prove
   * something about the repo, not about the value.
   *
   * `rawAppEnvLines` bypasses sandboxAppEnvText for a named key, and exists for
   * ONE reason: that helper's quote() refuses `"`, `$` and a backtick in an
   * override, because they change meaning inside the double quotes every app.env
   * line uses. A test whose subject IS such a value therefore has to write the
   * line itself — in the form an operator writes it, escaped. It is refused for
   * the path keys, which keep the containment check; a non-path key cannot name a
   * file to write.
   */
  function renderUnit(
    target: { label: string; appEnvPath: string },
    keys: Record<string, string> = {},
    opts: { rawAppEnvLines?: Record<string, string>; files?: readonly string[] } = {}
  ) {
    const dir = mkTempTree('unit-')
    // A FILE, sourced — never the text inlined into the script. See runGate.
    const appEnvFile = join(dir, 'app.env')
    let appEnvText = sandboxAppEnvText(target.appEnvPath, {
      INSTALL_DIR: join(dir, 'opt'),
      DATA_DIR: join(dir, 'data'),
      RUN_DIR: join(dir, 'run'),
      ...keys
    })
    for (const [key, line] of Object.entries(opts.rawAppEnvLines ?? {})) {
      if ((SANDBOX_PATH_KEYS as readonly string[]).includes(key)) {
        throw new Error(`rawAppEnvLines may not carry the sandboxed path key ${key}`)
      }
      appEnvText = replaceAssignment(appEnvText, key, line)
    }
    writeFileSync(appEnvFile, appEnvText)
    for (const name of opts.files ?? []) writeFileSync(join(dir, name), 'x\n')
    const script = `
. ${q(LIB)}
. ${q(appEnvFile)}
APP_NAME=${q(target.label)}
TARGET_USER=$(id -un)
TARGET_GROUP=$TARGET_USER
TARGET_HOME=$HOME
BUN_BIN=""
render_systemd_unit ${q(keys.EXEC_START ?? '/opt/app/bin')}
__rc=$?
printf 'RENDER_RC=%s\\n' "$__rc"
exit 0
`
    try {
      const res = spawnSync('bash', ['-c', script], { encoding: 'utf8', timeout: 30_000, cwd: dir })
      const rc = /RENDER_RC=(\d+)/.exec(res.stdout)?.[1] ?? '?'
      const body = res.stdout.slice(0, res.stdout.indexOf('RENDER_RC='))
      return { rc, body, stderr: res.stderr }
    } finally {
      rmSync(dir, { recursive: true, force: true })
    }
  }

  for (const target of TARGETS.resolved) {
    declareExercised(TARGETS.resolved)
    const values = readAppEnvValues(target.appEnvPath)

    test(`${target.label}: renders exactly the extras its app.env asks for`, () => {
      const { rc, body } = renderUnit(target)
      expect(rc, 'render_systemd_unit refused its own app.env').toBe('0')

      // Present iff asked for. The paired absence is the half that matters: a
      // template that always emitted these would have changed every existing
      // app's unit, and an assertion on presence alone would not notice.
      //
      // The values come from the app.env itself, and a key the app does not
      // declare reads as '' — the framework's documented "rendered only when
      // non-empty" default, not an absent lookup. ziptask declares none of the
      // three, subagentix declares all three, and both directions are asserted.
      const stateDir = values.UNIT_STATE_DIRECTORY ?? ''
      const envFile = values.UNIT_ENV_FILE ?? ''
      const extraEnv = values.UNIT_EXTRA_ENV ?? ''

      if (stateDir) {
        expect(body).toContain(`StateDirectory=${stateDir}`)
        // 0700, not the systemd default 0755: the app resolves its state through
        // this variable and writes a settings file into it.
        expect(body).toContain('StateDirectoryMode=0700')
      } else {
        expect(body).not.toContain('StateDirectory=')
      }

      if (envFile) expect(body).toContain(`EnvironmentFile=${envFile}`)
      else expect(body).not.toContain('EnvironmentFile=')

      const pairs = extraEnv.split(/\s+/).filter(Boolean)
      for (const pair of pairs) {
        expect(body, `UNIT_EXTRA_ENV pair ${pair} did not reach the unit`).toContain(
          `Environment=${pair}`
        )
      }
      if (pairs.length === 0) {
        // The framework's OWN lines and nothing else. Asserting the exact set is
        // what proves the app's missing keys add NOTHING — an empty Environment=
        // line would pass a "value not present" check.
        //
        // LD_LIBRARY_PATH is the framework's own line too, and DIST decides it:
        // it is emitted for DIST=binary only (ADR §2.2, so a compiled payload can
        // dlopen its embedded addon). Keying the expectation on DIST rather than
        // hardcoding it is what keeps this correct for an app of either mode.
        const own = [
          'NODE_ENV',
          'HOME',
          'PATH',
          ...(values.DIST === 'binary' ? ['LD_LIBRARY_PATH'] : [])
        ]
        const envLines = body.split('\n').filter(l => l.startsWith('Environment='))
        const ownLines = envLines.filter(l => own.some(k => l.startsWith(`Environment=${k}=`)))
        expect(envLines, `unexpected Environment= lines:\n${envLines.join('\n')}`).toEqual(ownLines)
        expect(ownLines).toContain(`Environment=HOME=${process.env.HOME ?? ''}`)
        // And the mode actually decided it, rather than the list being right by
        // coincidence: ziptask (binary) has the line, subagentix (source) must not.
        if (values.DIST === 'binary') expect(body).toContain('Environment=LD_LIBRARY_PATH=')
        else expect(body).not.toContain('Environment=LD_LIBRARY_PATH=')
      }
    })

    test(`${target.label}: the bounded stop is rendered, and the unit carries no backtick`, () => {
      // A backtick inside the rendered body would be executed by the UNQUOTED
      // heredoc this template used to be. systemd-unit.test.ts asserts the
      // absence with execution stubs; this asserts the same property from the
      // app.env side, so a new app cannot reintroduce it through its own keys.
      const { rc, body } = renderUnit(target)
      expect(rc).toBe('0')
      // TimeoutStopSec is the BOUND; the SIGKILL that enforces it is systemd's
      // own default (SendSIGKILL=yes), which arrives only once the bound expires.
      // KillSignal must therefore NOT be set: it used to be SIGKILL, which
      // replaced the app's SIGTERM handler (src/index.ts:123 — the WAL
      // checkpoint) with an immediate unblockable kill, so every stop ended in
      // status=9 and the shutdown path never ran (#1124/#1125). Asserting the
      // ABSENCE is the contract now: a future edit that re-adds the line fails
      // here instead of re-breaking every graceful stop on the host.
      expect(body).toContain('TimeoutStopSec=15')
      expect(body).not.toContain('KillSignal=')
      expect(body).toContain('Restart=always')
      expect(body).not.toContain('`')
      expect(body).not.toContain('$(systemctl')
    })

    test(`${target.label}: Group= names the group the tree was chowned to`, () => {
      // The install chowns to TARGET_USER:TARGET_GROUP and the unit then runs as
      // that same pair; before this, the unit left the group to systemd's lookup.
      const { body } = renderUnit(target)
      expect(body).toContain(`Group=${process.env.USER ?? 'root'}`)
    })

    // ── assert_unit_value ──
    // A newline is the sharpest case: systemd reads the text after the first `=`
    // as a fresh directive, so `UNIT_STATE_DIRECTORY=a\nExecStartPre=/bin/rm`
    // would forge a line. For the two path-shaped keys the value is substituted
    // whole, so the guard sees the line break and refuses.
    for (const key of ['UNIT_STATE_DIRECTORY', 'UNIT_ENV_FILE'] as const) {
      test(`${target.label}: ${key} with a value systemd would fold is REFUSED, and no unit is printed`, () => {
        const { rc, body, stderr } = renderUnit(target, {
          [key]: 'app\nExecStartPre=/bin/rm -rf /'
        })
        expect(rc, `render accepted a newline-bearing ${key}`).not.toBe('0')
        // The contract is that it returns 1 WITHOUT printing a partial body: a
        // half-written unit is what the caller would install.
        expect(body).not.toContain('[Service]')
        expect(body).not.toContain('ExecStartPre')
        // And it says which key and what is wrong with it.
        expect(stderr).toContain(key)
        expect(stderr).toMatch(/line break/)
      })
    }

    test(`${target.label}: UNIT_EXTRA_ENV is guarded PER PAIR, so one bad pair refuses the render and names itself`, () => {
      // The guard is deliberately per pair: a whole-blob guard would always fire
      // (a blob is whitespace by construction) and an operator would learn to
      // ignore it. So a blob with a valid pair and a hostile one must REFUSE —
      // with the refusal naming the offending pair rather than dropping it.
      for (const hostile of ['BAD%1', 'BAD;1', 'BAD\\x', 'BAD#1']) {
        const { rc, body, stderr } = renderUnit(target, { UNIT_EXTRA_ENV: `GOOD=1 ${hostile}` })
        expect(rc, `render accepted UNIT_EXTRA_ENV=${hostile}`).not.toBe('0')
        expect(body).not.toContain('[Service]')
        expect(stderr, `no warning for ${hostile}`).toContain('UNIT_EXTRA_ENV entry')
        // The valid pair is NOT rendered on its own: a partial unit is worse
        // than none, because the caller installs what it got.
        expect(body).not.toContain('Environment=GOOD=1')
      }
    })

    test(`${target.label}: a newline in UNIT_EXTRA_ENV REFUSES the render, names the entry and prints no unit`, () => {
      // #1112 pinned this case as safe-but-unrefused, on purpose: the loop was
      // `for pair in ${UNIT_EXTRA_ENV:-}`, so IFS ate the line break BEFORE the
      // guard ran, no pair ever contained one, and the blob rendered as four
      // inert `Environment=` lines. That was SAFE (an Environment= value is an
      // assignment, never a directive) and UNREFUSED, and the second half is what
      // #1117 fixed by splitting on a space by hand.
      //
      // So the security property this test originally asserted is now the STRONGEST
      // form available: not "nothing escaped the directive" but "nothing was
      // printed at all", because the refusal happens before the body is emitted.
      const { rc, body, stderr } = renderUnit(target, {
        UNIT_EXTRA_ENV: 'GOOD=1\nExecStartPre=/bin/rm -rf /'
      })
      expect(rc, 'render accepted a multi-line UNIT_EXTRA_ENV').not.toBe('0')
      expect(body, 'a partial unit was printed before the refusal').not.toContain('[Service]')
      expect(body, 'a forged ExecStartPre= line appeared in the unit').not.toMatch(
        /^ExecStartPre=/m
      )
      // The valid pair is NOT rendered either, and that is the property the old
      // behaviour got wrong: it printed what it could and dropped the rest, so the
      // caller installed a unit missing the variable an operator believed was
      // there. No Environment= line at all is the only honest outcome here.
      expect(body, 'a pair was rendered from a refused blob').not.toContain('Environment=')
      expect(stderr).toContain('UNIT_EXTRA_ENV entry')
      expect(stderr).toMatch(/line break/)
    })

    test(`${target.label}: a TAB between two pairs is refused as a CONTROL CHARACTER, not a line break`, () => {
      // The direct counterpart of the case above, and it pins the split itself.
      // A TAB used to be a SEPARATOR too, so `GOOD=1<TAB>BAD=2` became two
      // perfectly valid pairs and the render was accepted rc=0. The split is on a
      // space now, so the TAB stays INSIDE the pair and the guard names the
      // defect it actually is. Asserting the message is what separates the two
      // classes: a split that reached the line-break arm some other way would
      // fail here instead of passing as a near miss.
      const { rc, body, stderr } = renderUnit(target, { UNIT_EXTRA_ENV: 'GOOD=1\tBAD=2' })
      expect(rc, 'render accepted a TAB-separated UNIT_EXTRA_ENV').not.toBe('0')
      expect(body).not.toContain('[Service]')
      expect(stderr).toContain('UNIT_EXTRA_ENV entry')
      expect(stderr).toMatch(/control character/)
      expect(stderr, 'a TAB is not a line break; the wrong defect was named').not.toMatch(
        /line break/
      )
    })

    test(`${target.label}: a blob of several VALID pairs still renders, one Environment= line each`, () => {
      // What keeps every refusal above honest: a guard that refused all blobs
      // would pass all of them. Runs of spaces and leading/trailing ones are here
      // too — a space separates, a run of them adds no empty pair, so no bare
      // `Environment=` line is ever emitted.
      for (const blob of ['A=1 B=2 C=3', 'A=1  B=2', ' A=1 B=2 ']) {
        const { rc, body, stderr } = renderUnit(target, { UNIT_EXTRA_ENV: blob })
        expect(rc, `${JSON.stringify(blob)} was refused: ${stderr.trim()}`).toBe('0')
        for (const pair of blob.split(' ').filter(Boolean)) {
          expect(body, `${JSON.stringify(blob)} lost ${pair}`).toContain(`Environment=${pair}`)
        }
        expect(
          body.split('\n').filter(l => l === 'Environment='),
          `${JSON.stringify(blob)} rendered an empty pair`
        ).toEqual([])
      }
    })

    test(`${target.label}: FOO=* does not pathname-expand against the render's working directory`, () => {
      // The old unquoted expansion globbed as well as split, so
      // UNIT_EXTRA_ENV="FOO=*" rendered as whichever files in the CWD matched —
      // the same app.env producing a different unit depending on the directory
      // install.sh happened to run in. Parameter expansion does not glob.
      //
      // `files` is what makes this non-vacuous, and the seeded name has to match
      // the WHOLE pattern: the glob is `FOO=*`, not `*`, so only a file whose name
      // starts with `FOO=` can ever be picked up. In a directory holding exactly
      // that, the old code renders `Environment=FOO=leaked.txt` and the last
      // assertion fails; in a directory holding unrelated files it would leave
      // `FOO=*` unexpanded and this test would pass for the wrong reason. A glob
      // is not a defect, so the render must still SUCCEED — first assertion.
      const { rc, body } = renderUnit(
        target,
        { UNIT_EXTRA_ENV: 'FOO=*' },
        { files: ['FOO=leaked.txt'] }
      )
      expect(rc, 'a glob is not a defect and must not be refused').toBe('0')
      expect(body).toContain('Environment=FOO=*')
      expect(body, 'the glob was expanded against the working directory').not.toContain('leaked')
    })

    // ── quotes: finding #3 ──
    // Filed by #1112 as "unit_value_defect does not refuse a double quote in a
    // UNIT_EXTRA_ENV pair (FOO=ba\"r renders rc=0) although it refuses a single
    // quote". It did not reproduce, and the reason is a property of the PROBE, not
    // of the guard, so both halves are pinned here.

    test(`${target.label}: unit_value_defect refuses BOTH quotes, measured as an ARGVUMENT`, () => {
      // common.sh matches *'"'*|*"'"* in one arm and has done so since the guard
      // was written. The rc=0 in the finding came from writing the value raw into
      // a script: `UNIT_EXTRA_ENV=FOO=ba"r` is a bash syntax error, so the
      // sourcing shell aborted on that line, the key kept its default empty, and
      // the render succeeded on nothing. The single quote survived that probe only
      // because it happened to be the probe's own delimiter.
      //
      // The value goes in as $1 (see unitValueDefect), so nothing can go wrong
      // between this test and the guard except the guard.
      for (const value of ['FOO=ba"r', "FOO=ba'r", '"', "'", 'A=1 FOO=b"q\'s']) {
        expect(unitValueDefect(value), `${JSON.stringify(value)} was not refused`).toMatch(/quote/)
      }
      // Non-vacuity: a pair with no refused class is silent, so the loop above is
      // not passing because the function always prints something.
      for (const value of ['A=1', 'BUN_RUNTIME_TRANSPILER_CACHE_PATH=0', 'PATHLIKE=/usr/bin']) {
        expect(unitValueDefect(value), `${JSON.stringify(value)} should be clean`).toBe('')
      }
    })

    test(`${target.label}: a quote in UNIT_EXTRA_ENV is refused end to end, from the line an operator actually writes`, () => {
      // The direct call above proves the guard; this proves the whole path, from
      // the app.env text through sourcing to the render. The value is written the
      // way a `"` survives a double-quoted app.env line at all — backslash-escaped
      // — which is why this is the one place in the file that writes an app.env
      // line itself instead of going through sandboxAppEnvText.
      const { rc, body, stderr } = renderUnit(
        target,
        {},
        { rawAppEnvLines: { UNIT_EXTRA_ENV: 'UNIT_EXTRA_ENV="GOOD=1 FOO=ba\\"r"' } }
      )
      expect(rc, 'render accepted a quoted UNIT_EXTRA_ENV pair').not.toBe('0')
      expect(body, 'a partial unit was printed before the refusal').not.toContain('[Service]')
      expect(body).not.toContain('Environment=GOOD=1')
      expect(stderr).toContain('UNIT_EXTRA_ENV entry')
      expect(stderr).toMatch(/quote/)
    })
  }
})

// ── Gap (d): INSTALL_FLAGS / BUILD_CMD order, and the build timeout ─────────
//
// The pair lives in lib/common.sh so install.sh and update.sh run the identical
// two functions. Order is load-bearing: a build that runs before `bun install`
// has no devDependencies, and the failure names nothing that points at the cause.

describe('deploy/build step — flags, order, and a bounded build', () => {
  for (const target of TARGETS.resolved.filter(
    t => readAppEnvValues(t.appEnvPath).DIST === 'source'
  )) {
    declareExercised(TARGETS.resolved.filter(t => readAppEnvValues(t.appEnvPath).DIST === 'source'))
    const values = readAppEnvValues(target.appEnvPath)
    /**
     * A real source install against the app's own BUILD_CMD, with `bun` stubbed
     * so the trace records the argv without installing a real node_modules.
     *
     * The stub is here for the BUILD, not for the checkout: `git clone` and
     * `git checkout` run for real against a local origin, because the flags and
     * the build sit behind the checkout and a fixture that skipped it would
     * prove nothing about a real install.
     */
    function sourceInstall(opts: { env?: Record<string, string>; buildExit?: number } = {}) {
      const origin = seedGitOrigin(target.label)
      const fx = seedTarget(target, {
        env: { REPO_URL: origin.url, CHECKOUT_POLICY: 'stable', ...opts.env }
      })
      const { stubsDir, log } = seedPrivStubs(ensureRoot())
      const trace = join(ROOT, 'bun.log')
      seedBunStub(stubsDir, trace, { buildExit: opts.buildExit })
      seedHealthStub(stubsDir, HEALTHY_BODY)
      const res = runInstall(fx, stubsDir, log, ['--version', origin.tag])
      return { res, trace, stubsDir, log, origin }
    }

    test(`${target.label}: installs dependencies with INSTALL_FLAGS, then runs BUILD_CMD`, () => {
      const { res, trace } = sourceInstall()
      expect(res.status, res.stdout + res.stderr).toBe(0)
      const calls = readFileSync(trace, 'utf8').split('\n').filter(Boolean)
      expect(calls.length, 'bun was never invoked').toBeGreaterThanOrEqual(2)
      // The install comes first, carrying the app's OWN flags verbatim.
      expect(calls[0]).toContain('install')
      for (const flag of values.INSTALL_FLAGS.split(/\s+/).filter(Boolean)) {
        expect(calls[0], `INSTALL_FLAGS value ${flag} did not reach bun install`).toContain(flag)
      }
      // Then the build, as the app's own command, and only after.
      const buildIdx = calls.findIndex(c => c.startsWith('build '))
      expect(buildIdx, `BUILD_CMD never ran; calls:\n${calls.join('\n')}`).toBeGreaterThan(0)
      // The stub records bun's ARGV, so the recorded line is the app's command
      // without the leading `bun`. Comparing against `bun ${argv}` compares the
      // whole thing the framework was asked to run — which is what BUILD_CMD is.
      expect(`bun ${calls[buildIdx].replace(/^build /, '')}`).toBe(values.BUILD_CMD)
    })

    test(`${target.label}: the checkout is real, so the pair above ran inside it`, () => {
      // Non-vacuity for the test above: the install only reached bun install
      // because a genuine clone and tag checkout happened first. Without this
      // the flags assertion would also hold if source_prepare were broken.
      const { res, trace } = sourceInstall()
      expect(res.status, res.stdout + res.stderr).toBe(0)
      expect(res.stdout).toContain('Cloning')
      const fxInstallDir = join(ROOT, 'opt')
      expect(existsSyncSafe(fxInstallDir)).toBe(true)
      expect(existsSyncSafe(join(fxInstallDir, '.git'))).toBe(true)
      expect(readFileSync(trace, 'utf8')).toContain('install')
    })

    test(`${target.label}: a failing build aborts the install and nothing is installed`, () => {
      const { res, log } = sourceInstall({ buildExit: 1 })
      expect(res.status).not.toBe(0)
      expect(res.stdout).not.toContain('Done.')
      // It names the command and says nothing was installed, because a failed
      // build IS a missing artefact — the worst case to report as success.
      expect(res.stderr + res.stdout).toContain('build failed')
      expect(res.stderr + res.stdout).toMatch(/Nothing was installed/)
      // And no service was started on top of an unbuilt tree.
      expect(privCalls(log).some(c => c.includes('systemctl') && c.includes('restart'))).toBe(false)
    })

    test(`${target.label}: a build that HANGS is killed at BUILD_TIMEOUT instead of hanging the install`, () => {
      // BUILD_CMD replaced by a sleep, because the point is the TIMEOUT and not
      // the app's own build; everything else (INSTALL_FLAGS, the clone, the
      // checkout, the user) is the app's real configuration.
      const started = Date.now()
      const { res } = sourceInstall({ env: { BUILD_CMD: 'sleep 120', BUILD_TIMEOUT: '2' } })
      const elapsed = Date.now() - started
      expect(res.status, res.stdout + res.stderr).not.toBe(0)
      expect(res.stdout).not.toContain('Done.')
      // The bound is the assertion: 120s of sleep, 2s of budget. Without the
      // timeout this run would sit until the suite's own spawn timeout and fail
      // for a completely different reason.
      expect(elapsed, `install ran for ${elapsed}ms, so the build was not bounded`).toBeLessThan(
        30_000
      )
      expect(res.stderr + res.stdout).toMatch(/build failed|timed out|124/)
    })
  }
})

// ── Gap (e): per-app binary file lists ──────────────────────────────────────
//
// #1110's measured finding: without the BINARY_* overrides ziptask cannot
// install AT ALL, because the payload check looks for synaptomind's artefacts.

describe('deploy/BINARY_* — per-app payload shape', () => {
  for (const target of TARGETS.resolved.filter(
    t => readAppEnvValues(t.appEnvPath).DIST === 'binary'
  )) {
    declareExercised(TARGETS.resolved.filter(t => readAppEnvValues(t.appEnvPath).DIST === 'binary'))
    const values = readAppEnvValues(target.appEnvPath)

    /**
     * Run the REAL binary_stage_payload against a fixture release directory,
     * with `curl` stubbed to copy the local asset wherever it is asked for.
     *
     * The required-file check is three lines INSIDE that function
     * (common.sh:1500), not a standalone helper — so there is nothing to call
     * directly. An earlier draft sliced the loop out of the source with `sed` and
     * `eval`'d it; that is a copy of the check that can drift from the check, and
     * it read `$entry` (a `local` of the enclosing function) so it failed for a
     * reason that had nothing to do with the app. Calling the real function, with
     * only the DOWNLOAD stubbed, tests the shipped bytes.
     *
     * `appEnvOverride` is where the BINARY_* keys are swapped, which is how the
     * negative control below turns the framework defaults back on.
     */
    function stagePayload(appEnvOverride: Record<string, string> = {}): {
      ok: boolean
      output: string
    } {
      const dir = mkTempTree('payload-')
      try {
        const release = seedReleaseDirIn(dir, target.label)
        const asset = join(
          release.dir,
          release.tag,
          `${target.label}-${release.tag}-linux-x86_64.tar.gz`
        )
        const appEnvFile = join(dir, 'app.env')
        writeFileSync(
          appEnvFile,
          sandboxAppEnvText(target.appEnvPath, {
            INSTALL_DIR: join(dir, 'opt'),
            DATA_DIR: join(dir, 'data'),
            RUN_DIR: join(dir, 'run'),
            RELEASES_BASE: `file://${release.dir}`,
            RELEASE_API: '',
            ...appEnvOverride
          })
        )
        const stubs = join(dir, 'stubs')
        mkdirSync(stubs, { recursive: true })
        writeFileSync(
          join(stubs, 'curl'),
          [
            '#!/usr/bin/env bash',
            'out=""; prev=""',
            'for a in "$@"; do',
            '  if [ "$prev" = "-o" ]; then out="$a"; fi',
            '  prev="$a"',
            'done',
            `cp ${q(asset)} "$out"`,
            'exit $?'
          ].join('\n')
        )
        chmodSync(join(stubs, 'curl'), 0o755)
        const script = `
. ${q(LIB)}
. ${q(appEnvFile)}
detect_os; detect_arch
TAG=v9.9.9-test
binary_stage_payload
printf 'STAGED=%s\\n' "\${STAGED_PAYLOAD:-}"
exit 0
`
        const res = spawnSync('bash', ['-c', script], {
          encoding: 'utf8',
          env: { ...process.env, PATH: `${stubs}:${process.env.PATH}` },
          timeout: 60_000
        })
        return { ok: /STAGED=\S/.test(res.stdout), output: `${res.stdout}${res.stderr}` }
      } finally {
        rmSync(dir, { recursive: true, force: true })
      }
    }

    test(`${target.label}: the app's own artefact satisfies BINARY_REQUIRED_FILES`, () => {
      // Driven through the real staging function rather than by reading the list:
      // a list the check ignores would pass this assertion and fail the install.
      const res = stagePayload()
      expect(res.ok, res.output).toBe(true)
      // The staged directory is the payload's own top-level dir, which is what
      // the swap and the version check then operate on.
      // The staged directory is the payload's own top-level dir — the shape the
      // published asset has, and what binary_swap_payload() then moves.
      expect(res.output).toContain(`STAGED=`)
      expect(res.output).toContain(`/${target.label}-9.9.9-test-linux-x86_64`)
    })

    test(`${target.label}: without the BINARY_* overrides the same payload is REFUSED (negative control)`, () => {
      // The framework DEFAULTS are synaptomind's own file names. With them in
      // force, ziptask's payload satisfies none of them — #1110 measured exactly
      // this as `release payload is incomplete: missing synaptomind`, which is
      // why the four BINARY_* keys are load-bearing rather than optional.
      const res = stagePayload({
        BINARY_SUPPORTED_PLATFORMS: 'linux-x86_64',
        BINARY_REQUIRED_FILES: 'synaptomind vec0.so lib/libonnxruntime.so.1',
        BINARY_ROLLBACK_FILES: 'synaptomind vec0.so lib/libonnxruntime.so.1',
        BINARY_SWAP_ORDER: 'synaptomind vec0.so lib/libonnxruntime.so.1'
      })
      expect(res.ok, 'a payload with none of the framework default names passed the check').toBe(
        false
      )
      expect(res.output).toMatch(/release payload is incomplete: missing/)
      // It names the file it wanted, which is what makes the message actionable.
      expect(res.output).toContain('synaptomind')
    })

    test(`${target.label}: BINARY_SWAP_ORDER lists every rollback file`, () => {
      // The rollback set is what a no-git rollback moves back. A file in
      // ROLLBACK but not in SWAP_ORDER is never replaced, so the "keep a .prev"
      // promise is false for it.
      const swap = values.BINARY_SWAP_ORDER.split(/\s+/).filter(Boolean)
      for (const file of values.BINARY_ROLLBACK_FILES.split(/\s+/).filter(Boolean)) {
        expect(swap, `${file} is a rollback file but is not in BINARY_SWAP_ORDER`).toContain(file)
      }
    })

    test(`${target.label}: the platform is one the app publishes for`, () => {
      expect(values.BINARY_SUPPORTED_PLATFORMS.split(/\s+/)).toContain('linux-x86_64')
    })
  }
})

// ── Gap (f): the corrected update.sh comment ────────────────────────────────
//
// #1108 corrected a comment that claimed render_systemd_unit() has "exactly ONE
// call site" when it has two (install.sh's install_service and update.sh's
// refresh_unit). The count is the invariant: an audit reading "one call site"
// concludes the update path cannot change the unit body and stops looking. So
// the assertion is a COUNT against the code, not a grep for the word "two".

describe('deploy/update.sh — the two-call-site claim is true', () => {
  const UPDATE_SH = join(DEPLOY_DIR, 'update.sh')

  test('render_systemd_unit is called from exactly the two sites the comment names', () => {
    const callers: string[] = []
    for (const script of ['install.sh', 'update.sh', 'updater.sh', 'uninstall.sh']) {
      const lines = readFileSync(join(DEPLOY_DIR, script), 'utf8').split('\n')
      lines.forEach((line, i) => {
        // A real invocation is the command word itself: a comment mentioning it,
        // and the DEFINITION in lib/common.sh, are both excluded.
        if (line.trimStart().startsWith('#')) return
        if (!/(^|[;&|]|\bif[!\s]|\bthen\s)\s*!?\s*render_systemd_unit\b/.test(line)) return
        callers.push(`${script}:${i + 1}:${line.trim().slice(0, 50)}`)
      })
    }
    expect(callers, `call sites found: ${callers.join(' | ')}`).toHaveLength(2)
    expect(callers.some(c => c.startsWith('install.sh'))).toBe(true)
    expect(callers.some(c => c.startsWith('update.sh'))).toBe(true)
  })

  test('the comment above refresh_unit states TWO, and does not still claim ONE', () => {
    const src = readFileSync(UPDATE_SH, 'utf8')
    // The DEFINITION, not the first mention: the word appears in several
    // comments above it, and a slice anchored on one of those would read a
    // different paragraph than the one the claim lives in.
    const start = src.search(/^refresh_unit\(\) \{/m)
    expect(start, 'refresh_unit is gone from update.sh').toBeGreaterThan(-1)
    // The comment BLOCK directly above the definition: back to the section
    // header. Anchored on the box-drawing rule these files use for sections,
    // because splitting on "any comment line" cuts the block into single lines
    // and the claim then cannot be read at all.
    const before = src.slice(0, start)
    const sectionAt = before.lastIndexOf('# ── systemd unit')
    expect(sectionAt, 'the systemd-unit section header above refresh_unit is gone').toBeGreaterThan(
      -1
    )
    const header = before.slice(sectionAt)
    // The corrected wording, and the absence of the claim that was wrong. The
    // wrong count was harmful in one direction: an audit reading "one call site"
    // concludes the update path cannot change the unit body and stops looking.
    expect(header, 'the paragraph above refresh_unit does not state the count').toMatch(
      /TWO call sites/
    )
    expect(header, 'the paragraph above refresh_unit still claims ONE').not.toMatch(
      /exactly ONE call site/
    )
    // And it names the two sites, so the claim is falsifiable by a reader rather
    // than only by this test.
    expect(header).toContain('install_service()')
    expect(header).toContain('refresh_unit()')
  })
})

// ── Per-target helpers ──────────────────────────────────────────────────────

function existsSyncSafe(path: string): boolean {
  try {
    return statSync(path).isDirectory()
  } catch {
    return false
  }
}

/**
 * A real local git origin, with a tag and a package.json, so DIST=source does a
 * genuine `git clone` + `checkout` + `bun install` + build.
 *
 * Not a stubbed clone. The gaps under test (INSTALL_FLAGS order, BUILD_CMD, the
 * bounded build) all sit BEHIND the checkout, and a fixture that skipped the
 * clone would let a broken `source_prepare` pass these tests while breaking
 * every real install. `file://` is used rather than a bare path because
 * `git clone --depth=1` refuses a local path on some git versions, and the point
 * here is to be an ordinary origin.
 */
function seedGitOrigin(
  label: string,
  opts: { tag?: string; version?: string } = {}
): { url: string; tag: string; version: string } {
  const tag = opts.tag ?? 'v9.9.9-test'
  const version = opts.version ?? '9.9.9-test'
  const root = ensureRoot()
  const origin = join(root, 'origin.git')
  const work = join(root, 'origin-work')
  const git = (...args: string[]) => {
    const res = spawnSync('git', args, {
      encoding: 'utf8',
      cwd: work,
      env: {
        ...process.env,
        GIT_AUTHOR_NAME: 'fixture',
        GIT_AUTHOR_EMAIL: 'fixture@test',
        GIT_COMMITTER_NAME: 'fixture',
        GIT_COMMITTER_EMAIL: 'fixture@test'
      }
    })
    if (res.status !== 0) throw new Error(`git ${args.join(' ')} failed: ${res.stderr}`)
    return res
  }
  mkdirSync(work, { recursive: true })
  writeFileSync(
    join(work, 'package.json'),
    JSON.stringify({ name: label, version }, null, 2) + '\n'
  )
  writeFileSync(join(work, '.env.example'), `${label.toUpperCase()}_SETTING=\n`)
  git('init', '--quiet', '--initial-branch=main')
  git('add', '-A')
  git('commit', '--quiet', '-m', 'initial')
  git('tag', tag)
  git('clone', '--quiet', '--bare', work, origin)
  return { url: `file://${origin}`, tag, version }
}

/**
 * A bun stub that records its argv and can be told to fail or hang, so the
 * install/build pair can be asserted on without bun's real behaviour (a
 * 400 MB node_modules install) in the way.
 */
function seedBunStub(stubsDir: string, trace: string, opts: { buildExit?: number } = {}) {
  const path = join(stubsDir, 'bun')
  writeFileSync(
    path,
    [
      '#!/usr/bin/env bash',
      `printf 'bun %s\\n' "$*" >> ${q(trace)}`,
      'case "${1:-}" in',
      '  run)',
      `    printf 'build %s\\n' "$*" >> ${q(trace)}`,
      // Only the BUILD can be made to fail. A non-zero `bun install` would abort
      // before the pair under test was ever reached, which is a different
      // failure with the same message.
      ...(opts.buildExit ? [`    exit ${opts.buildExit}`] : []),
      '    ;;',
      'esac',
      'exit 0'
    ].join('\n')
  )
  chmodSync(path, 0o755)
}