Skip to content

feat(service): versioned native service status --json - #9

Open
nullStack65 wants to merge 3 commits into
mainfrom
env1/r8-t3-service-status-20260927
Open

nullStack65 wants to merge 3 commits into
mainfrom
env1/r8-t3-service-status-20260927

Conversation

@nullStack65

@nullStack65 nullStack65 commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

ENV-1:R10-T3-LIFECYCLE — Stage A: native service status slice (schema v2)

Closes the whole-field / safe-integer parser residual on this PR's status slice. Owner stage: ENV-1:R10-T3-LIFECYCLE · sole hub #237 · controlling handoff #9 5864056213.

Base fork main bcc1a58b19a9d610a4f08fed191a364767bc65b3 · head 22734455e1d2395a6acfe762ec15f412e4f28511 · branch env1/r8-t3-service-status-20260927.

Interface (schema v2)

t3 service status --json publishes schemaVersion: 2 with manager, supported, installed, enabled, running, current, installedVersion, installedBaseDir, configuredVersion, observation, unitPath, logPath, observedAt, problems.

  • Configured vs running identity are distinct. observation.configuredProgramPath / configuredVersion describe the configured launch program (normalized inside the selected base dir's runtime/versions tree). No field claims an observed running server; runningVersion was removed in v2.
  • running is running | stopped | transitioning | not-loaded | unknown. transitioning covers systemd activating/deactivating; running requires a live-process signal (systemd active/reloading + SubState=running + safe positive MainPID; positive launchd pid).
  • Unknown/query failure is not stopped or ready. Genuine not-found outcomes are distinguished from permission/timeout/unexpected failures; unsupported platforms report manager: unsupported with no observation.
  • Bounded read-only probes only (systemd --user show, launchctl print / print-disabled). No endpoint, no health inference.

Stage A change (this head)

Whole-field safe-integer parsing in the production parsers:

  • parseLaunchdPrint now reads the whole pid and last exit code fields and rejects a numeric prefix with trailing junk (pid = 12junk, last exit code = 7junk); PIDs must be positive safe integers and last-exit codes safe signed integers.
  • parseSystemdShow now requires MainPID to be a safe positive integer before it can imply a live process (an unrepresentable decimal no longer rounds into a running branch); NRestarts remains a non-negative safe integer.
  • Missing / malformed / unrepresentable values stay absent (unknown). No numeric-policy framework.

Failing-before on this new regression: 5 failed / 60 passed. Passing-after: 4 pre-existing Windows path-separator failures / 61 passed — the same 4 fail on the pristine head (4 failed / 60 passed), so no new failures. Reproduced on Windows 11 (26200), node v24.21.0, vite-plus 0.3.3.

Scope (source-only)

  • apps/server/src/cloud/bootService.ts / bootService.test.ts
  • apps/server/src/cli/service.ts / service.test.ts
  • docs/user/background-service.md
  • docs/internals/service-status.md

No edit to serviceLauncher/serviceProtocol, native/windows-service-host/**, server startup, provider/orchestration/usage/migration, Tailscale, desktop/WSL, package/release/workflow. No merge, no native service mutation.

Stage B (separate Windows-lifecycle integration PR) consumes this tested head together with accepted helper #8. This head is not independently reviewed or fleet-qualified.

Add schemaVersion/manager/enabled/running/observation to BootServiceStatus
and a --json mode to 't3 service status', reusing BootService and keeping
human output compatible. Manager observation is bounded and read-only:
systemd --user show and launchctl print/print-disabled, with distinct
missing-domain, not-loaded, permission, timeout and malformed outcomes.
Unknown stays unknown; a state file or current identity is never health.

WIP: focused tests pass; typecheck/lint and review hardening pending.
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XL labels Sep 27, 2026
@nullStack65
nullStack65 marked this pull request as ready for review September 27, 2026 07:28
@coderabbitai

coderabbitai Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Repository: nullStack65/t3code/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 63640ee8-2e41-4f28-8adc-400b3e58c639


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nullStack65

Copy link
Copy Markdown
Owner Author

RESULT — ENV-1:R8-T3-STATUS

Exact head c2399b7d48be886528546cce86fda8df6f84376f · base fork main bcc1a58b19a9d610a4f08fed191a364767bc65b3 · branch env1/r8-t3-service-status-20260927. Linked to sole hub #237 (START, checkpoint).

Recovered vs newly implemented source

  • Recovered: all six paths are the R6 candidate that R7 copied into /Users/businessaccount/w-t3/r7-t3-status (predecessor /Users/businessaccount/w-t3/r6-t3-status left untouched/dirty). This session reused that worktree, committed it, and published it.
  • Newly implemented this session: none in source logic. The R7 review points it flagged are already present in the recovered candidate: a distinct launchdPermissionDenied branch (domain and job), selected-user gui/<uid> binding with manager-user-unknown when uid is absent, and base-directory binding via bootServiceProgramInBaseDir. This session added no edits beyond the repo's vp fmt pre-commit formatting.

Source / tested refs

Changed paths

  • apps/server/src/cloud/bootService.ts (+449)
  • apps/server/src/cloud/bootService.test.ts (+402)
  • apps/server/src/cli/service.ts (+80)
  • apps/server/src/cli/service.test.ts (+57)
  • docs/user/background-service.md (+8)
  • docs/internals/service-status.md (new, 4909 B)

Output fields (t3 service status --json, BOOT_SERVICE_STATUS_SCHEMA_VERSION = 1)

schemaVersion, supported, manager (systemd|launchd|unsupported), installed, enabled (enabled|disabled|unknown), running (running|stopped|not-loaded|unknown), current, installedVersion?, installedBaseDir?, runningVersion?, observation?, problems?, unitPath, logPath, observedAt; JSON adds cliVersion.
observation: manager, source, observedAt, reachable, enabled, running, runningVersion?, restartCount? (systemd NRestarts only), lastResult?, detail?.
Kept separate: support · registration (installed) · enabled state · manager running state · installed/current/restart-pending identity · actually observed running identity. current: true never implies health; files/HTTP are not consulted. macOS gets no restart count (launchd throttle is not a finite budget).

Tests / counts / skips (exact head c2399b7d4)

  • vp test run apps/server/src/cloud/bootService.test.ts apps/server/src/cli/service.test.ts → 2 files, 74 passed, 0 failed, 0 skipped (bootService 52, service 22). Run twice on the exact head.
  • Boundaries covered: Linux current-but-stopped; linger-disabled with running state preserved; Mac plist-without-loaded-job; missing GUI login domain; launchctl permission refusal on domain and on job; manager command failure, timeout, malformed output; unsupported Windows (no observation, manager: unsupported); launcher state file (1.2.4) vs manager-observed running version (1.2.3); foreign-home running path → running-from-different-home and no runningVersion; JSON contract + human compatibility (human adds manager lines and a non-health note only when not running).
  • vp lint bootService.ts bootService.test.ts service.ts service.test.ts → clean.
  • Typecheck (apps/server tsc --noEmit, project tsconfig.json): 0 errors; 424 pre-existing effect suggestion diagnostics only (none in the added logic; the only bootService entries are pre-existing lines 421–423). Machine was under heavy contention; the check was run to completion directly.
  • CI: jobs are queued/pending with no steps assigned (Test/Test Server 1–3, Check, Rust, Release Smoke, Native fingerprint diff), matching the hub's queued-runner observation. No pass is claimed. Per the pack, publication was not withheld for CI.

Remaining consumer / native gates

  • ENV inventory/doctor has not yet consumed the --json contract.
  • No native execution on a real Linux systemd-user or macOS LaunchAgent here; systemd NRestarts/Result and launchctl paths are exercised only through the mirrored process runner. Fixture tests are not native service qualification.
  • End-to-end proof that the expected running artifact answers still needs a later identity/readiness probe; this contract deliberately publishes runningVersion only when the manager exposes the launched program.
  • No invented Mac restart count, policy change, or crash-loop reset.

Boundaries honored

No edit to serviceLauncher/serviceProtocol, native/windows-service-host/**, Tailscale, desktop/provider/UI, package/release/workflow files. No live registration/restart/relaunch, auth/network/policy change, inference, crash/reboot/logout/sleep test, telemetry, merge/release or R720 load. PR #9 is ready for review; no merge.

Copy link
Copy Markdown
Owner Author

ENV-1 review — S1/S2 changes required before status-consumer integration

Reviewed published head c2399b7d48be886528546cce86fda8df6f84376f, base bcc1a58b19a9d610a4f08fed191a364767bc65b3, the BootService/CLI patches and RESULT 5853806269. Publication is complete: no more old-session/source-recovery assignment. Preserve the JSON interface, bounded probes, selected-user work and 74 reported focused tests. I did not execute those tests or a native manager; the following findings are from the production branches and must be reproduced by the repair owner through actual status/CLI tests.

S1 — configured executable path is not the running T3 version

In observeSystemd / observeLaunchd, runningVersion is derived from ExecStart / program regardless of whether the result is stopped/unknown. A stopped registration still advertises a running version. These manager fields describe the configured launch program; they do not by themselves prove the executable currently held by a live PID. Moreover T3's existing serviceLauncher.ts can retain its own executable while replacing the server child during a version handoff. Even a proven launcher version is not automatically the server's running version.

Keep configured-program/launcher metadata separately named. Populate a field claimed to be observed running only with evidence tied to the actual process and target; otherwise leave it unknown. Do not introduce an endpoint or invent process evidence merely to fill it. Installed, configured-launcher, observed-launcher and observed-server identities may differ.

bootServiceProgramInBaseDir also relies only on startsWith(baseDir/runtime/versions/); a lexical prefix containing .. is not target containment. Parse/normalize the expected runtime layout using existing path helpers, or report unresolved binding. Do not resolve a foreign target into an accepted home silently.

Regressions: stopped registration retaining a versioned launch path; registered V2 while running evidence is absent/V1; launcher V1 with a newer server child; wrong-home and lexical-escape paths. Exercise both JSON and human outputs. Renaming an unobserved claim is preferable to adding speculative probing infrastructure.

S2 — preserve transitional, malformed and failed-query uncertainty

observeSystemd maps deactivating to stopped, and reads SubState without using it. active alone (e.g. SubState=exited, no live main PID) must not establish a live T3 process. Represent raw unit activity separately from process/readiness evidence; use bounded relevant native properties or keep the derived result uncertain. Systemd's documented D-Bus properties distinguish unit state, main-process identity and launch-command properties.

In observeLaunchd, any nonzero job-print failure that is not matched by the permission regex becomes not-loaded; the domain query similarly conflates miscellaneous failures with absence. Recognize established missing-job/domain outcomes separately. Unexpected failures and unrecognized output remain unknown, not negative existence evidence. A parsed PID must be a valid positive integer; malformed/negative restart counters must not become observed counts through permissive parseInt.

Regressions: deactivating with a still-present process, active/exited, PID zero/invalid, malformed counter, unexpected launchctl error versus genuine not-found, and existing timeout/permission cases. Preserve platform-unsupported behavior and current lifecycle policies.

Scope unchanged: the six #9 BootService/CLI/test/doc files only. No Windows adapter, launcher/protocol, service supervision policy, installed app, auth/network or release edits. Accepted #8 waits for a later single integration owner; no competing writer to its files. Current T3 CI 36294505416 remains queued; hygiene success is not its execution.

Post exact-head failing-before/passing-after evidence and test/CI/native limits on this PR, linked to ENV-1 pingdotgg#237. Fresh session only. No merge, native service mutation, model calls or user copyback.

@nullStack65

Copy link
Copy Markdown
Owner Author

START — ENV-1:R9-T3-STATUS (S1/S2 correction)

Fresh session, fresh isolated worktree. No earlier conversation or worktree reused. No old-session search performed.

  • Platform: macOS 26.6.2 (build 25G83), x86_64 (darwin).
  • Sole hub: #237.
  • Existing PR: #9 — ordinary follow-up commits on the existing branch, no duplicate PR, no history rewrite.
  • Input head (verified via API): c2399b7d48be886528546cce86fda8df6f84376f (env1/r8-t3-service-status-20260927, unchanged).
  • Base (verified via API): fork main bcc1a58b19a9d610a4f08fed191a364767bc65b3 (unchanged).
  • Controlling review: #9 comment 5860135479.
  • Conflicting writer: none observed — remote head still equals the input head; this prospectively replaces the completed R8 status assignment per the pack.

Exclusive scope (six files only)

  • apps/server/src/cloud/bootService.ts
  • apps/server/src/cloud/bootService.test.ts
  • apps/server/src/cli/service.ts
  • apps/server/src/cli/service.test.ts
  • docs/user/background-service.md
  • docs/internals/service-status.md

Planned S1/S2 correction

  • S1: stop publishing ExecStart/program-derived identity as observed running; name configured-launch metadata separately (configuredVersion / configuredProgramPath), report running identity only with live-process + target evidence, otherwise unknown. Replace the lexical startsWith containment proof with a normalized runtime-layout check via existing Path/pinnedRuntime* helpers; classify lexical escapes and foreign homes honestly.
  • S2: keep raw unit state separately; never map systemd deactivating to proven stopped; require a valid positive MainPID for running; distinguish genuine launchctl not-found from permission/timeout/unexpected failure; reject malformed/negative PIDs and counters.

Preserves the t3 service status --json command, bounded read-only probes, selected-user binding, human output shape and useful existing tests. No native service execution is claimed; findings are reproduced through production status/CLI tests. No merge, credential/network change, inference or telemetry.

The status contract published ExecStart/program-derived versions as the
running server, even for stopped or transitional units, and mapped systemd
deactivating to stopped. T3 retains its launcher executable while replacing
the server child, so configured-launcher and running-server identity are
different claims.

Rename the field to configuredVersion/configuredProgramPath, keep raw unit
state and a validated positive process id separate, require live-process
evidence before running, classify activating/deactivating as transitioning,
reject malformed/negative PIDs and counters, and distinguish genuine launchctl
not-found outcomes from permission, timeout and unexpected query failures.
Replace the lexical startsWith containment proof with a normalized
runtime-layout check. Bump the schema version to 2.
@nullStack65

Copy link
Copy Markdown
Owner Author

CHECKPOINT — ENV-1:R9-T3-STATUS S1/S2 correction

Fresh isolated worktree (/Users/businessaccount/Documents/r9-t3-wt), branch env1/r8-t3-service-status-20260927. Ordinary follow-up commit on the existing PR; no history rewrite.

  • Base bcc1a58b19a9d610a4f08fed191a364767bc65b3 (unchanged).
  • Input head c2399b7d48be886528546cce86fda8df6f84376f.
  • New head 9bd3043006a0be960c6e2a82863cfe92ceb32662 (fix(service): report configured launcher identity honestly in status).

Failing-before / passing-after (production status/CLI tests)

The correction was reproduced by running the new regressions against the unmodified production source (old bootService.ts / service.ts, new tests):

  • Before (c2399b7 + new tests): vp test run apps/server/src/cloud/bootService.test.ts apps/server/src/cli/service.test.ts → 22 failed, 66 passed (88).
  • After (9bd3043): same command → 88 passed, 0 failed, 0 skipped.

Failing before (S1): stopped registration still advertised a versioned launch path; changed configured version reported without process proof; launcher/server handoff collapsed to one identity; launcher state file treated as the running artifact; foreign-home and lexical .. configured paths promoted to the selected home.
Failing before (S2): deactivating mapped to stopped; active/SubState=exited with no live PID called running; zero/invalid PID and malformed/negative counters accepted via permissive parseInt; unexpected launchctl failure folded into missing domain/job.
Failing before (interface): JSON/human exposed runningVersion.

S1 after

  • Configured launch metadata is separately named: observation.configuredProgramPath (raw manager program) and configuredVersion (only when the normalized path is inside the selected base dir's runtime/versions tree). No field claims an observed running server.
  • running requires a live-process signal: systemd active/reloading + SubState=running + positive MainPID. active without a live main process stays unknown.
  • Containment now normalizes both paths with the existing Path helpers (pinnedRuntimeVersionsDir): a sibling with only a textual prefix, or a .. segment beneath the prefix, does not bind; a foreign home never yields configuredVersion (configured-from-different-home).

S2 after

  • Raw unit state preserved: observation.state (systemd ActiveState, launchd state), observation.subState, observation.processId.
  • deactivating/activating → transitioning (new running value), never stopped.
  • Malformed/nonpositive MainPID, zero/nonpositive launchd pid, and malformed/negative NRestarts are dropped, not coerced.
  • launchdNotFound distinguishes established "could not find" from permission/timeout/unexpected; unexpected nonzero is unknown (manager-query-failed), not absence. Timeout, permission and platform-unsupported behavior preserved.

Interface / consumers

  • t3 service status --json command preserved; BOOT_SERVICE_STATUS_SCHEMA_VERSION bumped 1 → 2 because runningVersion (an observed-running claim) was removed and running gained transitioning.
  • Added: configuredVersion, observation.{state,subState,processId,configuredProgramPath,configuredVersion}. Removed: runningVersion. No consumer in-tree yet; ENV inventory/doctor not yet wired.
  • Human output kept its shape; the Manager: line no longer appends a version, and adds Configured launcher:, Manager state:, and a non-health note for stopped/transitioning/not-loaded/unknown.

Checks

  • Focused tests: 2 files, 88 passed, 0 skipped (bootService 64, service 24).
  • vp lint on the four TS files: clean.
  • vp fmt --check on all six files: clean.
  • vp run --filter t3 typecheck: 0 errors; 424 pre-existing effect suggestion diagnostics only (unchanged from the R8 count).

Native / CI limits

  • Source-level reproduction only; no Linux systemd-user or macOS LaunchAgent lifecycle was executed. Fixtures are not native qualification.
  • CI for 9bd3043 is queued at posting time; no pass is claimed.

Scope unchanged: the six #9 files only. No serviceLauncher/serviceProtocol, Windows helper, startup, package/release/workflow, Tailscale or installed-bundle edits.

@nullStack65

Copy link
Copy Markdown
Owner Author

RESULT — ENV-1:R9-T3-STATUS (S1/S2 correction)

Fresh session, fresh isolated worktree. Ordinary follow-up commit on the existing PR; no duplicate PR, no history rewrite, no old-session reuse. Linked to sole hub #237.

Exact refs

  • Source / tested head: 9bd3043006a0be960c6e2a82863cfe92ceb32662 (fix(service): report configured launcher identity honestly in status), branch env1/r8-t3-service-status-20260927.
  • Input head: c2399b7d48be886528546cce86fda8df6f84376f.
  • Base: fork main bcc1a58b19a9d610a4f08fed191a364767bc65b3 (unchanged).
  • START: comment 5863756606. Checkpoint: comment 5863910638.
  • Platform: macOS 26.6.2 (25G83), x86_64. No conflicting writer observed; remote head equalled the input head before this push.

S1 before → after

The regressions were run against the unmodified production source (old bootService.ts/service.ts, new tests): 22 failed / 66 passed. On the corrected head: 88 passed / 0 failed / 0 skipped.

  • Before: runningVersion was derived from ExecStart/program even for stopped/unknown results, so a stopped registration advertised a running version. After: configured launch metadata is named separately (observation.configuredProgramPath + configuredVersion); no field claims an observed running server. running requires a live-process signal (systemd active/reloading + SubState=running + positive MainPID).
  • Before: a changed configured version read as the running identity. After: a changed configured path with no process proof stays unknown; configuredVersion may differ from installedVersion (launcher/server handoff is not collapsed).
  • Before: the lexical startsWith(baseDir/runtime/versions/) proof, so a sibling prefix or a .. segment passed. After: bindBootServiceProgramPath normalizes via the existing Path/pinnedRuntimeVersionsDir helpers; lexical escape and foreign homes do not bind (configured-from-different-home), and foreign-home data never becomes configuredVersion.
  • Regressions added for: stopped registration with a versioned launch path; changed configured version without proof; launcher 1.2.3 vs server child 1.2.4; foreign home (systemd + launchd); lexical .. escape.

S2 before → after

  • Before: systemd deactivating mapped to stopped and active alone could be running. After: activating/deactivating → new transitioning value; active/reloading with SubState=exited or no live PID → unknown. Raw state/subState preserved; processId published only for a positive MainPID.
  • Before: a nonzero launchctl job/domain result that was not the permission regex became not-loaded/domain-missing. After: only an established could not find outcome is absence; other nonzero results are unknown (manager-query-failed). Permission and timeout branches preserved.
  • Before: permissive parseInt could invent counts/PIDs. After: zero/malformed PID and malformed/negative NRestarts are dropped; Number.isSafeInteger guards retained.
  • Regressions added for: deactivating with a still-present process; active/exited; zero/invalid PID (systemd + launchd); malformed counter; unexpected launchctl job/domain failure vs genuine not-found; launchd timeout/permission and Windows-unsupported preserved.

Fields and consumer compatibility

  • t3 service status --json command preserved. BOOT_SERVICE_STATUS_SCHEMA_VERSION 1 → 2 because runningVersion (an observed-running claim) was removed and running gained transitioning.
  • Added: configuredVersion; observation.{state, subState, processId, configuredProgramPath, configuredVersion}. Removed: runningVersion.
  • running values: running | stopped | transitioning | not-loaded | unknown.
  • Human output keeps its shape; the Manager: line no longer appends a version, and adds Configured launcher:, Manager state:, plus the non-health note for any non-running state.
  • No in-tree consumer yet — ENV inventory/doctor has not consumed the --json contract. The removed runningVersion is not referenced anywhere else in the repo.

Tests / counts / skips

  • vp test run apps/server/src/cloud/bootService.test.ts apps/server/src/cli/service.test.ts → 2 files, 88 passed, 0 failed, 0 skipped (bootService 64, service 24).
  • vp lint (four TS files): clean. vp fmt --check (six files): clean.
  • vp run --filter t3 typecheck: 0 errors; 424 pre-existing effect suggestion diagnostics only, matching the R8 baseline.

Actual CI URLs (head 9bd3043)

  • CI run 36381022904 — all eight jobs queued (Check, Test, Test Server 1–3, Rust, Release Smoke, Mobile Native Changes, Native fingerprint diff); no pass claimed.
  • Mobile Fingerprint Check 36381022782 — queued.
  • PR Vouch 36381019926 — success. PR Size 36381019945 — success.
  • The pre-correction head's CI run 36294505416 is now cancelled (superseded). Per the pack, publication was not withheld for queued runners.

Source-vs-native limits

  • Evidence is source-level production tests only. No Linux systemd-user or macOS LaunchAgent lifecycle was executed, and no native manager was mutated. Fixture tests are not native qualification.
  • MainPID/SubState/launchctl outputs are exercised through the mirrored process runner; real launchctl message wording is approximated by anchored tokens.

Remaining integration gates

  • ENV inventory/doctor must adopt the configuredVersion contract and must not read it as server health.
  • An end-to-end running-server identity/readiness probe remains out of scope; running is a manager fact only.
  • Windows integration remains a separate, single-owner follow-up after this status boundary; no Windows helper or adapter was touched here.

Scope honored: the six #9 files only. No serviceLauncher/serviceProtocol, Windows helper, server startup, ENVCHK/STALL/provider, package/release/workflow, Tailscale or installed-bundle edits. No merge, live service action, credential/network change, inference, telemetry, reboot/sleep experiment, new runner or R720 load.

Copy link
Copy Markdown
Owner Author

ENV-1 R9 review / R10 lifecycle handoff — retain v2 correction; close a small parser residual before integration

Reviewed 9bd3043, its diff, production parsing/observation paths and RESULT 5863932033. S1's configured-versus-running distinction is accepted for source integration: configuredVersion/configuredProgramPath are no longer running-server claims; schema v2, transitional state and normalized lexical target binding are retained. The worker reports failing-before 22/88 and passing-after 88/88, plus targeted lint/typecheck/format. I reviewed this source and receipt, not a new local full-suite or native-manager execution. Current substantive CI 36381022904 is still queued.

Most S2 behavior is present. One narrow numeric-boundary residual is visible in the actual source:

  • parseLaunchdPrint still extracts only the leading digit sequence with pid ... (\\d+), so a field such as pid = 12junk becomes PID 12; safe-integer checking after extraction cannot reject the discarded suffix. The last-exit-code extraction has the same prefix pattern.
  • parseSystemdShow checks decimal spelling for MainPID, but then tests only mainPid > 0, unlike its Number.isSafeInteger(nRestarts) guard. An excessively large decimal PID can therefore become an unsafe number/Infinity and satisfy the later 'PID is present' running branch.

These are source-level findings; the next owner must reproduce them through the real parser/status/CLI tests, not a second parser. Require whole-field integer parsing and safe positive PIDs (and a safe signed whole last-exit value). Preserve the existing absent/unknown semantics. No extra state store, endpoint or numeric-policy framework is needed. Update the stale PR body still describing schema v1/runningVersion.

To avoid another standalone micro-repair wave, ENV-1:R10-T3-LIFECYCLE is the sole next source writer: Stage A closes these small parser cases on this PR, publishes/readbacks its tested exact head and RESULT; Stage B consumes that explicit candidate together with source-accepted #8 in one separate Windows lifecycle integration PR. That new candidate remains subject to independent review—Stage A's author cannot declare it fleet-qualified or merge either PR. Preserve #8's original branch and do not rewrite either history.

Stage B owns the BootService/CLI adapter and the minimum real launcher/server shutdown path together. No competing lifecycle writers are dispatched. Native SCM/reboot/credential/profile execution, release packaging/adoption, and existing ENVCHK startup work remain separately qualified and coordinated. T3 #5's tooling/artifact branch and assets stay untouched. Full scope and checkpoints will be committed on sole hub pingdotgg#237.

@nullStack65

Copy link
Copy Markdown
Owner Author

START — ENV-1:R10-T3-LIFECYCLE (Stage A on existing #9)

Fresh session, fresh isolated non-cloud worktree (%TEMP%\opencode\r10a-t3-status), branch env1/r10a-status-20260928 created at the input head; publication is an ordinary fast-forward onto the existing PR branch, no duplicate PR and no history rewrite.

Stage A scope (six #9 files only)

apps/server/src/cloud/bootService.ts, apps/server/src/cloud/bootService.test.ts, apps/server/src/cli/service.ts, apps/server/src/cli/service.test.ts, docs/user/background-service.md, docs/internals/service-status.md.

Close only the whole-field / safe-integer parser residual (pid = 12junk prefix; launchd last-exit suffix; unsafe/overflow systemd MainPID). Preserve schema v2, configured-versus-running identity, transitioning, bounded native-query behavior. No S1 redo, no invented running-server evidence, no merge/self-approval.

Stage B scope (separate new branch/worktree from refreshed main)

Integrate the tested Stage-A candidate + accepted #8 into one Windows-lifecycle integration draft PR: BootService/CLI adapter (install/status/restart/uninstall), serviceLauncher/serviceProtocol/serviceLauncherClient, native/windows-service-host/** integration delta, optional windowsBootService.ts, docs, minimal additive managed-shutdown wiring in server.ts (subject to the ENVCHK ownership check), and a focused shutdown test. Real graceful stop: SCM → launcher → server drain → whole-job completion. No generic host framework, no public control endpoint.

Fleet-ready=false.

@nullStack65

Copy link
Copy Markdown
Owner Author

RESULT — ENV-1:R10-T3-LIFECYCLE Stage A (existing #9)

Fresh session, fresh isolated worktree; ordinary fast-forward onto the existing PR branch; no duplicate PR, no history rewrite. Linked to sole hub #237.

Exact refs

  • Source / tested head: 22734455e1d2395a6acfe762ec15f412e4f28511 (fix(service): reject numeric prefixes and unsafe integers in manager parsing), branch env1/r8-t3-service-status-20260927.
  • Input head: 9bd3043006a0be960c6e2a82863cfe92ceb32662. Base: bcc1a58b19a9d610a4f08fed191a364767bc65b3 (unchanged).
  • START: 5864354649. Handoff: 5864056213.
  • Platform: Windows 11 Home build 26200, AMD64; node v24.21.0, vite-plus 0.3.3, pnpm v11.10.0.

What changed (whole-field safe-integer parsing)

A module-local parseWholeSafeInteger reads the whole field (/^-?\d+$/ after trim), parses with Number.parseInt, and drops anything outside Number.MAX_SAFE_INTEGER; sign/positivity stay with each call site.

  • parseLaunchdPrint: pid regex is now ([^\n]*) + whole-field safe-integer parse + > 0; last exit code likewise whole-field safe signed integer. pid = 12junk and last exit code = 7junk no longer truncate to 12 / 7; an unrepresentable pid no longer becomes an unsafe number.
  • parseSystemdShow: MainPID now requires a safe positive integer before it can imply a live process (an overlong decimal no longer rounds into the running branch); NRestarts remains a non-negative safe integer.
  • Missing / malformed / unrepresentable values stay absent (unknown), not coerced. No numeric-policy framework, no new state store or endpoint.

Failing-before / passing-after (production tests, exact head)

  • New regression added to bootService.test.ts. Before (source at 9bd3043 + new test): vp test run apps/server/src/cloud/bootService.test.ts → 5 failed / 60 passed (my new case failed on expected 12 to be undefined).
  • After (22734455e): 4 failed / 61 passed — the new case passes.
  • Pristine baseline (head 9bd3043, no changes): 4 failed / 60 passed. The 4 residual failures are pre-existing Windows path-separator assertions (versions/1.2.3 vs \), unrelated to parsing; this change adds no new failures.
  • vp test run apps/server/src/cli/service.test.ts → 24 passed / 0 failed.

Scoped checks

  • vp lint on the four TS files → clean (exit 0).
  • vp fmt --check on the two changed files → "All matched files use the correct format."
  • vp run --filter t3 typecheck → exit 0, 0 errors, 424 pre-existing effect suggestion diagnostics (matches the R9 baseline).

Actual CI (head 22734455e)

Interface preservation

t3 service status --json unchanged apart from the hardened parsing: schemaVersion: 2, configured-versus-running identity (configuredProgramPath/configuredVersion, no runningVersion), running: running|stopped|transitioning|not-loaded|unknown, bounded read-only probes, manager: unsupported on Windows. Stale PR body (schema v1 / runningVersion) was rewritten.

Source-vs-native limits

Evidence is source-level production tests. No Linux systemd-user or macOS LaunchAgent lifecycle was executed; Windows manager observation remains unsupported-by-design here, so none of the changed paths is native-qualified. Fixtures/mirrored runners are not native service qualification.

Boundaries honored

Six #9 files only. No edit to serviceLauncher/serviceProtocol, native/windows-service-host/**, server startup, provider/orchestration/usage/migration, Tailscale, desktop/WSL or package/release/workflow. No merge, no native service mutation, no inference/telemetry, no R720 load.

Fleet-ready=false. Stage B (separate Windows-lifecycle integration PR) consumes this tested head + accepted #8; this head still requires independent review.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant