Skip to content

format: two archives, one vocabulary, and a guard that keeps it that way - #29

Merged
donislawdev merged 1 commit into
mainfrom
format/archives-share-one-vocabulary
Sep 1, 2026
Merged

format: two archives, one vocabulary, and a guard that keeps it that way#29
donislawdev merged 1 commit into
mainfrom
format/archives-share-one-vocabulary

Conversation

@donislawdev

Copy link
Copy Markdown
Owner

Groundwork for archive settings. No user visible change, and that is the whole
claim being made: same keys, same defaults, same refusals, same bytes.

What was measured first

ZIP and TAR.GZ each carried their own copy of what an archive holds and how to
read it. Across the two packages:

duplicated detail
six constants defaultEntries, maxEntries, defaultEntryFmt, defaultEntrySizeText, commentPaddingLimit, commentCapacity - identical values
two property readers intProperty and sizeProperty, identical apart from the message prefix
mustSize identical apart from the message prefix
the entry ceiling refusal written out twice, down to its wording
groupsFor identical to the byte - every difference between the two copies was a comment

One of those comments pointed at the other copy. That comment was the whole
mechanism holding them together, and 7Z is already named and measured
(MVP-FORMATS.md section 2.5) with RAR and ISO behind it in M1.

What changed

internal/format/archive is the third family package, after imagelabel for
the pictures and opc for the Office formats. A container names the axes it
takes:

Properties: archive.Axes(archive.Entries, archive.EntryFormat, archive.EntrySize),

Naming them rather than receiving all of them is not tidiness. A window sends
every setting it draws, so a format must only ever declare a setting it can
actually carry - which is what makes an axis like a password declarable by ZIP
and not by TAR.GZ later on.

What was deliberately not merged

The padding ceiling. Both formats cap it at 65 535 B and the agreement is a
coincidence: in ZIP it is the width of the two byte length field, in TAR.GZ it
is the measured point where 7-Zip stops reading a gzip comment. One number, two
independent facts. Merging them would leave the next container inheriting a
limit with no reason attached.

Evidence

  • TestOurOwnGeneratorsHaveNotDrifted green - D11 only allows this refactor to
    be invisible, so the pinned hashes are the proof rather than the claim.
  • The old and new command line binaries were built and compared:
    tfg formats zip --json and tfg formats targz --json are identical to the
    byte
    , and so is tfg formats.
  • Full suite green. preflight --quick: all 11 checks, including staticcheck,
    lint and fidelity, which otherwise only run on the runner.
  • The depth crowding ceiling comes down from 54 to 52 - the ratchet reporting
    that two duplicated functions stopped existing.

Three things that were not planned

A guard was written and deleted the same day, and the deletion is worth more
than the guard.
It said a format which is not a container may not use these
names at all. Run against the registry it went red at once: log declares its
own entry_format and means the shape of a line by it. That is not a collision

  • each field is drawn from its own format's Detail, and SettingLabel only
    spaces and capitalises the key - it is proof the premise was false. entries
    and entry_size are just as reasonable for a log, and "how many entries" is
    already a named deferred log setting, so the guard would have gone red on a
    format doing nothing wrong. A defence that reddens on the legitimate case is
    worse than none. The naming hazard went to the glossary.

TestTheArchiveEntryFormatSortsBeforeTheArchive was scraping defaultEntryFmt
out of zip.go as text
, because the constant was unexported and the guard
lives outside that package. It went red on the move, which is the good failure.
The constant is exported from archive now, so it asks the constant and there
is nothing left to go stale.

TestEveryLogShapeWritesWholeLinesAtTheRightSize shipped unproven with the
log work in #28 and now has a mutation: the default line terminator becomes a
space, one byte either way, so every size and every exit code stays right and
only this guard can see it. CI could not have caught that gap - mutate.py
lives in tools/, outside the repository, so the mutation coverage guard skips
on a fresh clone and only ever runs locally.

New guard

TestEveryContainerDeclaresTheSharedAxesAsTheyAreDeclaredOnce, proven by
mutation. Asked of every registered container and of every axis the archive
package declares, so a third container and a fourth setting are both covered on
the day they arrive rather than the day somebody remembers the file.

ZIP and TAR.GZ each carried their own copy of what an archive holds and how to
read it. Measured across the two packages before the move: six constants with
identical values, two identical property readers, an identical mustSize, the
entry ceiling refusal written out twice down to its wording, and a groupsFor
whose code was identical to the byte. Every difference between the two copies
was a comment, and one of those comments pointed at the other copy. That
comment was the whole mechanism holding them together.

7Z is already named and measured in docs/MVP-FORMATS.md section 2.5 and M1
lists RAR and ISO behind it, so the copy was going to be taken a third time and
a fourth.

It now lives in internal/format/archive, the third family package after
imagelabel for the pictures and opc for the Office formats. A container names
the axes it takes - archive.Axes(archive.Entries, ...) - rather than receiving
all of them. That is not tidiness: a window sends every setting it draws, so a
format must only ever declare a setting it can actually carry.

Nothing a user sees moves. Same keys, same defaults, same refusals, and the
pinned generator hashes are unchanged - TestOurOwnGeneratorsHaveNotDrifted is
the proof rather than the claim, since D11 only allows this refactor to be
invisible.

What was deliberately NOT merged is the padding ceiling. Both formats cap it at
65 535 B and the agreement is a coincidence: in ZIP it is the width of the two
byte length field, in TAR.GZ it is the measured point where 7-Zip stops reading
a gzip comment. One number, two independent facts, and merging them would have
left the next container inheriting a limit with no reason attached. Sharing is
for what is the same fact, not for what is the same number.

Three things came out of the work that were not planned:

  A guard was written and deleted the same day, and the deletion is worth more
  than the guard. It said a format which is not a container may not use these
  names at all. Run against the registry it went red at once: log declares its
  own entry_format and means the shape of a line by it. That is not a collision
  - each field is drawn from its own format's Detail and SettingLabel only
  spaces and capitalises the key - it is proof the premise was false. entries
  and entry_size are just as reasonable for a log, and "how many entries" is
  already a named deferred log setting, so the guard would have gone red on a
  format doing nothing wrong. A defence that reddens on the legitimate case is
  worse than none. The naming hazard went to the glossary instead.

  TestTheArchiveEntryFormatSortsBeforeTheArchive was scraping defaultEntryFmt
  out of zip.go as text, because the constant was unexported and the guard
  lives outside that package. It went red on the move, which is the good
  failure. The constant is exported from archive now, so it asks the constant
  instead and there is nothing left to go stale.

  TestEveryLogShapeWritesWholeLinesAtTheRightSize shipped unproven with the log
  work and now has a mutation: the default line terminator becomes a space, one
  byte either way, so every size and every exit code stays right and only this
  guard can see it. CI could not have caught that gap, because mutate.py lives
  in tools/ which is outside the repository, so the mutation coverage guard
  skips on a fresh clone.

The depth crowding ceiling comes down from 54 to 52, which is the ratchet
reporting that two duplicated functions stopped existing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@donislawdev
donislawdev merged commit 5552406 into main Sep 1, 2026
18 checks passed
@donislawdev
donislawdev deleted the format/archives-share-one-vocabulary branch September 1, 2026 07:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant