From 393ce90fc762d67cfa2bbfe234c2b86e2a983bd3 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Wed, 29 Jul 2026 19:03:15 +1000 Subject: [PATCH] Tcl exit/finalization constraint recorded: ARCHITECTURE.md shutdown section, basis-qualified src/AGENTS.md exit-path guidance, scope index Assisted-by: harness=opencode; primary-model=openrouter/moonshotai/kimi-k3; api-location=openrouter.ai --- AGENTS.md | 2 +- ARCHITECTURE.md | 6 ++++++ src/AGENTS.md | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 59fc6312..beb37916 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -197,7 +197,7 @@ The project version is fully independent of module versions. A module bump (even - `.agents/skills/` — Project agent skills in the cross-framework Agent Skills SKILL.md format (read natively by pi, OpenCode, Codex and others). Canonical home; `.claude/skills/` holds byte-identical copies for Claude Code discovery - edit under `.agents/skills/` and re-copy, verify sync with a plain diff (no child AGENTS.md) - `GOALS.md` — Top-level technical goal index, summary-only (ID, status, title, scope, detail pointer per goal); required read for non-trivial work (no child AGENTS.md; the file documents its own format, read workflow, the agent goal-authoring workflow, and the canonical goals-system version definition) - `GOALS-archive.md` — Summary records of achieved goals moved out of the active index (historical context only) -- `ARCHITECTURE.md` - Descriptive current-state map of punkshell system mechanics (boot/launch, interp/thread topology, console abstraction, module loading, build/provenance/kits, test harness). Own lane beside DOX and the goals system: AGENTS.md files say what you must do, GOALS.md/goals say what changed and why, ARCHITECTURE.md says how it works now. Read for orientation before non-trivial work in an unfamiliar subsystem. Pointer-first and current-state only - never transcribe content owned by another doc; keep sections durable and thin. Verified by `tclsh scriptlib/developer/architecture_lint.tcl` (referenced paths exist, goal references resolve, size budget) - run it after editing the file (no child AGENTS.md; the file's front matter carries its own contract) +- `ARCHITECTURE.md` - Descriptive current-state map of punkshell system mechanics (boot/launch, interp/thread topology, console abstraction, module loading, build/provenance/kits, test harness, shutdown/finalization). Own lane beside DOX and the goals system: AGENTS.md files say what you must do, GOALS.md/goals say what changed and why, ARCHITECTURE.md says how it works now. Read for orientation before non-trivial work in an unfamiliar subsystem. Pointer-first and current-state only - never transcribe content owned by another doc; keep sections durable and thin. Verified by `tclsh scriptlib/developer/architecture_lint.tcl` (referenced paths exist, goal references resolve, size budget) - run it after editing the file (no child AGENTS.md; the file's front matter carries its own contract) - `goals/` — Per-goal detail files carrying the canonical Goal/Acceptance contract prose plus supporting detail (see goals/AGENTS.md) - `goals/archive/` — Detail files for achieved/archived goals - `TEMP_REFERENCE/` - Reference-only material for agents, not a work folder; ignored by both git and fossil, so its contents (including its AGENTS.md) are machine-local and unversioned. Holds the nested fossil checkout of the Tcl core sources at `TEMP_REFERENCE/tcl9`, which must stay on `trunk` per the fossil branch-discipline rule in User Preferences (see TEMP_REFERENCE/AGENTS.md when present) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 8619619f..c178ee7c 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -58,6 +58,12 @@ app package from src/lib/ (app-punkshell, app-punkscript, app-repl, app-shellsp - **Shared state.** Infrastructure `tsv` arrays are `punk_`-prefixed by contract (legacy unprefixed arrays predate the rule); console facts, ownership and mode caches live in tsv so every thread sees one truth (G-007). Source: `src/modules/AGENTS.md`. - **Subshells.** Named secondary repls exist; the substantial subshell roadmap is in-flux - see "In-flux areas". +## Process exit and shutdown + +- **App-level ordered teardown.** The shell does not leave thread/channel cleanup to Tcl's process finalization: the app package runs an explicit shutdown sequence while interps are fully alive - `shellthread::manager::shutdown_free_threads` terminates free workers under a bounded timeout (a wedged worker must not hang shutdown), remaining threads are released, the repl cancels its deferred stdin reader registration and drops input arriving after codethread teardown, and the codethread flushes stdout/stderr before returning results. Sources: `src/lib/app-punkshell/punkshell.tcl`, `src/modules/shellthread-999999.0a1.0.tm`, `src/modules/punk/repl-999999.0a1.0.tm`, `src/tests/shell/testsuites/punkexe/shellexit.test`. +- **Piped-stdin exit policy.** PUNK_PIPE_EOF selects terminate vs console-reopen at piped EOF (G-015, achieved) so scripted termination is deterministic rather than dependent on console-availability heuristics. Source: `src/lib/app-punkshell/punkshell.tcl`. +- **Upstream constraint: Tcl finalization order is not a documented contract.** Tcl_Finalize runs a fixed-but-undocumented subsystem order in which extension exit handlers can fire while other threads are still alive and while channels, allocators and mutexes are mid-teardown - the shape behind the G-036 tcludp worker wedge ("Interp and thread topology") and the dead-console class ("Console abstraction"). Current upstream state: trunk carries a partial fix for the busy-thread finalize crash (tcl ticket e55a589d2b, 2026-05) explicitly NOT backported to 9.0, so 9.0.x runtimes (buildsuite target 9.0.5) still carry it. Work guidance derived from this constraint: `src/AGENTS.md` "Work Guidance". Surveyed core-source detail: `TEMP_REFERENCE/AGENTS.md` surveyed-areas entry for `tcl9/` finalize path. + ## Console abstraction Two layers with a deliberate dependency direction (the class never depends on the integrating layer): diff --git a/src/AGENTS.md b/src/AGENTS.md index f3abea2d..baee91f7 100644 --- a/src/AGENTS.md +++ b/src/AGENTS.md @@ -96,6 +96,7 @@ Recovery after a wrong path guess: - When a tool summary reports cleanup issues such as `git diff --check` whitespace errors, verify against exact command output and exit code before running secondary scans or reporting the issue. - `make.tcl` performs version-aware bootsupport staleness detection: major bumps abort, minor bumps prompt (y/N; `-confirm 0` proceeds without prompting, non-interactive stdin without it aborts fast), patch bumps warn-and-proceed. See `src/bootsupport/AGENTS.md` "Bootsupport Staleness Handling" for the full contract and the version-bump discipline agents must follow. - Project-level version bumps are governed by the root `AGENTS.md` "Project Versioning" section. Any `src/` change that ships user-visible shell behaviour requires a `punkproject.toml` version bump and a `CHANGELOG.md` entry as part of the DOX closeout pass. Run `tclsh src/make.tcl projectversion` to verify. +- Exit-path work, qualified by current upstream reality (recorded 2026-07): Tcl core's exit/finalization sequence is not a documented, guaranteed order, and Tcl 9.0.x (buildsuite target 9.0.5) still carries the busy-thread finalize crash (tcl ticket e55a589d2b - partial fix is trunk-only). The responsible maintainer's stated position (2026-05/07) is that a complete fix between non-cooperating threads is not expected. The rules below match THAT reality and are not permanent policy - re-evaluate them as Tcl core finalization work lands: (a) do real cleanup while interps are fully alive - never rely on exit handlers or Tcl_Finalize ordering to finish business (the app-level ordered teardown this rule exists to protect is described in root `ARCHITECTURE.md` "Process exit and shutdown"); (b) treat any extension registering exit/thread-exit handlers (the tcludp UDP_ExitProc class, see G-036) as suspect until audited for what it closes and which threads are still alive when it fires; (c) at/after exit, assume channels are already dead (per Exit.3, I/O vanishes) and that thread-local storage may already be finalized - exit-path code must not allocate Tcl_Objs, perform channel I/O, or touch other threads' state. ## Verification