27 KiB
G-127 Cross-target bake of a custom .vfs
Status: achieved 2026-07-31 Scope: src/make.tcl (target-keyed output location and deploy folder, kit-name disambiguation by target, payload/target mismatch reporting, retirement of the kit-loop cross-platform TODO); src/vfs/*.vfs.toml (per-target payload declaration - the achieved G-115 surface this goal adds the platform axis to); src/runtime/mapvfs.toml (one vfs definition paired with several targets); src/vfs/ (per-target payload convention with punk9linux.vfs as the migration case); bin/kits// (non-native kit output tier, beside the existing bin/runtime// input tier); src/tests/shell/testsuites/punkexe/ (characterization - two-target bake, no-collision, mismatch report); bin/AGENTS.md + src/AGENTS.md (where cross-target kits land) Goal: one kit definition bakes for several declared target platforms from a single source of truth - each target getting ITS binary payload and an output location that cannot be overwritten by another target's kit of the same name - so building a custom .vfs for this machine and for FreeBSD is a declaration, not a duplicated .vfs folder and a naming convention held in the developer's head. G-122 made the build read the right runtime for a target; this makes it write the right kit for one. Acceptance: two mapvfs entries pairing the SAME vfs definition with two different targets bake in one run to distinct artifacts - a native-target kit stays at bin/ exactly where it lands today, a non-native one at bin/kits// with src/_build separated the same way - and neither is overwritten when the two are baked as separate selective single-kit runs (the current defect: exe_names_seen resets per run, so the second write lands on the first); two NON-NATIVE targets sharing a kit name coexist as distinct artifacts, where today they collide or are disambiguated by runtime name rather than by platform; the binary payload installed into each target's kit is selected for that target from src/vendorlib_tcl/ through one declaration rather than by duplicating a .vfs folder per target, and a kit declared for a single target builds byte-comparably to today; a payload binary that would be LOADED on the kit's target but was built for another platform is reported naming the file and both platforms, while per-platform subdirectory layouts that the loader resolves correctly (e.g tcllib's //) are explicitly not flagged; bakelist shows each row's output location; the existing punk9linux.vfs is either migrated to the declaration or recorded here as deliberately staying hand-curated, and the linux punkshell902 kit's move from bin/ to bin/kits/linux-x86_64/ is recorded as an intended relocation; the G-023 reconciliation is stated (version-named outputs compose with the platform directory rather than competing with it); native win32 kit outputs are unchanged in name and location and the existing punkexe suites pass unchanged; the kit-loop TODO at src/make.tcl "allow building of kits for other platforms" is retired pointing here.
Context
G-122 (achieved - see goals/archive/G-122-host-target-platform-split.md) made the build
READ correctly for a target: which bin/runtime/<tier> a runtime comes from, its
filename suffix, the artifact's suffix, presence checks, process-sweep applicability. It
deliberately did not touch the output side. make.tcl still carries the standing TODO in
the kit loop, of which G-122 satisfied only the second half:
#TODO - allow building of kits for other platforms
# - we need to use <project>/bin for only kits targetting current platform,
# and use <project>/bin/<platform> for others
# that we we can have same target executable for multiple platforms
# - to do this we need to change runtime/mapvfs.config to have platform names <-- done
Three gaps remain, all measured 2026-07-26:
-
Output is flat.
src/_build/<name>andbin/<name>, no platform dimension.targetkit = <appname><suffix>and both linux-x86_64 and freebsd-x86_64 yield an empty suffix, so two POSIX targets of the same kit collide. Within one run the duplicate guard renames the second to<appname>_<runtimename>- RUNTIME-keyed, not platform-keyed. Across separate SELECTIVE runsexe_names_seenstarts empty, so the second simply overwrites the first in bothsrc/_buildandbin. win32-vs-posix is safe today only because of the.exesuffix - naming luck, not design, and the reason the linuxpunkshell902currently coexists withpunk91.exewithout trouble. -
The payload model is one hand-curated folder per target.
punk9linux.vfsIS the convention: a separate folder carrying linux.sofiles (tcllibc, tcltls, tdom, thread). It is not a vfslibs participant, so its lib tree is maintained by hand.vendorlib_vfs.tomlhardcodes the platform into the source path (source = "vendorlib_tcl9/win32-x86_64/tcludp1.0.13") with explicit per-vfs target lists - a shape inherited from G-037 (achieved 2026-07-08), which established "which vfs folders participate is explicitly declared per kit" back when there was only one target. So "bake the same custom .vfs here and on FreeBSD" is not expressible; you duplicate the folder and re-copy the pure-Tcl payload. Two mitigating facts:_vfscommon.vfs(merged into every kit) carries no platform binaries, so the common payload is already target-agnostic - the problem is confined to the kit-specific vfs. And leakage runs both ways:punk9linux.vfscurrently containstcllib2.0/md5c/win32-x86_64/md5c.dllandtcllib2.0/tcllibc/win32-x86_64/tcllibc.dll, harmless (the loader picks by platform dir) but dead weight from a wholesale tree copy. -
Nothing verifies payload against target. The cross-target
punkshell902bake produced a valid ELF, but the build would not have complained had that vfs been full of DLLs.
The runtime side is already in place for a FreeBSD experiment: bin/runtime/freebsd-x86_64/
exists locally (empty but for sha1sums.txt) and punkbin upstream carries
freebsd-x86_64/tclkit-851-freebsd7-x86_64, fetchable via
punk-runtime.cmd fetch -platform freebsd-x86_64.
Approach
- Adopt the developer's recorded split, with the directory named explicitly:
native-target kits stay at
bin/<name>(every launcher, test and habit expectsbin/punk91.exethere), non-native kits go tobin/kits/<platform>/<name>.bin/kits/rather thanbin/<platform>/becausebin/runtime/<platform>/already means build INPUTS - having outputs in a sibling shape one level up would read as the same thing. No ignore-rule work:/bin/*already coversbin/kits/(verified), so neither.gitignorenor the fossil ignore-glob needs a change. - Supply the PLATFORM AXIS to whatever payload-declaration mechanism exists at the time
(today
vendorlib_vfs.toml's explicit per-kit lists; G-115's toml if it lands first). This goal does not own the declaration FORMAT - it owns the requirement that a payload can be declared per target and selected by the kit's target. - Keep the mismatch check load-path-relevant: report a binary that would actually be LOADED on the target, and stay silent on per-platform subdirectory layouts the loader resolves correctly. A check that fires on every existing tree is noise, and the current trees have benign leakage.
Alternatives considered
- Put ALL kits under
bin/kits/<platform>/, including native - rejected: breaks every launcher, test, doc and habit that expectsbin/punk91.exe, for uniformity's sake. - Keep one .vfs folder per target and fix only the output collision - rejected: leaves in place exactly the duplication that motivates the goal.
- Make a payload/target mismatch a hard build failure - rejected as the DEFAULT: the current tree would fail immediately on known-benign leakage. Offer it as a strict flag.
- Disambiguate colliding names by runtime rather than by platform (extending today's
<appname>_<runtimename>behaviour) - rejected: it encodes the wrong axis, and it does nothing for the cross-run overwrite, which is the sharper half of the defect.
Notes
- Depends on: G-122 (achieved) - the input half; this is the output half of the same split, and retires the TODO that goal left standing.
- Related: G-125 (achieved 2026-07-27 - see goals/archive/G-125-unbootable-kit-deploy-gate.md; pointer pushed at its archive sweep, where the pair were each other's only recorded bridge). It added a boot-precondition gate that refuses a kit BEFORE any build product is written - immediately after the vfs merges, before the kit-type assembly switch. Two consequences for this goal: whatever output location it settles on for non-native kits (bin/kits//) inherits the never-written guarantee for free as long as the gate stays upstream of the artifact write, and the payload/target mismatch reporting this goal adds should read as one vocabulary with the gate's refusal rather than a second, differently-worded failure surface. The gate is deliberately non-executing precisely so it covers cross-target kits this host could never run.
- Related: G-115 (achieved 2026-07-31 - see goals/archive/G-115-declarative-vfs-composition.md) - owned the payload DECLARATION FORMAT, which is now live as per-.vfs files (src/vfs/.vfs.toml, [payload.*] entries with source/source_root/target/supersedes/replace) deliberately WITHOUT a platform axis. This goal lands second and adopts that format: it supplies the axis (per-target payload entries in those files), the format supplies the surface.
- Related: G-023 - owns output NAMES and its Acceptance hardcodes
punk9-0.5.0.exe. This goal owns output LOCATION and the suffix; the two must compose (bin/kits/<platform>/punk9-0.5.0) rather than compete. - Related: G-114 - the same platform axis one layer down (per-platform tm module roots registered by the boot for the running platform). A cross-target kit's tm roots must be the TARGET's. Note the xref shows G-114 and G-115 unlinked despite overlapping: the platform axis and the payload axis have never been connected, and this goal is that connection.
- Related: G-024 (achieved 2026-07-31 - see goals/archive/G-024-mapvfs-toml.md) - the mapvfs.toml format home for expressing one vfs definition against several targets is live (per-kit [kit.] tables each carrying their own target already allow one vfs under several targets; the output-collision half stays this goal's).
- Related: G-057 (achieved 2026-07-29, archived - see
goals/archive/G-057-kit-icon-embedding.md) - icon embedding is a
windows-target-only wrap step keyed off a per-vfs override, so "which kits
get an icon" becomes a target question once one vfs definition serves
several targets. Settled there 2026-07-26 and implemented so: the override
is shared across a kit definition's targets and carries no target dimension
- used where embedding applies, ignored elsewhere. Every target's kit gets a .resources.toml sidecar either way (a linux-target kit records not-applicable; a win32 kit cross-baked from a non-windows host records unavailable with the G-128 remedy named (G-128 achieved 2026-07-29 - see goals/archive/G-128-portable-pe-resource-stamping.md; cross-host kits now EMBED via the punkres arm when bin/punkres is present, demonstrated by the linux cross-bake in its Progress) - both proven live 2026-07-29, WSL cross-bake included), so this goal's per-target outputs inherit working record semantics.
- Related: G-105 - produces the cross-target RUNTIMES this consumes for custom kits.
- Related: G-005 / G-006 - where a target's binary payload comes from: built from source or downloaded with consent.
- Related: G-116 / G-020 - concrete per-platform binary payloads inside kit vfs lib trees (suite-built tcltls; the screen-capture backends under punk9wintk903.vfs/lib_tcl9).
- Related: G-101 - 8.6 containers ride the same mapping and output surfaces.
- Related: G-037 (achieved 2026-07-08) - established the per-kit declared vfslibs propagation whose single-target assumption this goal generalizes; see goals/archive/G-037-vendorlib-vfs-propagation.md.
- Tension to hold in view: G-004 (no committed binaries) - per-target payloads multiply the binary count in the tree unless they arrive by build or fetch.
- G-133 (archived) landed second-guessing-free target addressing for its checks (arch scan keyed by the entry's rt_target; smoke probe gated on host process-family runnability with a stated-reason skip) - per its cross-reference this goal, landing second, adopts that target-addressing; its output-location move must also keep the smoke probe pointed at wherever the freshly built artifact lands - see goals/archive/G-133-bake-payload-consistency-checks.md.
- Acceptance redundancy (recorded 2026-07-28, user-directed): the payload/target mismatch clause in this goal's Acceptance - a payload binary built for another platform reported naming the file and both platforms, with platform-discriminated subdirs explicitly not flagged - is ALREADY DELIVERED by G-133 (achieved 2026-07-27). Its acceptance requires a recapped BUILD-WARNING naming file, found architecture and kit target; no warning for multi-arch platform-subdir packages or recognised vendor spellings; and the scan running for cross-target kits. The clause is deliberately left in place rather than trimmed (that would be a contract edit): read it as a regression pin on behaviour that already exists, not as work to do. The genuine remainder of this goal is the other two gaps in Context - the bin/kits// output split and per-target payload declaration - and, per the note above, keeping G-133's smoke probe pointed at wherever the artifact now lands.
- Related: G-131 - sibling surface in the same kit loop (its kit_type_mismatch rework and container autodetection read the same mapvfs/runtime_caps machinery whose outputs this goal relocates); their previously recorded bridge was G-133, now archived.
- Related: G-123 (achieved 2026-07-31 - see goals/archive/G-123-thirdparty-runtime-tiers.md) - a published third-party runtime tier is a consumer of the cross-target artifacts this goal gives a home to; recorded directly now that the shared G-122/G-133 references are both archive-tier.
- G-123 (archived) recorded a finding this goal's make.tcl surface should note: its end-to-end demonstration (fixture-tier runtime -> non-default-target mapvfs entry -> bakelist row runtime-present) had to be LIVE-REVERSIBLE because make.tcl had no alternate-mapvfs-config seam for characterization tests. RETIRED by G-024 (achieved 2026-07-31): the PUNK_MAPVFS_CONFIG env var now points bakelist/bake at an alternate mapping file (used by the G-024 fixture tests in maketclbakelist.test) - this goal's cross-target characterizations can pin behaviour without editing the real config.
- Related: G-060 (recorded at activation, 2026-07-31) - the runnability split, settled as part of the activation decision: every Acceptance clause here is host-verifiable (artifact locations and cross-run coexistence are filesystem facts, payload selection is verified by inspecting kit contents, the G-125 gate is non-executing by design, and G-133's smoke probe skips non-runnable targets with a stated reason), so acceptance does not depend on EXECUTING a non-native kit on its platform - which this host cannot do for the FreeBSD case. On-target execution of cross-target kits belongs to G-060's guest matrix (its Alternatives survey, same date, records the VMM weighing: crosvm rejected, Firecracker review candidate, QEMU lean stands). Beyond contract, linux-x86_64 kits can get an opportunistic WSL execution smoke (the punkshell902 pattern from G-140, achieved 2026-07-30 - see goals/archive/G-140-linux-lib-tier-emission.md).
- Related: G-141 - libfetch consumption-derived target selection: the per-target payload declarations this goal adds to src/vfs/*.vfs.toml are that goal's derivation input, and its Acceptance's bake-sufficiency clause leans on them (its Notes record the pairing - whichever lands second adopts the other's surface). Back-pointer added at the activation freshness survey 2026-07-31 (goals_xref score: strongest one-directional pair).
- Related: G-137 - RT_VERSION stamping rides the same kit-icon wrap seam whose outputs this goal relocates: its stamp + .resources.toml sidecar emission must follow non-native artifacts to bin/kits// (the same per-target wrap-seam composition question the G-057 icon note above records; bridge recorded from its side 2026-07-31, back-pointer added at the activation freshness survey).
Progress
- 2026-07-31 increment 1 (output split, make.tcl): kit outputs are TARGET-keyed. Kits for the host's default target keep the flat locations byte-identically (punk86 rebake's deploy step skipped the install as content-identical to the pre-change deployed kit); any other target's kit builds to src/_build/kits// and deploys to bin/kits// with its own per-tier bin .punkcheck ledger - the merged .vfs image, icon/resources sidecar, arch scan, offset probe, smoke probe, release-gate and self-build guards all follow the per-kit locations. The duplicate-name guard is per (target, name) in BOTH the kit loop and the bakelist enumerator (mapvfs_kit_outputs), so same-named kits for different targets coexist instead of being runtime-renamed or overwritten; records gain out_tier; bakelist cross-target rows carry out=kits// and the per-kit detail block prints the tiered build/deploy paths (maketclplatform.test crosstarget pins updated to the new surface - 5/5 pass; maketclbakelist.test untouched 9/9 pass). The kit-loop cross-platform TODO is retired pointing here. LIVE: 'make.tcl bake punkshell902' relocated the linux kit to bin/kits/linux-x86_64/punkshell902 (valid ELF, sidecar beside it) - the INTENDED RELOCATION per Acceptance. The pre-move flat copies (bin/punkshell902, src/_build/punkshell902 + .vfs image) remain as orphans pending manual cleanup. CONSEQUENCE surfaced by the same rule: the G-129 verification kits (punkluck86, punk91ix86 - target win32-ix86 is not the host default win32-x86_64) will relocate to bin/kits/win32-ix86/ on their next bake; their old flat bin copies become the same class of orphan. (G-129 achieved - see goals/archive/G-129-kitboot-mountpoint-derivation.md; the kits stay active per their mapvfs group description.)
- 2026-07-31 increment 2 (per-target payload axis, VFSPAYLOAD + kit loop): a payload 'source' containing %platform% is a PER-PLATFORM entry - the platform set is DERIVED from the kit mapping (every mapvfs kit output pairing the vfs, bake_default included; PUNK_MAPVFS_CONFIG honoured via mapvfs_locate), never declared twice. Each platform's copy materializes punkcheck-tracked under _targets/// in the .vfs folder (VCS-ignored staging - rules added to .gitignore + .fossil-settings/ignore-glob); at bake time the kit's merged image gets ONLY its own target's subtree (selection overlay upstream of the arch scan and G-125 gate, staging dropped from the image; missing _targets/ in a staged vfs earns a recapped BUILD-WARNING). supersedes/replace apply within each platform subtree; %platform% in 'target' is rejected (staging prefix is implicit); a %platform% entry on an unconsumed vfs warns and skips. Spec extended in src/vfs/README.md. MIGRATION: the punk9linux.vfs tcllibc entry now reads source = "%platform%/tcl9/tcllibc" (was hardcoded linux-x86_64) and the flat COMMITTED lib_tcl9/tcllibc tree was removed from both VCS (declaration + packages tier govern it; committed-binary reduction in the G-004 direction). Verified live: bake punkshell902 stages _targets/linux-x86_64, selects it into the image (lib_tcl9/tcllibc present, no _targets residue, arch scan 6 scanned/6 match - same composition as the pre-axis bake), deploys to bin/kits/linux-x86_64/; bake punkdeclare (platform-neutral entries) unchanged, smoke probe resolves udp 1.0.13 + tcllibc 2.0 from the freshly built native artifact.
- 2026-07-31 increment 3 (characterization): new src/tests/shell/testsuites/punkexe/maketclkitlocations.test - fixture kit mappings through the PUNK_MAPVFS_CONFIG seam (nothing built, per the G-125 precedent of keeping heavy verdicts out of this suite): one vfs under two targets resolves to flat vs kits// locations in one report (location = pure function of (name, target) - the construction that makes the cross-run overwrite impossible); two NON-NATIVE targets sharing a kit name coexist as two rows split by tier and target-driven suffix with NO runtime-name rename; a same-name SAME-target duplicate keeps the historical _ disambiguation. The mismatch clause stays a REGRESSION PIN on G-133 coverage (verdicts: modules/punkboot/utils/testsuites/utils/binaryarch.test; advisory contract: maketclpayloadcheck.test) - deliberately not re-implemented. SURFACE FIX flushed out by the characterization: mapvfs_match_outputs stopped at the FIRST record matching a plain kit name (a pre-G-127 uniqueness assumption) - a name now selects ALL its records (one per target), matching the @group branch, so filtered bakelist detail and selective bake cover every target of a shared name. Full punkexe subtree green: 15 files, 101 tests, 97 pass / 4 constraint-skips / 0 fail (88s, native Tcl903 runner).
- 2026-07-31 increment 4 (docs + records; punkshell 0.39.0):
bin/AGENTS.md gains the "Cross-target kit outputs (bin/kits//)"
contract section; src/AGENTS.md gains the target-keyed-output bullet beside
its G-122 host/target material; make.tcl workflow text updated per its update
contract ([K9] output split, [K8] untracked additions, the .vfs.toml
%platform% line, bakelist helptext + report header); punkproject.toml bumped
0.38.0 -> 0.39.0 with the CHANGELOG entry (minor: new declaration capability +
relocated cross-target outputs).
G-023 RECONCILIATION (per Acceptance): version-named outputs COMPOSE with the
platform tier rather than competing - scheme-expanded records carry a target
like any entry and flow through the same per-kit location logic with no
special-casing, so a versioned scheme declaring target
yields bin/kits/
/punk- etc, and the G-023 release gate now checks the per-kit deploy dir (kit_deploydir) rather than flat bin/. G-023's own Acceptance naming bin/punk9-0.5.0.exe stays untouched: its outputs are default-target today, and location only moves if a target is declared. RELOCATION RECORDS (per Acceptance): punkshell902's move bin/punkshell902 -> bin/kits/linux-x86_64/punkshell902 is an INTENDED RELOCATION (executed live 2026-07-31, increment 1); the verify-ix86 kits (punkluck86, punk91ix86) relocate to bin/kits/win32-ix86/ on their next bake as the same rule's consequence. Pre-move flat copies (bin/punkshell902, src/_build/punkshell902
- .vfs image; later the flat ix86 kit copies) are orphans for manual removal
- also recorded in bin/AGENTS.md. PUNK9LINUX MIGRATION DECISION (per Acceptance): punk9linux.vfs is MIGRATED to the declaration for its tier-available payload - tcllib2.0 (neutral, G-115) and tcllibc (per-platform %platform%, this goal; flat committed copy removed from VCS) - while its remaining binary payload (tcltls, tdom, thread3.0.2 .so trees) DELIBERATELY STAYS HAND-CURATED until punkbin lib-tier artifacts exist for those packages (suite-built tcltls is G-116's scope; the general vendored-tree migration is G-004's recorded template). Mixed posture is the honest record: declared where a governed source tier exists, hand-curated drop-ins where none does yet.
- 2026-07-31 increment 5 (verification + closeout):
BYTE-COMPARABILITY: punk86 (sdx type) rebuilt through the split machinery
deploy-skipped as content-identical to its pre-G-127 deployed copy; punkdeclare
(zip type) forced-rebuild compared by the recorded method for
non-byte-reproducible kit zips - runtime prefix (2433536 bytes) identical, 2805
members, zero name/size/crc32 differences. Zip-kit rebuilds differ from their
7-29/30 bin copies only by intervening G-115-era payload-source evolution
(punkcheck-attributed), not by the location machinery.
DRIVING-TCLSH TRAP (found by the G-134 offset-style pin doing its job - G-134
achieved, see goals/archive/G-134-baked-kits-pinned-archive-relative.md): this host's PATH
tclsh is c:/tcl87 (dev 8.7, old zipfs API) whose mkimg emits FILE-relative
offsets - zip kits baked with it earn the G-134 BUILD-WARNING and drift bytes.
punk905 + punkdeclare were re-baked clean with the native Tcl903 tclsh90;
drive bakes with Tcl903 (or a punk kit), never the tcl87 PATH tclsh.
ONE-RUN TWO-TARGET SAME-VFS real bake (fixture mapping, full real bakes):
'bake fixnat fixlin' (punk9wintk903.vfs as win32-x86_64 zip + linux-x86_64
kit) produced bin/fixnat.exe AND bin/kits/linux-x86_64/fixlin in one run,
each arch-scanned against ITS OWN target (fixnat 72/72 match; fixlin 72
recapped mismatches naming file, found arch and target - the mismatch clause
demonstrated live, with the platform-subdir exemption visible on both).
SAME-NAME TWO-NON-NATIVE real bakes: legacy fixture fixsame (linux-x86_64 kit
- win32-ix86 zip on punk8min.vfs) - one 'bake fixsame' run built BOTH artifacts (distinct sha1s AE93E2AE... / ADDE393E...), a second run left both byte-identical: the cross-run overwrite is structurally impossible. Fixture artifacts removed after verification (ledger rows remain, harmless). VERIFY-IX86 RELOCATION LIVE: punkluck86 baked to bin/kits/win32-ix86/punkluck86.exe - arch scan 26/26 match for win32-ix86 and the smoke probe EXECUTED the tiered artifact (host-runnable non-default target): Thread 2.8.5. %platform% ON BOTH SOURCE ROOTS incl. the vendorlib path the Acceptance names: punkdeclare.vfs.toml migrated (tcludp from vendorlib_tcl9/%platform%/, tcllibc from the packages tier), flat folder payload removed - the whole payload stages under _targets/win32-x86_64/ and the smoke probe resolves udp 1.0.13 + tcllibc 2.0 inside the built kit. RELOCATION ORPHANS removed after verifying the tiered replacements: bin/punkshell902 + src/_build/punkshell902(.vfs), bin/punkluck86.exe + build copies; punk91ix86's flat copy stays until its next bake relocates it. Full punkexe subtree re-run after all increments: 15 files, 101 tests, 97 pass / 4 constraint-skips / 0 fail (Tcl903 runner).
- Acceptance assessment 2026-07-31: every clause verified - one-run two-target distinct artifacts with native placement unchanged; cross-run no-overwrite by construction plus the live two-run check; two-non-native same-name coexistence; per-target payload selection from vendorlib_tcl/%platform% and the packages tier through one declaration; single-target byte-comparability by the recorded prefix+content method; mismatch reporting live and pinned as the G-133 regression pin; bakelist row locations; the punk9linux migrated-plus-hand-curated record; the punkshell902 relocation recorded and executed; the G-023 reconciliation stated; native win32 outputs unchanged with the punkexe suites passing; the kit-loop TODO retired. Remaining-work list: resolved. Flipped achieved 2026-07-31.