Browse Source

G-101 increment 1: suite_tcl86 consumes the G-153 metakit records

Land the ready-to-land fork records in both flows and build the mk4
candidate's loadable shape as a suite battery:
- sources.config: source metakit git (gitea jn/metakit) pinned at
  6a92b479 (the G-153-verified tree); matching build.zig.zon .metakit
  lazy record (url+hash re-verified live via zig fetch at landing).
- build_mk4tcl86/: new extension module (build_tclvfs86 conventions) -
  engine static lib (q4_UNIV set, -fno-exceptions -fno-rtti) + Mk4tcl.dll
  (mk4tcl.cpp + bundled mk4too.cpp, USE_TCL_STUBS + BUILD_Mk4tcl) against
  the suite stub lib; package version parsed from the staged
  Tcl_PkgProvide; installs lib/mk4tcl2.4.9.8/{Mk4tcl.dll,pkgIndex.tcl}.
- build86.zig: build-mk4tcl step + install hook, pkg_smoke gains Mk4tcl,
  bootstrapMode source_dirs/recipe_items rows; suite.tcl required-source
  + staged recipe item lists; README deliverable + selection records.
- goal detail: Progress increment record with the verification transcript
  facts.

VERIFIED: suite.tcl build -steps {install install-libraries smoke} clones
the fork from the record, builds the dll, and pkg_smoke reports
Mk4tcl:2.4.9.8 in BOTH shells (tclsh86t dynamic AND tclsh86ts static).

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 week ago
parent
commit
de59ecc626
  1. 33
      goals/G-101-tcl86-kit-container-strategy.md
  2. 22
      src/buildsuites/suite_tcl86/README.md
  3. 9
      src/buildsuites/suite_tcl86/build.zig.zon
  4. 13
      src/buildsuites/suite_tcl86/build86.zig
  5. 125
      src/buildsuites/suite_tcl86/build_mk4tcl86/build_mk4tcl86.zig
  6. 7
      src/buildsuites/suite_tcl86/sources.config
  7. 4
      src/buildsuites/suite_tcl86/suite.tcl

33
goals/G-101-tcl86-kit-container-strategy.md

@ -204,6 +204,39 @@ becomes this container's payload.
(win32-x86_64 + win32-ix86), so the ix86 arm of whatever is decided here is
buildable from day one.
## Progress
Increment 1 landed 2026-08-02: the G-153 ready-to-land records are CONSUMED -
suite_tcl86 now builds the mk4 candidate's loadable shape from the pinned fork.
- sources.config `source metakit git ... 6a92b479... metakit` + build.zig.zon
`.metakit` record (both flows carry the same fork identity; the zon url+hash
pair re-verified live at landing - `zig fetch` under the pinned zig prints
exactly the recorded hash).
- build_mk4tcl86/build_mk4tcl86.zig (new module, build_tclvfs86 conventions):
the storage engine as its own static lib (upstream CMake q4_UNIV source set,
-fno-exceptions -fno-rtti) + Mk4tcl.dll (mk4tcl.cpp + bundled mk4too.cpp,
USE_TCL_STUBS + BUILD_Mk4tcl) linked against the suite's libtclstub86;
package version parsed from the staged mk4tcl.cpp Tcl_PkgProvide (never
hardcoded); installs lib/mk4tcl2.4.9.8/{Mk4tcl.dll,pkgIndex.tcl} onto the
default install step. Fork facts carried in the module header; the fork's
own build.zig mk4tcl step is the reference recipe (keep the two in step).
- build86.zig wiring: build-mk4tcl step, pkg_smoke gains Mk4tcl (both shells),
bootstrapMode source_dirs + recipe_items rows; suite.tcl required-source
list + staged recipe item list.
- VERIFIED (dev flow, 2026-08-02): `suite.tcl build -steps {install
install-libraries smoke}` cloned the fork from the gitea record and checked
out the pin, built and installed the dll, and pkg_smoke reports
Mk4tcl:2.4.9.8 in BOTH shells (tclsh86t dynamic AND tclsh86ts static)
alongside Thread/vfs/registry/dde/Tk.
Remaining for acceptance: the candidate-container assessments recorded against
the kit expectations (mk4 / cookfs / zipvfs-over-tclvfs, with the
zipfs-backport arm's bearing); one strategy demonstrated end-to-end (kit
wrapped from the suite-built runtime boots with no installed Tcl and runs a
script from inside its vfs, wrapping a SEPARATE copy per the self-mount
rider); decision + rationale + follow-on-to-parity record in this file.
## Reference material (TEMP_REFERENCE, machine-local)
Recorded 2026-07-26 from an agent survey. CAVEAT: TEMP_REFERENCE is ignored by

22
src/buildsuites/suite_tcl86/README.md

@ -85,6 +85,18 @@ ACTIVE - see `goals/G-100-suite-tcl86-tk-tcllib.md` for the progress ledger.
(suite.tcl `-zigargs {-Dlibrev=N}`; default 1). Publication to the real
punkbin repo remains a deliberate user step.
## Deliverables (G-101 kit-container work)
- Mk4tcl **2.4.9.8** (`lib/mk4tcl2.4.9.8/Mk4tcl.dll` + generated pkgIndex.tcl,
mk4too bundled - it is a hard link dependency and the oomk consumer needs it):
the mk4 container-candidate engine + `mk::*` binding, built from the
punkshell-maintained metakit_punk fork by `build_mk4tcl86/` (engine
-fno-exceptions/-fno-rtti, binding a stubs consumer against
`lib/libtclstub86.a`). `package require Mk4tcl` proven in BOTH shells by the
`smoke` step's pkg_smoke. Kit boot machinery (kitInit/boot.tcl/setupvfs
analogs) is deliberately NOT here yet - that is G-101's open investigation
(see `goals/G-101-tcl86-kit-container-strategy.md`).
## Sources (both flows carry the same identity)
- Dev flow: `sources.config` (live refs; `suite.tcl build` stages checkouts
@ -118,6 +130,16 @@ Selections (2026-07-26, G-100 dependency set):
(`lib/critcl/critcl_c/tcl8.6/` here), which is why one config file serves
both generations.
Selections (2026-08-02, G-101 kit-container work):
- metakit: the punkshell-maintained fork (gitea `jn/metakit`; project name
metakit_punk, pooryorick 2.4.9.8 porting baseline) pinned by commit
6a92b479 - the G-153-verified tree (standalone build, regress, cross-arch
interchange and dll smokes all ran at exactly that commit; see
`goals/archive/G-153-metakit-punk-zig-port.md`). No tags upstream yet, so
commit-pinned like punkzip; both flows carry the same identity (the zon
hash was re-verified live when the records landed).
## DERIVATION (build86.zig from the staged 8.6.18 win/makefile.vc)
Recorded findings from the staged tree (assess-not-assume items from the G-099

9
src/buildsuites/suite_tcl86/build.zig.zon

@ -81,6 +81,15 @@
.hash = "punkzip-2.3.1-PjFoDUNlgwCyYfz8k0-SNGxaXsh53YWTXv5f9JRRXQK8",
.lazy = true,
},
//metakit_punk fork commit 6a92b479 (no tags yet) - the G-153-verified
//tree: mk4 engine + Mk4tcl/mk4too binding for the G-101 kit-container
//work. Hash computed with the pinned zig 0.16.0 (2026-08-02, recorded
//at the G-153 pin capture - goals/archive/G-153-metakit-punk-zig-port.md).
.metakit = .{
.url = "https://www.gitea1.intx.com.au/jn/metakit/archive/6a92b47955c986e8d04db0a3f9555710482bc56d.tar.gz",
.hash = "metakit_punk-0.1.0-fjiV29tTbQAB6kfuzK76YFyu7-HYnwGJKwNlMFyBTECZ",
.lazy = true,
},
},
.paths = .{""},
}

13
src/buildsuites/suite_tcl86/build86.zig

@ -57,6 +57,7 @@ const build_zlib86 = @import("build_zlib86/build_zlib86.zig").build_zlib86;
const build_tclthread86 = @import("build_tclthread86/build_tclthread86.zig").build_tclthread86;
const tclvfs86 = @import("build_tclvfs86/build_tclvfs86.zig");
const tk86 = @import("build_tk86/build_tk86.zig");
const mk4tcl86 = @import("build_mk4tcl86/build_mk4tcl86.zig");
const tcl_dot_version = "8.6";
const tcl_nodot_version = "86";
@ -617,6 +618,13 @@ pub fn build(b: *std.Build) !void {
build_tclvfs_step.dependOn(&tclvfs_compile.step);
try tclvfs86.install_tclvfs_package("../tclvfs", b, tclvfs_compile);
//-- Mk4tcl (G-101): the metakit_punk fork's engine + mk::* binding as a
//suite battery - the mk4 container candidate's loadable shape (lib/mk4tcl<v>)
const mk4tcl_compile = try mk4tcl86.build_mk4tcl86(tcl_source_folder, "../metakit", b, target, optimize, finalstublib);
const build_mk4tcl_step = b.step("build-mk4tcl", "build the Mk4tcl loadable extension (metakit_punk fork; includes mk4too)");
build_mk4tcl_step.dependOn(&mk4tcl_compile.step);
try mk4tcl86.install_mk4tcl_package("../metakit", b, mk4tcl_compile);
//-- Tk 8.6 (G-100): tk86t.dll + the tk script library + pkgIndex, all hooked
//onto the default install step inside the helper (as the companions above).
const tk_build = try tk86.build_tk86(tcl_source_folder, "../tk86", b, target, optimize, finalstublib);
@ -696,7 +704,7 @@ pub fn build(b: *std.Build) !void {
pkgs.has_side_effects = true;
pkgs.setName(b.fmt("pkg_smoke {s}", .{sr.label}));
pkgs.addFileArg(b.path("tools/pkg_smoke.tcl"));
pkgs.addArgs(&.{ "-gui", "1", "Thread", "vfs", "registry", "dde", "Tk" });
pkgs.addArgs(&.{ "-gui", "1", "Thread", "vfs", "registry", "dde", "Tk", "Mk4tcl" });
pkgs.step.dependOn(b.getInstallStep());
pkgs.step.dependOn(install_libraries);
smoke_step.dependOn(&pkgs.step);
@ -1119,6 +1127,8 @@ fn bootstrapMode(b: *std.Build) !void {
.{ "critcl", "critcl" },
//G-138 library-artifacts
.{ "punkzip", "punkzip" },
//G-101 kit-container work: the metakit_punk fork (engine + Mk4tcl binding)
.{ "metakit", "metakit" },
};
var deps_pending = false;
for (source_dirs) |sd| {
@ -1157,6 +1167,7 @@ fn bootstrapMode(b: *std.Build) !void {
"build_tclthread86",
"build_tclvfs86",
"build_tk86",
"build_mk4tcl86",
"critcl_zig.config",
"expected_test_failures.txt",
"expected_test_failures_thread.txt",

125
src/buildsuites/suite_tcl86/build_mk4tcl86/build_mk4tcl86.zig

@ -0,0 +1,125 @@
const std = @import("std");
const common = @import("../build_common.zig");
//build_mk4tcl86 - Mk4tcl (metakit_punk fork) loadable extension for the 8.6
//suite (G-101). The metakit port itself lives in the fork-as-upstream repo
//(gitea jn/metakit; G-153 - built, regress-tested and smoke-verified standalone
//under the pinned zig, win32-x86_64 + win32-ix86). This module re-expresses the
//fork's own 'mk4tcl' build step in suite shape (build_tclvfs86 conventions:
//staged source tree beside the recipe, the suite's stub lib, install hooked onto
//the default step) so the suite's kit-container work consumes suite-built bytes.
//
//Fork facts carried over (decision records: metakit fork src/AGENTS.md +
//build.zig - keep the two recipes in step):
// - engine sources = upstream CMake metakit_SOURCES (q4_UNIV default set:
// univ.cpp in, std.cpp out), compiled -fno-exceptions -fno-rtti (the ported
// core is exception- and RTTI-free); mk4tcl.cpp keeps exceptions ON (real
// try/catch).
// - mk4too.cpp is bundled: a hard link dependency of mk4tcl.cpp (mk::view
// open/new construct MkView unguarded) and the oomk consumer needs it -
// matching both upstream builds (CMake and KitCreator).
// - -fno-sanitize=undefined/-fno-sanitize-trap=undefined everywhere: zig cc's
// default trapping UBSan fires on 2007-era formal UB (suite tcl/tk/zlib
// precedent; first site derived.cpp:308).
// - BUILD_Mk4tcl gates TCL_STORAGE_CLASS = DLLEXPORT in mk4tcl.h (TEA form);
// USE_TCL_STUBS for the stubs contract (proven cross-patchlevel and
// cross-CRT in the G-153 smokes).
// - dll name stays Mk4tcl.dll (fork/upstream-recognizable, no TEA-nmake
// vfs142t-style renaming): unlike tclvfs there is no script loading
// @PKG_LIB_FILE8@ by guessed init name - the generated pkgIndex passes the
// init name explicitly ('load ... Mk4tcl'; load-name guessing stops at
// digits, the historical Mk_Init quirk).
//upstream src/CMakeLists.txt metakit_SOURCES (q4_UNIV default set) - same list
//as the fork build.zig core_sources
const core_sources = [_][]const u8{
"column.cpp", "custom.cpp", "derived.cpp", "field.cpp", "fileio.cpp",
"format.cpp", "handler.cpp", "persist.cpp", "remap.cpp", "store.cpp",
"string.cpp", "table.cpp", "univ.cpp", "view.cpp", "viewx.cpp",
};
const engine_flags = [_][]const u8{
"-fno-exceptions",
"-fno-rtti",
"-fno-sanitize=undefined",
"-fno-sanitize-trap=undefined",
};
const binding_flags = [_][]const u8{
"-DUSE_TCL_STUBS=1",
"-DBUILD_Mk4tcl",
"-fno-sanitize=undefined",
"-fno-sanitize-trap=undefined",
};
//package version fact from the staged fork tree (never hardcoded): the
//Tcl_PkgProvide call in mk4tcl.cpp is the authority for what the built dll
//actually provides ('package provide stays 2.4.9.8' is the fork's recorded
//version policy - the upstream metakit version, not a fork-local number).
pub fn packageVersion(b: *std.Build, comptime subdir: []const u8) []const u8 {
const src = common.readSourceFile(b, subdir ++ "/src/tcl/mk4tcl.cpp");
const marker = "Tcl_PkgProvide(interp, \"Mk4tcl\", \"";
const at = std.mem.indexOf(u8, src, marker) orelse @panic("Tcl_PkgProvide(\"Mk4tcl\", ...) not found in mk4tcl.cpp (staged metakit tree mismatch?)");
const rest = src[at + marker.len ..];
const end = std.mem.indexOfScalar(u8, rest, '"') orelse @panic("unterminated Tcl_PkgProvide version string in mk4tcl.cpp");
return b.dupe(rest[0..end]);
}
//install the package dir (lib/mk4tcl<ver>, suite convention beside
//lib/thread2.8.13 and lib/vfs1.4.2): the dll + a generated pkgIndex.tcl
pub fn install_mk4tcl_package(comptime subdir: []const u8, b: *std.Build, mk4tcl_compile: *std.Build.Step.Compile) !void {
const ver = packageVersion(b, subdir);
const pkgsub = b.fmt("lib/mk4tcl{s}", .{ver});
const dll_inst = b.addInstallFileWithDir(mk4tcl_compile.getEmittedBin(), .prefix, b.fmt("{s}/Mk4tcl.dll", .{pkgsub}));
b.getInstallStep().dependOn(&dll_inst.step);
const wf = b.addWriteFiles();
const pkgindex = wf.add("pkgIndex.tcl", b.fmt(
\\package ifneeded Mk4tcl {s} [list load [file join $dir Mk4tcl.dll] Mk4tcl]
\\
, .{ver}));
const inst_pkgindex = b.addInstallFileWithDir(pkgindex, .prefix, b.fmt("{s}/pkgIndex.tcl", .{pkgsub}));
b.getInstallStep().dependOn(&inst_pkgindex.step);
}
pub fn build_mk4tcl86(comptime tcldir: []const u8, comptime subdir: []const u8, b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, stublib: *std.Build.Step.Compile) !*std.Build.Step.Compile {
//the Tcl-agnostic storage engine as its own static lib so the
//exceptions-off/rtti-off flag set stays separate from the binding's
const engine = b.addLibrary(.{
.linkage = .static,
.name = "mk4",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
});
engine.root_module.addIncludePath(b.path(subdir ++ "/src/include"));
inline for (core_sources) |nm| {
engine.root_module.addCSourceFile(.{
.file = b.path(subdir ++ "/src/core/" ++ nm),
.flags = &engine_flags,
});
}
engine.root_module.link_libcpp = true;
const mk4tcl = b.addLibrary(.{
.linkage = .dynamic,
.name = "Mk4tcl",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
});
mk4tcl.root_module.addIncludePath(b.path(subdir ++ "/src/include"));
mk4tcl.root_module.addIncludePath(b.path(subdir ++ "/src/core")); //univ.h (fork-edit note in the fork's mk4tcl.h)
mk4tcl.root_module.addIncludePath(b.path(tcldir ++ "/generic"));
inline for (.{ "mk4tcl.cpp", "mk4too.cpp" }) |nm| {
mk4tcl.root_module.addCSourceFile(.{
.file = b.path(subdir ++ "/src/tcl/" ++ nm),
.flags = &binding_flags,
});
}
mk4tcl.root_module.linkLibrary(engine);
mk4tcl.root_module.linkLibrary(stublib);
mk4tcl.root_module.link_libcpp = true;
return mk4tcl;
}

7
src/buildsuites/suite_tcl86/sources.config

@ -42,6 +42,7 @@
description Zig-built Tcl 8.6 windows runtime (core-8-6-branch): tclsh86ts/tclsh86t shells + tcl86t.dll + installed lib tree, thread 2.8 + tclvfs + Tk 8.6 + tklib/tcllib(+tcllibc), core-test gate (G-099/G-100 arc)
product tclsh86ts.exe static shell + tclsh86t.exe/tcl86t.dll dynamic pair with installed lib/tcl8.6 tree + tm modules (no zipfs in 8.6 - no self-contained szip runtime; kit containment is G-101 territory)
product thread 2.8.13 (thread2813t.dll) and tclvfs 1.4.2 (vfs142t.dll) + registry/dde package dlls, all loadable in both shells
product Mk4tcl 2.4.9.8 (Mk4tcl.dll, metakit_punk fork, mk4too bundled) loadable in both shells - the mk4 container engine for the G-101 kit work
product Tk 8.6.17 (tk86t.dll + lib/tk8.6 script library) with tklib 0.9 and tcllib 2.0 installed by their own installers, plus tcllibc critcl accelerators built with zig cc
product punkbin lib-tier library artifacts (G-138): tcllib/tcllibc -tcl8-r<N> zips with embedded schema-v2 records - out/library/punkbin/lib
product core + thread + tclvfs testsuite gates vs tracked expected_test_failures baselines (parsed all.tcl totals), tcllib/tklib record tiers and an opt-in test-tk
@ -64,3 +65,9 @@ source critcl git https://github.com/andreas-kupries/critcl 3.3.1 critc
# for the library-artifacts step. Generation-neutral (same pin as suite_tcl90);
# pinned by commit (v2.3.1; the repo carries no tags). Fetched lazily like critcl.
source punkzip git https://www.gitea1.intx.com.au/jn/punkzip 0882f0373eb2c983142e713c814b91aa82cfcea7 punkzip
# G-101: metakit_punk (punkshell-maintained fork of Metakit, pooryorick 2.4.9.8
# baseline) - the mk4 container engine + Mk4tcl/mk4too binding for the 8.6 kit
# work. Pinned by commit like punkzip (the fork carries no tags yet); the pin is
# the G-153-verified tree (build/regress/interchange/dll-smoke all run at it).
source metakit git https://www.gitea1.intx.com.au/jn/metakit 6a92b47955c986e8d04db0a3f9555710482bc56d metakit

4
src/buildsuites/suite_tcl86/suite.tcl

@ -310,13 +310,13 @@ proc git_source {name url ref dir} {
#recipe_items (build86.zig bootstrapMode).
log "staging recipe -> $builddir"
file mkdir $builddir
foreach item {build86.zig build_common.zig build_zlib86 build_tclthread86 build_tclvfs86 build_tk86 critcl_zig.config expected_test_failures.txt expected_test_failures_thread.txt expected_test_failures_tclvfs.txt src tools} {
foreach item {build86.zig build_common.zig build_zlib86 build_tclthread86 build_tclvfs86 build_tk86 build_mk4tcl86 critcl_zig.config expected_test_failures.txt expected_test_failures_thread.txt expected_test_failures_tclvfs.txt src tools} {
file delete -force [file join $builddir $item]
file copy [file join $suiteroot $item] $builddir
}
#-- sources ---------------------------------------------------------------
foreach required {tcl tclthread tclvfs tk tklib tcllib critcl punkzip} {
foreach required {tcl tclthread tclvfs tk tklib tcllib critcl punkzip metakit} {
if {![dict exists $sources $required]} {
fail "sources.config: missing required source record '$required'"
}

Loading…
Cancel
Save