Browse Source

G-096 piperepl rebased onto 9.0.5, gate flipped to G-103 policy; tclsh90spr + tclsh90sprzip products, matrix PASS

The recovered 2024 TCLSH_PIPEREPL patch applies cleanly to current
core-9-0-branch tclMain.c (8 hunks, offset 4). Tracked as
patches/tclMain_piperepl_905.c = stock 9.0.5 + patch + gate-default flip:
ENABLED unless TCLSH_PIPEREPL=0 (2024 was opt-in set-and-not-"0") per the
G-103 punk-kit policy - the goal's Goal/Acceptance were aligned to that
policy first at user request. Era files kept unmodified as recovered
artifacts; patches/README updated (status, gate semantics, known
tcl_interactive early-link divergence when the gate is open).

build905.zig gains tclsh90spr (per-source construction mirroring tclsh90s
with both tclMain compiles - unicode + TCL_ASCII_MAIN ansi - swapped to
the patched file; stock products stay byte-for-byte unpatched) and
tclsh90sprzip (zipfs_mkimg wrap of the pr exe, -infile form). patches/
added to suite.tcl's staged recipe items. Full suite build PASS.

Behaviour matrix recorded in the goal: 9/9 stock-equivalence rows
IDENTICAL (script-arg / script-consuming-stdin / piped-commands x
unset/0/1 - open-gate plain scripts behave stock; unread piped payload
never eval'd); feature row proves buffering verbatim into
::tclsh(inputbuffer) (write-trace capture), console reopen success and
repl landing; =0 feature row identical to stock; przip variant identical
to pr - the G-103 punk-kit precursor works.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 week ago
parent
commit
5be8f2fb77
  1. 59
      goals/G-096-zig-buildsuite-piperepl.md
  2. 148
      src/buildsuites/suite_tcl90/build905.zig
  3. 24
      src/buildsuites/suite_tcl90/patches/README.md
  4. 1112
      src/buildsuites/suite_tcl90/patches/tclMain_piperepl_905.c
  5. 2
      src/buildsuites/suite_tcl90/suite.tcl

59
goals/G-096-zig-buildsuite-piperepl.md

@ -2,8 +2,8 @@
Status: active
Scope: src/buildsuites/ (suite_tcl90 new; samplesuite1 sketch reconciliation; VCS ignore flip), src/buildsuites/_build/ (untracked build/source area via existing _build globs), .gitignore + .fossil-settings/ignore-glob, src/scriptapps/bin/ (getzig scriptset rename), bin/ (regenerated punk-getzig wrapper twins, old names retired, AGENTS.md naming policy), bin/tools/ (untracked zig toolchains, existing), TEMP_REFERENCE/2024zig (read-only recovery origin)
Goal: src/buildsuites gains its first real, VCS-tracked suite (suite_tcl90) that reproducibly builds the Tcl 9.0 windows runtime from clean sources using only project-managed tooling - a punk-getzig wrapper fetching a pinned zig into untracked bin/tools, with all transient checkouts and outputs under untracked src/buildsuites/_build - and the 2024 TCLSH_PIPEREPL tclMain patch is recovered into the suite in tracked form and rebased onto current Tcl 9 sources, so a suite-built runtime can return to a standard interactive repl after a kit-style main.tcl completes with unconsumed piped input buffered rather than eval'd, while stock behaviour is preserved whenever the env gate is unset.
Acceptance: from a clean checkout plus a punk-getzig-fetched pinned zig, the suite builds tclsh90s/tclsh90szip from a fresh core-9-0-branch checkout under src/buildsuites/_build with recipe-generated tclUuid.h and tclsh.exe.manifest (no hand-placed files) and the result reports the expected patchlevel; the recovered patch set (piperepl plus the companion tclZipfs/tclCmdIL patches and a notes doc) is tracked in the suite; a piperepl-patched build with TCLSH_PIPEREPL unset behaves identically to the unpatched build on a tty/piped/script-arg matrix, and with TCLSH_PIPEREPL=1 a script setting ::tclsh(dorepl) 1 lands in a live interactive console repl after piped-input eof with the unconsumed input available in ::tclsh(inputbuffer); src/buildsuites is tracked in both VCSes with only _build content ignored and the samplesuite1 vendorbuild-prefix residue reconciled (vendorbuild concept retired); bin/AGENTS.md documents the utility naming policy (punk- prefix for punkshell-own utilities; exceptions: external-tool wrappers such as dtplite/sdx/kettle, and getpunk as the intended future cross-platform entry point) and the getzig scriptset and its bin wrapper twins (getzig.cmd/getzig.ps1) are renamed to punk-getzig with the old names removed and references updated.
Goal: src/buildsuites gains its first real, VCS-tracked suite (suite_tcl90) that reproducibly builds the Tcl 9.0 windows runtime from clean sources using only project-managed tooling - a punk-getzig wrapper fetching a pinned zig into untracked bin/tools, with all transient checkouts and outputs under untracked src/buildsuites/_build - and the 2024 TCLSH_PIPEREPL tclMain patch is recovered into the suite in tracked form and rebased onto current Tcl 9 sources, so a suite-built runtime can return to a standard interactive repl after a kit-style main.tcl completes with unconsumed piped input buffered rather than eval'd. The gate default follows the G-103 kit-family policy (2026-07-20): the patched binary is ENABLED unless TCLSH_PIPEREPL=0 (opt-out; the unpatched plain build serves those wanting stock semantics) - and even enabled it is inert for scripts that never set ::tclsh(dorepl): script-arg runs default dorepl to 0 and unconsumed piped input is buffered, not eval'd, so plain scripts behave stock.
Acceptance: from a clean checkout plus a punk-getzig-fetched pinned zig, the suite builds tclsh90s/tclsh90szip from a fresh core-9-0-branch checkout under src/buildsuites/_build with recipe-generated tclUuid.h and tclsh.exe.manifest (no hand-placed files) and the result reports the expected patchlevel; the recovered patch set (piperepl plus the companion tclZipfs/tclCmdIL patches and a notes doc) is tracked in the suite; the rebased piperepl build gates per the G-103 punk-kit policy: with TCLSH_PIPEREPL=0 it behaves identically to the unpatched build on a tty/piped/script-arg matrix, with the gate open (unset or any other value - the default) scripts that never touch ::tclsh(dorepl) still behave stock on the same matrix (script-arg runs default dorepl to 0; unconsumed piped input is buffered in ::tclsh(inputbuffer), not eval'd), and a script setting ::tclsh(dorepl) 1 lands in a live interactive console repl after piped-input eof with the unconsumed input available in ::tclsh(inputbuffer); src/buildsuites is tracked in both VCSes with only _build content ignored and the samplesuite1 vendorbuild-prefix residue reconciled (vendorbuild concept retired); bin/AGENTS.md documents the utility naming policy (punk- prefix for punkshell-own utilities; exceptions: external-tool wrappers such as dtplite/sdx/kettle, and getpunk as the intended future cross-platform entry point) and the getzig scriptset and its bin wrapper twins (getzig.cmd/getzig.ps1) are renamed to punk-getzig with the old names removed and references updated.
## Context
@ -295,14 +295,49 @@ Remaining for acceptance:
- punk-getzig per-version fetch argument, so the wrapper itself can materialize
arbitrary pinned versions into versioned dirs (payload growth needs the 512-byte
spacer check on re-wrap).
- Rebase the piperepl patch onto current core-9-0-branch sources; build a
piperepl-enabled variant; behaviour matrix (TCLSH_PIPEREPL unset == stock on
tty/piped/script-arg forms; =1 with a script setting ::tclsh(dorepl) 1 -> live
interactive console repl after piped eof with unconsumed input in
::tclsh(inputbuffer)).
KIT-DEFAULT POLICY UPDATE (2026-07-20, G-103): with a kit FAMILY the env gating
flips for punk kits - the patch is applied and ENABLED BY DEFAULT there (env
opt-OUT, e.g TCLSH_PIPEREPL=0), while the PLAIN family member carries no patch
for stock semantics. The matrix above still characterizes the patch itself on
the patched binary; restate it in opt-out terms when the variant lands.
- DONE 2026-07-20 (see the Progress increment): piperepl rebase + variant builds +
behaviour matrix. (Acceptance/Goal wording was first aligned to the G-103
kit-default policy at user request - the pre-G-103 text described the 2024
opt-in gating.) Residual user-level check: a true interactive-typing session
on tclsh90spr (tty launch behaves as the normal repl; TCLSH_PIPEREPL=0
likewise) - the automated matrix covers the piped/script-arg forms and the
console-reopen landing, not keystroke-driven use.
- Final clean-checkout re-verification of the whole flow once the above land.
### 2026-07-20 increment: piperepl rebased, gate flipped to G-103 policy, variants built + matrix PASS
- Rebase was clean: the recovered 2024 patch applies to current core-9-0-branch
(9.0.5) tclMain.c with all 8 hunks at offset 4. Tracked as
patches/tclMain_piperepl_905.c (stock + patch + gate flip + provenance banner);
the 2024 era files are kept unmodified as recovered artifacts.
- Gate default flipped per the G-103 punk-kit policy: hasPipeRepl is now
(s == NULL) || strcmp(s, "0") - ENABLED unless TCLSH_PIPEREPL=0 (2024 original
was opt-in set-and-not-"0"). Goal/Acceptance wording aligned first, at user
request, before the rebase landed.
- Products: build905.zig gains tclsh90spr (same per-source construction as
tclsh90s with both tclMain compiles - unicode + TCL_ASCII_MAIN ansi - swapped
to the patched file) and tclsh90sprzip (zipfs_mkimg wrap of the pr exe with
the tcl library attached, -infile form). Stock products remain byte-for-byte
unpatched. patches/ added to the suite.tcl staging list. Full suite build PASS
(9.0.5 checkout 1a9c3b9d96, zig 0.16.0) with the new products installed.
- Behaviour matrix (tclsh90s vs tclsh90spr; env unset / =0 / =1):
- script-arg, script-consuming-stdin, and piped-commands forms: OUTPUT+EXITCODE
IDENTICAL across all 9 combinations - including the key open-gate rows (a
plain script never touching ::tclsh(dorepl) behaves stock; piped payload a
script left unread is NOT eval'd - 'puts SHOULD-NOT-EVAL' payload never
executed).
- Feature row (script sets ::tclsh(dorepl) 1, piped payload unread, gate
open): payload lines buffered VERBATIM into ::tclsh(inputbuffer) (proven via
a write trace firing when the C code publishes the buffer: BUFFER<puts
SHOULD-NOT-EVAL / random pipe payload ()>), '? reopen stdin from console'
emitted, CONIN$ reopen succeeded and the repl landed (in an uninstrumented
run the process sits live at the prompt reading the console - observed as
the harness timeout; the trace variant exits deterministically pre-prompt).
- Same feature row with TCLSH_PIPEREPL=0: identical to stock (script-ran,
rc 0, no ::tclsh machinery).
- tclsh90sprzip: identical feature behaviour to tclsh90spr - the attached-lib
kit-style form works, i.e. the G-103 punk-kit precursor.
- Known divergence documented in patches/README.md: with the gate open,
tcl_interactive is LINKED before the startup script runs (stock links at repl
entry), so scripts that WRITE ::tcl_interactive can influence prompting where
stock ignores the write until repl entry; readers see identical values.

148
src/buildsuites/suite_tcl90/build905.zig

@ -1798,6 +1798,119 @@ pub fn build(b: *std.Build) !void {
std.debug.print("prefix {s}\n", .{b.install_path});
b.getInstallStep().dependOn(&install_tclsh.step);
//---------------------------------------------------------------------
//tclsh90spr: the TCLSH_PIPEREPL-patched shell (G-096). Identical to
//tclsh90s except tclMain.c is replaced by patches/tclMain_piperepl_905.c
//(rebased 2024 patch; gate default flipped per the G-103 punk-kit policy:
//enabled unless TCLSH_PIPEREPL=0 - see patches/README.md). Kept as a
//SEPARATE product so the stock shells stay byte-for-byte unpatched.
const tclsh_pr_exe = b.addExecutable(.{
.name = tclsh_static ++ "pr",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
tclsh_pr_exe.mingw_unicode_entry_point = true;
tclsh_pr_exe.step.dependOn(&finalstublib.step);
tclsh_pr_exe.step.dependOn(&tcl_objs_static.step);
tclsh_pr_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/generic"));
if (target.result.os.tag == .windows) {
tclsh_pr_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/win"));
tclsh_pr_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib"));
tclsh_pr_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib/contrib/minizip"));
} else {
tclsh_pr_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/unix"));
tclsh_pr_exe.root_module.addIncludePath(b.path(zlib_sourcedir));
tclsh_pr_exe.root_module.addIncludePath(b.path(zlib_sourcedir ++ "/contrib/minizip"));
}
tclsh_pr_exe.root_module.addIncludePath(b.path(tommath_sourcedir));
if (target.result.os.tag == .windows) {
//ansi copy of the patched main (see the stock shell's mainansi note above)
var pr_mainansi_flags = std.array_list.Managed([]const u8).init(b.allocator);
try pr_mainansi_flags.appendSlice(tclobjs_flags.items);
try pr_mainansi_flags.appendSlice(&.{
"-UUNICODE",
"-U_UNICODE",
"-DTCL_ASCII_MAIN",
});
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path("patches/tclMain_piperepl_905.c"),
.flags = pr_mainansi_flags.items,
});
}
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(appinit_path),
.flags = tclsh_flags.items,
});
for (tcl_objs_static_sources.items) |src| {
if (build_specials.get(std.fs.path.stem(src))) |val| {
current_flagset = val.flags;
} else {
current_flagset = tclobjs_flags;
}
if (std.mem.eql(u8, std.fs.path.stem(src), "tclMain")) {
//the unicode copy: patched file in place of stock, same special flags
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path("patches/tclMain_piperepl_905.c"),
.flags = current_flagset.items,
});
} else {
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(src),
.flags = current_flagset.items,
});
}
}
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclStubLib.c"),
.flags = stublib_flags.items,
});
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclStubCall.c"),
.flags = stublib_call_flags.items,
});
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclStubLibTbl.c"),
.flags = stublib_tbl_flags.items,
});
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclTomMathStubLib.c"),
.flags = stublib_tom_flags.items,
});
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclOOStubLib.c"),
.flags = stublib_oo_flags.items,
});
if (target.result.os.tag == .windows) {
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/tclWinPanic.c"),
.flags = winpaniclib_flags.items,
});
tclsh_pr_exe.root_module.linkSystemLibrary("netapi32", .{});
tclsh_pr_exe.root_module.linkSystemLibrary("kernel32", .{});
tclsh_pr_exe.root_module.linkSystemLibrary("user32", .{});
tclsh_pr_exe.root_module.linkSystemLibrary("advapi32", .{});
tclsh_pr_exe.root_module.linkSystemLibrary("userenv", .{});
tclsh_pr_exe.root_module.linkSystemLibrary("ws2_32", .{});
tclsh_pr_exe.root_module.addObjectFile(b.path(tcl_source_folder ++ "/libtommath/win64/libtommath.dll.a"));
}
tclsh_pr_exe.step.dependOn(&zlib_lib_compile.step);
tclsh_pr_exe.root_module.linkLibrary(zlib_lib_compile);
tclsh_pr_exe.step.dependOn(&tclvfs_compile.step); // review (parity with stock shell)
tclsh_pr_exe.root_module.link_libc = true;
tclsh_pr_exe.root_module.addWin32ResourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/tclsh.rc"),
.flags = &.{
"/I",
tcl_source_folder ++ "/generic",
},
});
const install_tclsh_pr = b.addInstallArtifact(tclsh_pr_exe, .{});
b.getInstallStep().dependOn(&install_tclsh_pr.step);
//---------------------------------------------------------------------
//OPTIONAL steps
const install_tzdata_step = b.step("install-tzdata", "install tcl timezone data");
@ -2045,6 +2158,41 @@ pub fn build(b: *std.Build) !void {
make_zipfs.dependOn(&install_mkimg.step);
// ** *** ***
// ** *** *** mkimg for the piperepl shell -> tclsh90sprzip(.exe)
//Same attached-library wrap as tclsh90szip, but the prefix executable is the
//patched shell, named explicitly via -infile (the stock wrap uses the calling
//interp as its own prefix on windows).
var mkimg_pr_run: *std.Build.Step.Run = undefined;
if (target.result.os.tag == builtin.os.tag) {
mkimg_pr_run = b.addRunArtifact(tclsh_exe);
} else {
//require an existing tclsh90 - with zipfs support on the path for x-builds
mkimg_pr_run = b.addSystemCommand(&.{"tclsh90"});
}
mkimg_pr_run.addFileArg(b.path("tools/zipfs_mkimg.tcl"));
mkimg_pr_run.addArg("-outfile");
var out_mkimg_pr: std.Build.LazyPath = undefined;
if (target.result.os.tag == .windows) {
out_mkimg_pr = mkimg_pr_run.addOutputFileArg("tclsh90sprzip.exe");
} else {
out_mkimg_pr = mkimg_pr_run.addOutputFileArg("tclsh90sprzip");
}
mkimg_pr_run.addArg("-indir");
mkimg_pr_run.addDirectoryArg(tcl_library_path.dirname());
mkimg_pr_run.addArg("-strip");
mkimg_pr_run.addDirectoryArg(tcl_library_path.dirname());
mkimg_pr_run.addArg("-infile");
mkimg_pr_run.addArtifactArg(tclsh_pr_exe);
var install_mkimg_pr: *std.Build.Step.InstallFile = undefined;
if (target.result.os.tag == .windows) {
install_mkimg_pr = b.addInstallFileWithDir(out_mkimg_pr, .prefix, "bin/tclsh90sprzip.exe");
} else {
install_mkimg_pr = b.addInstallFileWithDir(out_mkimg_pr, .prefix, "bin/tclsh90sprzip");
}
install_mkimg_pr.step.dependOn(&mkimg_pr_run.step);
make_zipfs.dependOn(&install_mkimg_pr.step);
// ** *** ***
//****************************
const thread_lib_compile = try build_tclthread(tcl_source_folder, "../tclthread", b, target, optimize, finalstublib);

24
src/buildsuites/suite_tcl90/patches/README.md

@ -5,8 +5,10 @@ Origin: `c:/buildtcl/2024zig/build_tcl90/patch/` (reference snapshot
(Aug 2024; tclZipfs/tclCmdIL Nov 2024). Recovered into tracked form 2026-07-20 - these
files previously existed only in those two VCS-ignored on-disk copies.
NONE of these are applied by the suite's default build: `suite.tcl build` compiles
stock upstream sources. Rebasing/applying is per-patch opt-in work.
The tclZipfs/tclCmdIL patches are NOT applied by any build. The piperepl capability
IS built by default since 2026-07-20 - as SEPARATE products (`tclsh90spr`,
`tclsh90sprzip`) compiled from the rebased `tclMain_piperepl_905.c`; the stock
`tclsh90s`/`tclsh90szip` products continue to compile upstream sources unpatched.
## tclMain.c.piperepl.patch / tclMain_piperepl.c (the TCLSH_PIPEREPL capability)
@ -29,9 +31,21 @@ enabled:
windows, `/dev/tty` elsewhere) and installed as the std channel, yielding a live
standard repl after a pipeline ends.
Status: NOT yet rebased onto current Tcl 9 sources. Note the punkshell-level analogue
for the punk repl is G-038 (piped-to-interactive session continuity) - this patch is
the plain-tclsh/kit-runtime counterpart.
Status: REBASED 2026-07-20 as `tclMain_piperepl_905.c` (stock 9.0.5 tclMain.c + this
patch, which applied cleanly at offset 4, + the gate-default flip). GATE SEMANTICS
CHANGED from the 2024 original per the G-103 punk-kit policy: the patched binary is
ENABLED unless `TCLSH_PIPEREPL=0` (2024 was opt-in via set-and-not-"0"). Even
enabled it is inert for scripts that never set `::tclsh(dorepl)` - script-arg runs
default dorepl to 0, and unconsumed piped input is buffered rather than eval'd. The
stock-behaviour escape hatches are `TCLSH_PIPEREPL=0` and the unpatched plain
products. Era files (`tclMain_piperepl.c`, the `.patch`) are kept unmodified as the
recovered 2024 artifacts. Known divergence when the gate is OPEN: `tcl_interactive`
is linked to the internal tty flag BEFORE the startup script runs (stock links it
only when entering the repl loop), so a script that WRITES ::tcl_interactive
influences prompting behaviour in the patched shell where stock would ignore the
write until repl entry - scripts that merely read it see identical values. Note the
punkshell-level analogue for the punk repl is G-038 (piped-to-interactive session
continuity) - this patch is the plain-tclsh/kit-runtime counterpart.
## tclZipfs.patch, tclCmdIL.patch

1112
src/buildsuites/suite_tcl90/patches/tclMain_piperepl_905.c

File diff suppressed because it is too large Load Diff

2
src/buildsuites/suite_tcl90/suite.tcl

@ -233,7 +233,7 @@ proc fossil_source {name url branch dir} {
set builddir [file join $stage build]
log "staging recipe -> $builddir"
file mkdir $builddir
foreach item {build905.zig build_libtommath build_zlib build_tclvfs build_tclthread build_tk src tools} {
foreach item {build905.zig build_libtommath build_zlib build_tclvfs build_tclthread build_tk patches src tools} {
file delete -force [file join $builddir $item]
file copy [file join $suiteroot $item] $builddir
}

Loading…
Cancel
Save