Skip to content

watchdog in daslang: a process handle in fio, whole-program standalone emission, watchdog.py retired - #3950

Merged
borisbat merged 14 commits into
masterfrom
bbatkin/fio-process-watchdog
Sep 6, 2026
Merged

watchdog in daslang: a process handle in fio, whole-program standalone emission, watchdog.py retired#3950
borisbat merged 14 commits into
masterfrom
bbatkin/fio-process-watchdog

Conversation

@borisbat

@borisbat borisbat commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Behavior change: the release bundles no longer carry watchdog.py - a deploy that launched python watchdog.py launches the watchdog executable at the bundle root instead, and a checked-in watchdog.json may carry only keys the flag table has.

Why. Every release bundle needed Python for its supervisor, and a -ctx standalone context could not host the supervisor because it emitted only the entry module's functions.

What changes.

  • fio gains a long-lived child process: spawn_process / process_drain / process_poll / process_wait / process_terminate / process_kill / process_pid / process_alive / close_process, the process_running sentinel, and with_process - a handle a supervisor drives on its own clock, with a non-blocking line drain, KEY=VALUE environment overrides, a working directory, and tree-wide signals.
  • The standalone (-ctx) emitter emits every used function of every module into its one translation unit - foreign functions, class methods through their slots, and the C++ modules their externs reach - with one copy per shared generic instance.
  • utils/watchdog/watchdog.das is the supervisor: config and watchdog.json layering, layout discovery, the exit 0 / 3 / 4 contract, ranked startup stages, a JSON-lines log with rotation, crash bundles, WER, notifications, and a stop ladder (request, terminate, kill, give up).
  • bin/watchdog is that supervisor as a static executable (libDaScript and dasHV linked as archives, no compiler, no shared module, no lock on the files a deploy replaces); daslang utils/watchdog/main.das runs the same code under the interpreter.
  • daspkg gains release_include_tool("watchdog"): a manifest names a built tool once and daspkg resolves bin/<name> or bin/Release/<name>.exe to the bundle root. The four manifests use it; the deploy scripts launch the binary.
  • watchdog.py, its three Python tests and their CI step are gone; the exchange consent dialog, the sidecar tray rails and the tray icon were cut, not ported.

Observable behavior.

  • A bundle's supervisor -> python watchdog.py -> ./watchdog (.\watchdog.exe), beside watchdog.json as before.
  • A -ctx program calling a required das module's function -> a link error or a table miss -> runs.
  • exit(N) in a supervised das child -> the parent read 1 -> unchanged, now documented: a code comes from def main() : int.
  • A stale key in watchdog.json -> refused start, as before; the checked-in dasllama-server config no longer carries one.
  • bin/watchdog --cwd utils/dasllama-server from a source tree -> refused ("nothing to supervise") -> supervises: discovery also finds the daslang beside the watchdog itself.
  • The full test_aot binary on master -> two objects carrying impl_aot_f16_cvt, no link -> links; tests/daslib/test_tune_shells.das under --use-aot -> error[50101] -> registered and green.

Where to look. builtin_spawn_process and builtin_process_drain in src/builtin/module_builtin_fio.cpp; foreignUsedFunctions and its walks in daslib/aot_cpp.das / daslib/aot_standalone.das; Supervisor.tick and on_child_exit in utils/watchdog/watchdog.das; ship_tools in utils/daspkg/commands.das.

Validation, claims, ledger

Validation

  • tests/fio/test_process.das (6), tests/watchdog/test_watchdog.das (22), tests/aot/test_standalone_emit.das (24 incl. the foreign-call arm with its table-count pin), utils/daspkg/test_daspkg.das (229) - interpreted and, for the first two, under JIT; watchdog_smoke (small lane) runs bin/watchdog supervising a daslang child through a crash and a clean exit; standalone_full_runtime and the four standalone_ctx / standalone_modules ctests pass on the changed emitter; test_aot_subset builds.
  • The full AOT sweep (test_aot -use-aot ... --test tests) on the rebased tip: 13232 tests, 0 failed, 0 errors, 4 skipped. It needed two fixes master owns in tests/aot/CMakeLists.txt: the metal module list still added daslib/f16_cvt.das after the file moved into the daslib set (duplicate symbols, no link), and master's new tests/daslib/test_tune_shells.das was not registered (error[50101] under --use-aot).
  • bin/watchdog supervised utils/dasllama-server with a real model on macOS: ready in 31 s, a kill -9 of the child became a crash bundle, a restart and a recovered event in 7 s, and Ctrl-C became a /shutdown POST, a clean child exit and exit 0.
  • A real daspkg release --root utils/internal/dasweb-buildd on macOS shipped watchdog into the .app's Contents/MacOS beside watchdog.json with its exec bit intact.
  • The EASTL lane (build_eastl) is the proof for the eastl-string arm of spawn_process / process_drain and the environ-composed child environment; no local EASTL build.
  • Not run here: the Windows arms of the nine externs (job object, PeekNamedPipe, CreateProcess failure, env block) and of the watchdog (WER via reg.exe, the PowerShell balloon) - tests/fio/test_process.das runs on every CI platform, tests/.das_test does not gate fio.
  • tests/watchdog is gated off under --use-aot in tests/.das_test: options no_aot covers the file's own functions, but the watchdog module it requires is outside the AOT set and fails the link.
  • The "Test watchdog tray wording (python)" step in extended_checks.yml is removed with the two files it ran; their subject (tray labels, the consent dialog) is cut, and the stage ranking they leaned on has its own arm in tests/watchdog, swept per PR.
  • utils/daspkg/test_daspkg.das joins run_utils_tests; its unit suite ran in no CI row before.

Claims - stated, not tested

  • The stop ladder's kill and give-up rungs (kill_requested, child_unkillable): a das child cannot ignore SIGKILL, so no fixture reaches them; a break would show as a supervisor that never ends after --stop-timeout + 20 s.
  • process_kill / TerminateJobObject(9) and close_process on Windows: read only.
  • The manifest -> PackageReleaseInfo -> cmd_release -> ship_tools chain is proven by the macOS release above, not by a unit test; ship_tools itself is.

Not done

  • --help on the static exe prints Usage: daslang watchdog -- [flags] - clargs' help header assumes the interpreter host.
  • Process metrics (memory events, Windows-only in the Python) are not ported.
  • The tray icon returns in a follow-up as a dasStdDlg primitive: the place people click for status and the control page.
  • Lint candidates (proposals): a utils/watchdog/REVIEW.das gate that the emit() event names and the StageRule names equal the README's lists and every WatchdogConfig field carries @clarg_doc; a modules/dasLLAMA/REVIEW.das check that EXCHANGE_CONSENT_NOTICE equals the fixture's consent_notice.
  • Checklist proposals from the round, not applied: widen modules/dasLLAMA/REVIEW.md's doc-staleness rule from "call, flag, or default" to "anything the change made untrue"; restate include/daScript/simulate/REVIEW.md's ABI-sweep trigger as the property.

borisbat and others added 10 commits September 5, 2026 22:14
…ait/terminate/kill/pid/alive, close_process - the primitive a supervisor drives on its own clock

popen_argv is block-scoped and blocks to EOF; a supervisor needs a handle it
can poll and drain on a 250 ms tick, signal, and reap. Nine externs in fio_core
next to popen_argv, an opaque `SubProcess?` handle, and a `process_running`
sentinel that process_poll / process_wait answer while the child is alive.

- spawn_process(argv, cwd, env): no shell, forward-slash argv[0] on every
  platform; `cwd` empty inherits; `env` is KEY=VALUE overrides on the
  inherited environment. stdout+stderr merge into one non-blocking read end.
  Windows: kill-on-close job object, exe resolved from the parent's directory
  as CreateProcess does. POSIX: the child leads its own process group so
  killpg reaches the tree; a relative argv[0] naming a path is made absolute
  before the child chdir's, matching Windows.
- process_drain(p, blk): fires blk once per complete line ready this tick
  (partial lines buffer across calls, a final unterminated line flushes at
  EOF); returns false once stdout is closed. Never blocks the tick.
- process_terminate / process_kill: TerminateJobObject 15 / 9 on Windows,
  killpg SIGTERM / SIGKILL on POSIX.
- close_process: frees the handle; a still-running child dies with it
  (job close on Windows, SIGKILL + reap on POSIX), so nothing leaks or zombies.
- daslib/fio: `with_process(argv[, cwd, env]) <| $(var p) {}` RAII over
  spawn/close; `process` typedef.

tests/fio/test_process.das drives a daslang child through the whole
lifecycle - env and cwd observed in its output, lines drained one per call,
running while parked, the real exit code from wait, alive before and dead
after - and terminates a parked one. The fixture returns its code from main:
a das `exit()` unwinds as an abnormal termination and the CLI reports 1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…state machine the host ticks, with the Python's config, discovery, stages, crash bundles and exit-code contract

The library half of the watchdog.py replacement. One module, no Python:

- WatchdogConfig is a [CommandLineArgs] struct. watchdog.json beside the
  program sets each flag's default (a key becomes a `--flag value` the CLI
  did not supply), an unknown key is fatal by name, and everything after
  `--` goes to the child verbatim - clargs drops flag-shaped positionals,
  so the split is explicit. Discovery: main.das beside bin/Release/daslang
  is script mode, one *.exe is program mode, anything else is an error.
- Emitter writes {"ts","event",...} as one compact JSON line per record
  to a file that rotates at 20 MB x 5, and to stdout.
- StageDetector ranks the child's startup lines and reports only forward
  moves; the tune-restart marker rewinds the rank. @tune events log at the
  kernel boundaries. Health and shutdown go over dasHV's client.
- Crash bundles: crash.json, the log, the dump, symbols, the tune sidecar,
  the JIT artifacts the child named. WER policy read/install via reg.exe.
- Supervisor.tick() is one 250 ms step: drain, health, poll, classify an
  exit - 0 ends, 3 with the marker relaunches at once, 3 without backs off,
  4 relaunches, anything else is a crash with bounded backoff. The host
  owns the loop and calls request_stop() on its signal; run() is the loop
  for a host that owns nothing else. watchdog_start() does everything
  before the loop: config, the dump policy, the single-instance pid file.

Cut on purpose: the exchange UI (consent, sidecar fold, tray rails, the
DAS_TUNE_CONTROL stop file) and the tray; notifications stay, spawned
through the platform's own helper, no binding needed.

tests/watchdog drives the library under dastest with a daslang child:
config layering, discovery, stage ranks, the log envelope and rotation,
crash-bundle-restart, config restart, the script-mode tune bootstrap,
and the stop-file handshake from a host stop request.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ext is one translation unit, and a required module's functions, its class methods and the modules their externs reach used to be left out

A `-ctx` context walked only the entry module: `collectUsedFunctions` ran
with `all_modules = false` on every standalone path, the markers, the
block-variable collector, the `ArgsConverter` thunks and the emitter all
visited `thisModule` alone, and the module registration marked only what
the entry module's own code named. A required das module's functions were
neither declared nor defined, its class methods had no table row for their
Func slot, and a C++ module reached only from one of them (dasHV from a
supervisor's health probe) was neither included nor registered. The
example survived because its one cross-module das call was inlined; the
cross-module fixtures covered globals and types, never a call.

Now `foreignUsedFunctions` (aot_cpp) lists the used, non-builtin,
non-template functions outside the entry module, and every pass walks
them after the entry module through the bound `visit(fn, adapter)`:
NoAotMarker (before the can-AOT check, so its verdicts are final),
PrologueMarker, SetPrinterFlags, the collector, `UseTypeMarker` where the
standalone path prunes and links, the thunks, and `StandaloneContextGen`.
Declarations, the function count, the debug-info rows and the table are
whole-program. A generic instance several modules instantiated shares one
AOT name, so declarations, bodies, thunks and rows are emitted once per
name. Foreign globals' initializer temporaries collect under the
collector's null key, the one `__init_script` declares.

tests/aot: `_standalone_foreign_call_fixture` calls a required module's
function and a class method through its Func slot; the arm asserts both
rows, the body, and that fio_core - reached only from the foreign
function - is registered. The example and the C++ standalone-context
tests are unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… the full runtime with libDaScript and dasHV static, driven one tick at a time by a C++ main that owns argv, the pid and the signals

utils/watchdog/main.das is the entry for two hosts. The exe host calls
start / tick / request_stop / result: main.cpp hands argv to
setCommandLineArguments, installs SIGINT/SIGTERM (SIGBREAK on Windows)
into an atomic the loop turns into request_stop, passes its pid for the
single-instance file, and returns the supervisor's code. argv[0] locates
the bundle: watchdog.json and the discovered program sit beside the
executable, where the Python sat beside what it supervised. The
interpreter host runs main: the same supervision, --cwd or the current
directory as the program directory, no pid file, no signal hook.

utils/CMakeLists.txt builds it the way examples/standalone/06 is built:
`utils/aot/main.das -- -ctx` generates the context, the exe links
libDaScript and libDasModuleHV, lands in bin/, strips in Release, installs
beside daslang. watchdog_smoke (small lane) runs the binary supervising a
daslang child that crashes once and exits clean, and reads the log back.
`--help` prints the flag table; everything after `--` goes to the child.

CLAUDE.md: `exit(N)` under the daslang CLI reports 1 whatever N was - a
code the parent must read comes from `def main() : int`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…daspkg's new release_include_tool, the deploy scripts launch it, and the docs, skills and changelist follow

The four manifests (dasllama-server, dasllama-ladder, dasweb-buildd,
dasweb-playground) replace `release_include_from("utils/watchdog/watchdog.py")`
with `release_include_tool("watchdog")`: daspkg resolves `bin/<name>` or, in an
MSVC tree, `bin/Release/<name>.exe`, and copies the executable to the bundle
root, failing the release when no build has it (`ship_tools`, tested).
The systemd unit runs `<release>/watchdog`, deploy-jit.ps1 copies
`bin\Release\watchdog.exe` beside watchdog.json, the latchpoint launchers run
the built binary, and the "tray wording" Python CI step goes with the tests
of the features the port cut.

utils/watchdog/README.md and doc/source/reference/utils/watchdog.rst describe
the daslang watchdog; REVIEW.md binds the log's event vocabulary and the
flag table instead of the consent wording that no longer exists here. The
exchange review rule and the consent notice lose their watchdog copy.
fio's nine process externs, the SubProcess type and process_running get
their handmade entries; the shipped fio skill and cpp_integration carry the
new contracts; tests_in_repo drops a stale note about in-file no_aot.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the JIT artifacts' relative layout, an unassignable Windows job never shadows the process handle

Three defects from review. A child that ignored SIGTERM held the
supervisor forever: after the stop timeout the request now escalates to
process_terminate once and to process_kill ten seconds later, each logged.
The bundle computed a JIT artifact's path under jitted_scripts/ by chopping
the jit dir's length off the source, which left a leading separator that
path_join took as absolute; it now uses relative_result, which also refuses
a sibling directory sharing the prefix. On Windows, a job object the child
could not be assigned to (the watchdog itself inside a restrictive job)
stayed on the handle empty, so terminate and kill acted on nothing; the job
is closed and the process handle takes over.

The crash test's child now names a cached DLL under a subdirectory of the
jit dir, and the test asserts the DLL and its sibling artifact land in the
bundle at that relative path. utils/daspkg/test_daspkg.das joins
run_utils_tests: its unit suite ran in no CI row.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… handle is proven to kill, three checklists say what they mean, and the bundled watchdog.json loses its retired key

daspkg's ship_tools refused nothing: a manifest tool name went straight
into `bin/<name>` and `<bundle>/<name>`, so a name with a separator read
and wrote outside both directories. It now passes is_safe_pkg_name first
(tested), and a shipped tool gets the rpath scrub and the symbols every
other shipped binary gets. fio's "closing kills a child still running"
was a C++-decided contract no test failed on; tests/fio now closes a
handle - explicitly and by leaving a with_process block - while the child
runs, and asserts the pid is dead.

utils/dasllama-server/watchdog.json still carried the Python-era `tray`
key, and the daslang watchdog refuses an unknown key: that bundle's
supervisor would not have started. The key is gone, and the watchdog
checklist gains the rule that would have caught it - a removed config
field updates every watchdog.json in the tree. Its other rules now bind
the log, not the file, and name the in-tree readers; utils/daspkg's rules
name the release path, the sink property behind is_safe_pkg_name, and the
main exe the sidecar rule means; daslib's struct-name rule names the
property (anything reaching aotStructName / aotEnumName) instead of one
visitor class. Drift the port left behind is settled: the shipped daspkg
skill loses a `bin/Release/` the bundle gate bans and a self-defeating
example, the shipped fio skill's sample compiles (every process_* call is
unsafe), aot_testing.md drops the retired two-marker no_aot rule, and the
dasLLAMA engine doc and server README stop naming a watchdog dialog.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… config's precedence needs no lookup, and the tuner abort, the --no-shutdown stop, the wait timeout and the paired table counts each get their test

finish() carried a terminate/kill ladder no call site could reach - every
caller nulls the child first, and the stop ladder in tick() is what ends a
live child - so the arm is gone and the contract is stated on the function.
config_to_args skipped a config key the command line also carried, but
clargs keeps the last occurrence of a flag, so ordering the config's copy
first already makes the flag win; the lookup is gone. The discovery test
now pins the daslang beside main.das, not any string ending in "daslang".

New arms: exit 3 without the restart marker is a tuner abort - relaunched
with backoff, no crash; a stop under --no-shutdown terminates the child
and still ends with 0; process_wait answers process_running when its
timeout passes first; and the emission test checks the context sizes its
function table by exactly the info rows it fills, foreign rows included -
the two counts are computed separately and reverting either alone was
green before. The park fixture parks without a stop file named, until
terminated. The exchange client's comments and the exchange_accept
environment doc stop naming the deleted watchdog dialog and its tray rail.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ers the file, not the utils/watchdog module it requires, so the AOT sweep still failed the link on that module; the skills now say so

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…bin/watchdog --cwd utils/dasllama-server supervises the source tree the way the README promised

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 6, 2026 05:21
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new Windows spawn_process path passes a const std::string buffer to CreateProcessA via (LPSTR)cmdLine.c_str(), which is undefined behavior and needs a mutable command-line buffer.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR replaces the Python-based watchdog supervisor with a daslang implementation that can ship as a static executable, while also extending fio with a long-lived child-process handle API and updating the standalone (-ctx) emitter to include all used functions across required modules.

Changes:

  • Add a daslang watchdog (utils/watchdog/*.das) plus a bin/watchdog static executable host (C++ main + standalone context generation) and retire the Python watchdog + tests.
  • Introduce fio long-lived process APIs (spawn_process, process_drain/poll/wait/..., process_running, with_process) and document them.
  • Update standalone-context emission to include all used functions across modules (including foreign functions/method slots) and extend tests/packaging to ship the watchdog tool via release_include_tool.
File summaries
File Description
utils/watchdog/watchdog.das New watchdog supervisor library (config/discovery, logging, stages, crash bundles, supervision loop).
utils/watchdog/test_tray_state.py Removes Python tray wording tests (tray surface retired).
utils/watchdog/test_consent.py Removes Python consent tests (dialog surface cut).
utils/watchdog/smoke_test.cmake Adds CTest smoke coverage for the watchdog executable supervising a child through crash + clean exit.
utils/watchdog/REVIEW.md Updates review checklist for log/stage name stability and config-field discoverability.
utils/watchdog/README.md Updates documentation from Python supervisor to static watchdog executable + interpreter entry.
utils/watchdog/main.das New shared entrypoint used by both the standalone exe host and interpreter mode.
utils/watchdog/main.cpp New standalone executable host driving the supervisor via exported functions.
utils/watchdog/dummy_server.py Removes Python dummy supervised server (desktop-surface testing retired).
utils/internal/dasweb-playground/README.md Updates production launch instructions to use ./watchdog.
utils/internal/dasweb-playground/.das_package Switches bundle shipping from release_include_from(watchdog.py) to release_include_tool("watchdog").
utils/internal/dasweb-buildd/README.md Updates production launch instructions to use ./watchdog.
utils/internal/dasweb-buildd/.das_package Switches bundle shipping to release_include_tool("watchdog").
utils/internal/dasllama-ladder/README.md Updates deploy narrative to watchdog binary.
utils/internal/dasllama-ladder/dasllama-deploy.sh Updates systemd unit to start the watchdog executable.
utils/internal/dasllama-ladder/.das_package Switches bundle shipping to release_include_tool("watchdog").
utils/daspkg/test_daspkg.das Adds unit test coverage for ship_tools / release_include_tool behavior and safety checks.
utils/daspkg/REVIEW.md Expands release-path review guidance to include tool shipping and path/shell safety.
utils/daspkg/package_runner.das Plumbs tools through PackageReleaseInfo.
utils/daspkg/commands.das Implements ship_tools and wires it into cmd_release.
utils/dasllama-server/watchdog.json Removes stale tray key (now invalid).
utils/dasllama-server/test_openai_server.das Updates reference from watchdog.py to utils/watchdog.
utils/dasllama-server/README.md Updates launch instructions to watchdog executable; trims removed features.
utils/dasllama-server/deploy-jit.ps1 Updates deploy script to copy watchdog.exe and update instructions.
utils/dasllama-server/CONTROL_PAGE_PLAN.md Updates references to watchdog relaunch handling.
utils/dasllama-server/.das_package Switches to release_include_tool("watchdog").
utils/CMakeLists.txt Adds build + install of watchdog executable and registers a smoke test.
tests/watchdog/test_watchdog.das Adds watchdog behavior tests (config layering, discovery, stages, crash/restart, stop ladder paths).
tests/watchdog/_fixture_watchdog_child.das Adds supervised child fixture for watchdog tests.
tests/fio/test_process.das Adds tests for new long-lived process handle lifecycle and semantics.
tests/fio/_fixture_process_child.das Adds supervised child fixture for fio process tests.
tests/aot/test_standalone_emit.das Adds coverage for whole-program emission across required modules (foreign calls, class method slots, module registration).
tests/aot/CMakeLists.txt Adds watchdog to AOT suites list.
tests/aot/_standalone_foreign_call_fixture.das New fixture for foreign-call emission test.
tests/aot/_standalone_dep_call.das New required module fixture providing foreign-used functions/methods.
tests/.das_test Gates watchdog tests off under --use-aot to avoid linking non-AOT modules.
src/builtin/module_builtin_fio.cpp Adds long-lived process implementation and bindings (spawn_process, drain/poll/wait, signals, pid/alive, close, sentinel).
skills/internal/tests_in_repo.md Updates guidance on options no_aot and why required non-AOT modules still break test_aot.
skills/internal/aot_testing.md Updates AOT testing guidance consistent with new watchdog precedent.
skills/daspkg.md Documents release_include_tool("watchdog").
skills/daslang/references/files-and-paths.md Documents long-lived with_process / process_* API usage and semantics.
skills/cpp_integration.md Updates standalone-context integration docs to reflect whole-program emission behavior.
plans/dasweb_backend.md Updates plan references from Python watchdog to static watchdog executable.
plans/dasllama_io_site.md Updates plan references from Python watchdog to watchdog.das.
modules/dasLLAMA/REVIEW_EXCHANGE.md Removes watchdog-dialog requirement from exchange consent event guidance.
modules/dasLLAMA/ENVIRONMENT.md Updates wording to remove watchdog-specific behavior claim.
modules/dasLLAMA/dasllama/dasllama_exchange.das Removes watchdog-dialog wording coupling and updates consent-surface comments.
modules/dasLLAMA/dasllama/dasllama_env.das Updates docs for DASLLAMA_EXCHANGE_ACCEPT to avoid watchdog-specific claims.
modules/dasLLAMA/ARCHITECTURE_ENGINE.md Updates consent-surface description (control page only).
install/CLAUDE.md Updates utils/watchdog entry to new invocation modes.
include/daScript/simulate/aot_builtin_fio.h Adds AOT builtin declarations for new process APIs.
examples/games/latchpoint/start-server.sh Switches example launch from Python watchdog to bin/watchdog.
examples/games/latchpoint/start-server.ps1 Switches example launch from Python watchdog to watchdog.exe.
doc/source/stdlib/handmade/Variable-fio-process_running.rst Documents new process_running sentinel.
doc/source/stdlib/handmade/function-fio-spawn_process-0x5eca268e4ba0b640.rst Documents spawn_process.
doc/source/stdlib/handmade/function-fio-process_wait-0xe5ddee492688d8dc.rst Documents process_wait.
doc/source/stdlib/handmade/function-fio-process_terminate-0x1227a543288285f0.rst Documents process_terminate.
doc/source/stdlib/handmade/function-fio-process_poll-0x21f24a39d497e02e.rst Documents process_poll.
doc/source/stdlib/handmade/function-fio-process_pid-0x349c5646dfa72325.rst Documents process_pid.
doc/source/stdlib/handmade/function-fio-process_kill-0x430dd55ae318dbeb.rst Documents process_kill.
doc/source/stdlib/handmade/function-fio-process_drain-0x4dd6d23e716a3381.rst Documents process_drain.
doc/source/stdlib/handmade/function-fio-process_alive-0x90274ac9d5662c36.rst Documents process_alive.
doc/source/stdlib/handmade/function-fio-close_process-0xf82966f695d14fec.rst Documents close_process.
doc/source/stdlib/handmade/annotation-fio-SubProcess.rst Documents SubProcess annotation.
doc/source/reference/utils/watchdog.rst Updates reference docs for the watchdog tool.
doc/reflections/das2rst.das Adds new process APIs to fio doc grouping.
daslib/REVIEW.md Broadens emitter-review guidance for struct/enum name emission paths.
daslib/fio.das Adds process typedef + with_process convenience wrappers.
daslib/daspkg.das Adds tools to ReleaseSpec and exposes release_include_tool.
daslib/ARCHITECTURE_EMIT.md Updates architecture notes to reflect whole-program single-TU emission.
daslib/aot_standalone.das Implements whole-program emission across modules (foreign used functions + globals init temps).
daslib/aot_cpp.das Adds foreignUsedFunctions and expands registration/used-module discovery for standalone whole-program emission.
CMakeLists.txt Updates install to ship watchdog .das sources (exe installed from utils/CMakeLists).
CLAUDE.md Adds a repo-level note about exit(N) not setting CLI exit codes.
ci/test_check_shipped_skills.py Updates shipped-skill gate test string away from Python watchdog invocation.
CHANGELIST.md Adds changelist entries describing watchdog migration, fio process API, and standalone emission behavior.
.github/workflows/extended_checks.yml Removes Python watchdog tray/consent CI step (files removed).
Review details
  • Files reviewed: 78/78 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/builtin/module_builtin_fio.cpp
Comment thread doc/source/stdlib/handmade/Variable-fio-process_running.rst Outdated
Comment thread src/builtin/module_builtin_fio.cpp Outdated
Copilot AI review requested due to automatic review settings September 6, 2026 05:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The new fio process implementation has a couple of concrete correctness/contract issues (stored as PR comments) that should be addressed before approving.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/builtin/module_builtin_fio.cpp:1884

  • On POSIX, the pipe is made non-blocking via fcntl(F_SETFL, O_NONBLOCK), which overwrites (rather than augments) the existing file status flags. Prefer F_GETFL then F_SETFL(flags | O_NONBLOCK) (and handle F_GETFL failure) to avoid unintentionally clearing other flags.
    src/builtin/module_builtin_fio.cpp:2029
  • On Windows, close_process only closes handles. If AssignProcessToJobObject failed earlier (so hJob==NULL) and the child is still running, this leaves the child alive even though the API/docs describe kill-on-close semantics. Consider a best-effort TerminateProcess fallback (when hJob is null and the process is still active) before closing the process handle.
  • Files reviewed: 78/78 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

borisbat and others added 2 commits September 5, 2026 22:34
…ch the daslib set already emits since the file moved (two objects carried impl_aot_f16_cvt and test_aot did not link), and master's test_tune_shells.das registers so the AOT sweep links it

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… points environ at it before execvp (the eastl build poisons putenv), the drain accumulator is a daslang string so it matches under eastl, CreateProcessA gets a writable command-line buffer, and the running sentinel's wording admits the one Windows collision

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 6, 2026 05:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Exception-safety and config-validation issues were found that can leak/strand supervised processes or crash the watchdog on Windows rather than failing gracefully.

Review details

Suppressed comments (3)

Previously missed (3) — in code that hasn't changed since the last review.

daslib/fio.das:702

  • with_process does not guarantee close_process runs if blk panics/throws: the call happens after invoke, so an exception path can leak the handle and leave the child running/zombie. Since this helper is advertised as the safe RAII wrapper, it should close in a finally block.
    utils/watchdog/watchdog.das:115
  • server_args is explicitly supported as a watchdog.json key, but it is currently excluded from all type validation in config_to_args and then silently ignored in config_server_args when it is not an array. That makes a mis-typed server_args (e.g. a string) fail silently even though the config loader is otherwise strict about schema mistakes.
    utils/watchdog/watchdog.das:896
  • spawn_process throws on Windows when CreateProcess fails (see builtin_spawn_process), but Supervisor.spawn() only checks for a null handle. That means a bad --program/--daslang (or a missing executable) can crash the watchdog on Windows instead of producing a spawn_failed event and a controlled exit.
  • Files reviewed: 78/78 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

…mode arms skip where daslang cannot start the JIT - on the Windows lane without LLVM.dll the -jit child died at startup, the supervisor restarted it every half second, and run() waited four hours; the discovery arm compares paths with the separators Windows joins with

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 6, 2026 10:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

utils/watchdog/watchdog.das currently treats spawn_process failure as returning null, but the builtin throws a fatal error, so spawn failures won’t be handled as intended and can abort the watchdog.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

utils/watchdog/main.das:54

  • After deleting g_supervisor, the global pointer is left dangling. If the host ever calls result() twice (or mistakenly calls tick() after result()), this becomes a use-after-free. Clearing it to null after delete makes the API safer at essentially no cost.
    utils/watchdog/watchdog.das:896
  • spawn_process failures currently can't be handled by the child == null branch: the builtin calls Context::throw_error_at(...) on error (fatal panic) and never returns null. As written, a spawn failure will abort the watchdog instead of emitting spawn_failed and returning false. Wrap the call in try { ... } recover { ... } (or remove the null-check and treat spawn failure as fatal) so the control flow matches the intended non-fatal supervisor behavior.
  • Files reviewed: 78/78 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@borisbat
borisbat merged commit 8248220 into master Sep 6, 2026
34 checks passed
@borisbat
borisbat deleted the bbatkin/fio-process-watchdog branch September 6, 2026 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants