feat(rt-fragment-check): port fragment-check.sh → rt fragment-check (~128 LOC) #572
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#572
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Port
scripts/fragment-check.sh(128 LOC) tort fragment-checksubcommand. Phase 6b validator/gate per ADR-0009 §3.3. C4 (fragment format) validates against this — load-bearing for the milestone gate. The richest Phase 6b sub-tracker: mixed substrate + two sub-features unported + several byte-subtleties.Scope (updated 2026-07-27 per Engineer framing-verify bus e328)
Three concerns:
internal/fragments.AssertNoUnknownKindsEXISTS from Phase 5.::warning::to STDOUT for fragments > LENGTH_WARN_CHARS. ENTIRELY UNPORTED.NOT a Cutter/forgejo consumer (pure-fs). 3/5 Cutter refinement count firm.
AC
rt fragment-checkcompiles + all 3 concerns port byte-faithfulFRAGMENT_CHECK_KINDSoverride (#489) supported via new kind-set-parameterized check ininternal/fragments#488 length-warnfully ported: wc -m char-count + LENGTH_WARN_CHARS validation + =0-disables + hidden-file exclude + filename sanitize +::warning file=X::to STDOUTinternal/fragmentsgains kind-set-parameterized check (additive-dormant, no interface change to frozen #505 Reader)internal/gatesgains length-warn decision helper (char-count > threshold + filename-safe → annotation | fallback)cmd/rtowns fs-walk + LENGTH_WARN_CHARS validation + dir-not-foundfragment-check.shbyte-oracle (ASCII length-warn under LC_ALL=C)fragment-check.sh; the PR-CI callsite intentionally stays bash today for a dependency-free gate (reusable-changelog-fragment-check.yml:95). Restated 2026-07-30 from the original state-assertion "C4 (fragment format) validates via this port", which was not true at any point in this tracker's life. The state claim now lives in #607's ACs.go vet ./...+golangci-lint runcleanFraming-verify ratification notes (2026-07-27, bus e328)
Engineer's framing-verify surfaced 8 ratifiable forks + several substrate anchors. Fragment-check is the RICHEST Phase 6b sub-tracker (NOT a thin AssertNoUnknownKinds wrapper) — mixed substrate + two sub-features unported + several byte-subtleties.
F1 — 3-EXIT bespoke sentinels (RATIFIED)
verdict: 0 pass, 1 unknown-kind, 2 MALFORMED LENGTH_WARN_CHARS config error.
internal/verdictdoes NOT apply (exit 2 = config-validation-error, NOT cannot-grade).n=3 milestone-language-generalization pattern in Phase 6b:
Confirms per-oracle verdict shape; §5 "trivalent at wire boundary" is general framing, not universal contract.
F2 — internal/fragments kind-set extension (RATIFIED, Option a)
AssertNoUnknownKindsuses FIXED Kinds() set — no override support.FRAGMENT_CHECK_KINDSenv is LOAD-BEARING + in byte-oracle (bats 81-97 test it).Rejected Option b (reimplement in cmd): puts kind-checking logic in two places = shape-of-(c)-hole recurrence risk. Same principle as #557 Fire-wiring + #558 verbose-desync-fork-B.
Ratified Option a: extend
internal/fragmentswith kind-set-parameterized package-level check (like AggregateBump sits beside frozen Reader). Additive-dormant, no interface change to frozen #505 Reader. Default-set caller delegates. Second refinement of Phase-5-authored substrate this arc (first was manifest.Store CheckDesyncVerbose at #558) — substrate-refinement-per-port pattern continues.F3 — length-warn (#488) port (RATIFIED)
wc -m char-count; LENGTH_WARN_CHARS validation (non-neg int regex else EXIT 2); =0 disables; hidden-file exclude (.template.md); ::-injection filename sanitize (^[A-Za-z0-9._/-]+$ else stderr-fallback, ember#126 shape); ::warning file=X:: to STDOUT (COMPARED surface).
Each specific detail is oracle-honest bash-behavior preservation.
F4 — wc -m LOCALE trap (RATIFIED, honest coverage-split — substrate-of-record anchor)
Anchor:
/srv/CLAUDE.md § Reflex tablenames "LC_ALL=C, always" for text-matching a tool's human-readable output. F4 extends that class to semantically-load-bearing surface: wc -m under LC_ALL=C = BYTES; under UTF-8 = CHARS. Go'sutf8.RuneCountInString= CHARS by construction.Ratified split:
Anchor for future locale-sensitive semantics: locale-sensitive semantics need equivalence-harness under a pinned locale + unit tests for the divergent-under-other-locale case + explicit disclosure. Same "name the surface" discipline shape as #555 F5 + #570 F3.
Note (§5 ASCII-only): the ASCII constraint is about validator OUTPUT; fragment CONTENT can be unicode — that's why wc -m matters.
F5 — find-order (RATIFIED, strengthening-with-disclosure)
Bash
find -print0is READDIR order (unsorted); Goos.ReadDirSORTS. Multi-long-fragment :⚠️: emission order could diverge.Ratified: harness single-long-fragment (order moot; deterministic byte-compare). Go's sorted behavior is a strengthening over bash's nondeterministic emit order (same pattern as #557 manifest-atomic strengthening). Multi-warn order disclosed as bash-side nondeterminism (harness can't compare byte-verbatim because bash-side varies).
Not a divergence — an improvement whose relevant surface the harness naturally covers.
F6 — placement split (RATIFIED)
internal/fragmentsgains kind-set check (F2)internal/gateshelper for length-warn decision (char-count>threshold + filename-safe → annotation|fallback)cmd/rtdoes fs-walk + LENGTH_WARN_CHARS validation + dir-not-foundConsistent with #571/#570 placement pattern: reusable substrate at primitive layer; consumer-specific glue at command layer.
F7 — pure-fs harnessability (RATIFIED, favorable-substrate observation)
Fragment-check is pure-fs (no API/git) → byte-oracle covers MOST cases fully (pass/unknown-kind/dir-not-found/custom-dir/override/malformed-config-exit2/ASCII-length-warn). Much fuller than #570's API-path deferred coverage.
Anchor: coverage-surface strategy is per-substrate-shape, not one-size-fits-all across Phase 6b. Different validator categories have different coverage-surface shapes.
F8 — no events (RATIFIED, not applicable)
#572 sources
lib/fragments.sh, notevents.sh. Substrate-honest N/A rather than perform-not-applicable retirement. Same shape as #570 F4.Refinement accounting
Substrate-refinement-per-port pattern: #572 refines internal/fragments (Phase-5 substrate) same shape as #558 refined manifest.Store.
Related
internal/fragments(frozen #505 Reader stays untouched; additive check function alongside)Filed 2026-07-27 by Bosun as part of Phase 6b decomposition. F1-F8 ratified 2026-07-27 per Engineer framing-verify (bus e328).
AC-hygiene sweep 2026-07-30 (Quartermaster; per Bosun d6d1/8361 dispatch): 11/12 ACs substrate-verified as done-not-ticked → ticked per Engineer's cluster-grading (bus 53c5 @ main
3cca3b9: full go test 19 pkgs 0 FAIL + golangci-lint 0 issues). AC11 held un-ticked as LYING per Engineer's grading — the port is faithful but the wired PR-CI gate is bash by design; requires operator restate-vs-file-cutover call. Engineer owns the AC11 surface.Resolved 2026-07-30 (Engineer). The operator chose file-the-cutover, so AC11 was converted from a state-AC to an action-AC (
cutover FILED as #607) and is now ticked. Quartermaster's note above is preserved as the record of the sweep as it stood at that moment — "held un-ticked" describes that moment, not the current state. See the AC substrate-check section below.AC substrate-check (2026-07-30, Bosun d214 → Engineer)
All 12 ACs re-derived from the substrate at
main@3cca3b9; gate re-run at thatSHA:
go build+go vet ./...clean, fullgo test ./... -count=1green (19packages, zero FAIL),
golangci-lint run0 issues.Substrate reference for the eleven that hold: port + fs-walk +
LENGTH_WARN_CHARSvalidation + dir-not-found →
cmd/rt/fragment_check.go(hidden-file exclude at:134, threshold regex at:118); 3-EXIT verdict →TestFragmentCheckExitCodes;FRAGMENT_CHECK_KINDSoverride →cmd/rt/fragment_check.go:85+fragments.AssertNoUnknownKindsIn(
internal/fragments/fragments.go:212, additive beside the frozen #505Readerat:195— the additive-dormant shape held); #488 length-warn decision →internal/gates/fragment_check.go(FragmentLengthWarnat:41, filename-sanitizeRE at
:45); rune-counting / locale split →TestFragmentCheckLengthWarnCountsRunes;find-order disclosure →
cmd/rt/fragment_check_equiv_test.go:26; byte-oracle →cmd/rt/testdata/oracle/fragment-check-oracle.sh+TestFragmentCheckEquivalence.AC11 — converted from a state-AC to an action-AC (the one that did NOT hold)
As originally worded it was false, and had never been true.
rt fragment-checkappears nowhere in
.forgejo/ortemplates/; the C4 gate runs.release-toolkit/scripts/fragment-check.sh— bash — atreusable-changelog-fragment-check.yml:111.This was a mis-specified AC, not missing work. The bash callsite is deliberate,
and the workflow says so itself (
:95): "No extra deps needed: fragment-check.shonly uses bash + find + printf, all POSIX. Skipping the install-deps step keeps the
PR-gate lean (no apt-get on every touched-fragment PR)." The cut path pays
fetch-rt.shto obtain the binary; the lean PR gate deliberately does not. So theport was never going to satisfy this AC within Phase 6b's scope — the AC asserted a
wiring the architecture had chosen not to build.
Why the AC was converted rather than left un-ticked. Leaving a state-AC un-ticked
on a closed tracker recreates the precise drift class this audit exists to clear:
ac-state-audit.py --closed-untickedwould re-flag #572 on every future sweep, andeach auditor would re-derive this analysis to learn the flag was intentional.
"Will tick when #607 closes" also has no actor and no mechanism — nobody reopens a
closed tracker to tick a box. Per /srv/CLAUDE.md § Acceptance-criteria tick
discipline, the honest move is option 2: restate the state-assertion as an action
AC. The filing of #607 is a done fact, so the tick is a claim about what was
DONE, not about what IS TRUE — a different type with a different tick discipline.
The line leads with FILED so the ✓ cannot be skim-read as "C4 validates via the
port".
Direction, not a nice-to-have. Per operator (2026-07-30), single-stack v1.0.0
is the intended end-state — the bash→rt cutover is the direction of travel, and #607
is where it is tracked, sequenced behind #605 (binary trim) and #606 (fetch-rt
caching) so the gate pays a proportionate cost when it lands.
Disposition ratified by Bosun (bus f70c → b596 → f9db); operator chose file the
cutover over restate-and-tick-as-satisfied.
CLOSED via PR#576 merged @
ab1ec3e(2026-07-27 13:07 CEST, rebase-merge, HTTP 200, gate 4/4 byte-verified pre-fire).ACs re-derived from merged substrate + Surveyor's independent deep-verify (bus 82f4), applying "name the coverage surface" discipline:
rt fragment-checkcompiles + all 3 concerns port byte-faithful — surface: Surveyor's core-gate byte-faithful verify + em-dash end-to-end byte-trace (bash source → Go source → rendered rt output all e2 80 94)internal/fragments.AssertNoUnknownKindsIn— surface: F2 delegation byte-equivalent (empty-id arm unreachable both sides per Surveyor)::warning::to STDOUT) — surface: full-gate green host + forgejo-ci-go:latest; em-dash U+2014 byte-faithful (n=4 milestone-language-generalization anchor)internal/fragmentsgains kind-set check (additive-dormant, no interface change to frozen #505 Reader) — surface: 2nd Phase-5-substrate refinement this arc; sibling to manifest.Store CheckDesyncVerbose refinement at #558internal/gatesgains length-warn decision helper (additive) — surface: sibling to ManifestPrecheck + PreflightPushWhitelistgo vet ./...+golangci-lint runclean — surface: CI gate confirmsAll 8 ratified forks built + mutation-verified on 3 axes (bus e328 → 1519):
Mutation-verified non-vacuous on 3 axes each narrowest-reddening:
Surveyor's 2 non-blocking should-considers (both OFF harness surface, both one-line fixes, neither touches core gate):
::warning::annotation path (violates code's own "match bash find" comment)Engineer's disposition: bundle candidates for the pending micro-cleanup PR (currently: #574 empty-arg + #575 no-arg→alpha nit; now +these two = 4-item bundle). Default invocation never triggers either.
Correction cycle anchors filed (n=4 milestone-language-generalization corrections this Phase 6b):
/srv/CLAUDE.md § Reflex tableLC_ALL=C class to semantically-load-bearing surface)Refinement accounting: Cutter 3/5 STAYS FIRM. forgejo.Client STAYS at 3.
internal/fragments+1 (AssertNoUnknownKindsIn).internal/gates+1 (FragmentLengthWarn helper). cmd/rt/main.go +1 mechanism (exitError, reusable command-layer utility for future bespoke-exit validators).Phase 6b status: 3/6 sub-trackers CLOSED. Engineer /compacting at seam for wire-existing-gates modality post-#572 close. Remaining: #567/#568/#569 wire-existing-gates trio.
Closed by Bosun 2026-07-27 13:07 CEST per Engineer's Refs-only convention.