Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
189 commits
Select commit Hold shift + click to select a range
ba838f3
+ preserve exact DMG partition lengths
Hawkynt Aug 28, 2026
5fa3cd3
* teach DMG reader exact logical partition sizes
Hawkynt Aug 28, 2026
dbfe308
* expose DMG tail builders for mutation
Hawkynt Aug 28, 2026
ee7efa2
* identify the safely mutable DMG profile
Hawkynt Aug 28, 2026
06f86d2
+ add true DMG tail-index mutation
Hawkynt Aug 28, 2026
937c9f4
+ expose DMG as a modifiable raw UDIF container
Hawkynt Aug 28, 2026
41fc222
+ add a checksummed UEFI FV writer
Hawkynt Aug 28, 2026
fe22ada
+ edit UEFI FFS records through erased free space
Hawkynt Aug 28, 2026
1344957
+ expose UEFI FV creation and free-space mutation
Hawkynt Aug 28, 2026
25a2c15
* let UEFI FV walks cross erased mutation gaps
Hawkynt Aug 28, 2026
a9e7fd5
+ cover DMG random-access mutation and odd lengths
Hawkynt Aug 28, 2026
ff78987
+ verify UEFI FV create and in-place CRUD
Hawkynt Aug 28, 2026
690c889
+ put DMG under the generic modify round-trip contract
Hawkynt Aug 28, 2026
ca4b4b9
+ add OrangeFS DBPF object writing
Hawkynt Aug 28, 2026
8b353a9
+ promote OrangeFS DBPF objects to R/W
Hawkynt Aug 28, 2026
dda74a5
+ cover OrangeFS DBPF payload mutation
Hawkynt Aug 28, 2026
eff458a
+ preserve DTB node hierarchy when writing
Hawkynt Aug 28, 2026
a83734a
+ add hierarchy-preserving DTB mutation
Hawkynt Aug 28, 2026
89ccb8e
+ promote DTB to hierarchy-preserving R/W
Hawkynt Aug 28, 2026
5bdc6ca
* preserve DTB root and text-property round trips
Hawkynt Aug 28, 2026
1bbb620
+ verify hierarchy-preserving DTB R/W
Hawkynt Aug 28, 2026
2c7143e
- remove obsolete DTB read-only contract
Hawkynt Aug 28, 2026
235e91f
* define R/W by supported existing-image edits
Hawkynt Aug 28, 2026
b401d04
* align modify contract with existing-image semantics
Hawkynt Aug 28, 2026
c5a0382
* test R/W as supported existing-instance mutation
Hawkynt Aug 28, 2026
419bdda
+ reconstruct mutable EWF media payload
Hawkynt Aug 28, 2026
2ac4477
ci: apply one-shot R/W promotion patches
Hawkynt Aug 28, 2026
8bb2694
ci: split one-shot patch script from workflow
Hawkynt Aug 28, 2026
f747dac
ci: simplify one-shot R/W patch runner
Hawkynt Aug 28, 2026
3981851
ci: make one-shot descriptions case tolerant
Hawkynt Aug 28, 2026
56b3e15
+ add conservative offline ReFS file mutation
Hawkynt Aug 28, 2026
b67f5d8
ci: add exact ReFS descriptor promotion patch
Hawkynt Aug 28, 2026
e901fd0
ci: include ReFS descriptor in one-shot promotion
Hawkynt Aug 28, 2026
aef7384
+ cover promoted filesystem image mutation paths
Hawkynt Aug 28, 2026
9113900
- remove temporary R/W promotion workflow
Hawkynt Aug 28, 2026
489b877
- remove temporary ReFS promotion script
Hawkynt Aug 28, 2026
aa9ca83
- remove temporary bulk promotion script
Hawkynt Aug 28, 2026
a317dbc
+ add explicit purge capability contract
Hawkynt Aug 28, 2026
11187b1
+ make modifiable containers explicitly purgeable
Hawkynt Aug 28, 2026
367e06a
+ optimize multi-entry archive compression parameters
Hawkynt Aug 28, 2026
4aca577
ci: stage maintenance capability audit
Hawkynt Aug 28, 2026
375dc3b
ci: run maintenance capability audit
Hawkynt Aug 28, 2026
c00cac7
ci: simplify maintenance capability audit runner
Hawkynt Aug 28, 2026
5286bef
ci: stage live rebuild progress and cancellation UX
Hawkynt Aug 28, 2026
9f1a6fc
ci: run maintenance block-map UX audit
Hawkynt Aug 28, 2026
adcca8c
ci: fix staged-defrag UX transformer details
Hawkynt Aug 28, 2026
7e52184
ci: run corrected block-map UX audit
Hawkynt Aug 28, 2026
46b397c
ci: tighten rebuild UX tests for NUnit 4
Hawkynt Aug 28, 2026
a1b36d6
+ document staged rebuild block-map projection semantics
Hawkynt Aug 28, 2026
8b8f20f
ci: polish projected block-map and close cancellation UX
Hawkynt Aug 28, 2026
7c33bf7
ci: validate WPF block-map maintenance UX
Hawkynt Aug 28, 2026
8cb64d8
+ add live staged-rebuild progress and cancellation
Hawkynt Aug 28, 2026
80790bf
+ add cooperative maintenance cancellation
Hawkynt Aug 28, 2026
41b2d0d
+ drive generic archive defrag through live staged rebuilds
Hawkynt Aug 28, 2026
677874e
+ expose cancellable 7z regroup progress
Hawkynt Aug 28, 2026
93ee869
+ keep block-map live during archive rebuild and regroup
Hawkynt Aug 28, 2026
5d4005b
+ test staged rebuild progress and safe cancellation
Hawkynt Aug 28, 2026
dabcf5a
- remove one-shot maintenance transformer scaffold
Hawkynt Aug 28, 2026
e8a8deb
- remove one-shot maintenance UX transformer
Hawkynt Aug 28, 2026
5668fad
- remove one-shot maintenance UX patch scaffold
Hawkynt Aug 28, 2026
6e38860
- remove one-shot maintenance workflow
Hawkynt Aug 28, 2026
cbfafb7
+ cancel native block moves at safe boundaries
Hawkynt Aug 28, 2026
45b5b60
+ make layout-backed wipe a safe generic verb
Hawkynt Aug 28, 2026
b99ce63
+ make filesystem extent maps wipe-capable by contract
Hawkynt Aug 28, 2026
f072454
+ make archive layout maps wipe-capable by contract
Hawkynt Aug 28, 2026
432ad07
+ reconstruct logical media from EWF chunk tables
Hawkynt Aug 28, 2026
86b79bb
+ promote EWF to logical-media R/W and maintenance verbs
Hawkynt Aug 28, 2026
76d6433
fix: import binary primitives for EWF maintenance
Hawkynt Aug 28, 2026
c1963a8
+ test EWF media R/W, shrink and relayout
Hawkynt Aug 28, 2026
b26079f
fix: preserve EWF metadata and define pseudo-entry purge
Hawkynt Aug 28, 2026
ea211b9
+ verify EWF pseudo-container purge semantics
Hawkynt Aug 28, 2026
bb5cc9d
+ advertise CramFS offline R/W semantics
Hawkynt Aug 28, 2026
c7e682b
+ advertise SquashFS offline R/W and optimization
Hawkynt Aug 28, 2026
8812422
+ promote supported EROFS profile to safe offline R/W
Hawkynt Aug 28, 2026
3c0df01
+ verify EROFS supported-profile R/W and maintenance
Hawkynt Aug 28, 2026
a62e892
+ expose physical CBM nibble track layout and empty slots
Hawkynt Aug 28, 2026
21440cf
+ add direct G64/NIB track-container writers
Hawkynt Aug 28, 2026
03df3c5
Fix OrangeFS payload size guard
Hawkynt Aug 28, 2026
fca39f4
Fix DTB writer stack allocation
Hawkynt Aug 28, 2026
a671160
Fix EWF header candidate enumeration
Hawkynt Aug 28, 2026
0086583
Add direct R/W track operations for G64 and NIB
Hawkynt Aug 28, 2026
5122daf
Test direct G64 and NIB track mutation
Hawkynt Aug 28, 2026
e7a187e
Add filesystem and media driver contracts
Hawkynt Aug 28, 2026
17acbb7
Expose G64 and NIB as raw track devices
Hawkynt Aug 28, 2026
2daba19
Test raw track device layer
Hawkynt Aug 28, 2026
caed88d
Document mount-grade filesystem driver layering
Hawkynt Aug 28, 2026
0e0caa3
Use expandable stream in G64 track device test
Hawkynt Aug 28, 2026
b323c91
Route schema-driven archive optimization through compact
Hawkynt Aug 28, 2026
2d7d5e4
Expose G64 and NIB raw track device providers
Hawkynt Aug 28, 2026
e3fca95
Add random-access block device provider contract
Hawkynt Aug 28, 2026
bdfee95
Expose D64 as random-access block device
Hawkynt Aug 28, 2026
ca06da9
Add mount-grade CBM DOS filesystem session
Hawkynt Aug 28, 2026
86b24e6
Project canonical GCR tracks as 1541 sectors
Hawkynt Aug 28, 2026
429fe75
Fix CBM DOS session commit semantics
Hawkynt Aug 28, 2026
0729b45
Validate CBM DOS allocation before writable mounts
Hawkynt Aug 28, 2026
ea0f4fd
Expose D64 mount-grade block and filesystem drivers
Hawkynt Aug 28, 2026
f19326e
Preserve CBM DOS file types and locked-file semantics
Hawkynt Aug 28, 2026
29f7f23
Add mount-safe append-retarget G64 track device
Hawkynt Aug 28, 2026
d4b636d
Bridge G64 and NIB sector media to CBM DOS sessions
Hawkynt Aug 28, 2026
718433c
Expose G64 and NIB block and CBM DOS driver layers
Hawkynt Aug 28, 2026
cbbaba4
Make GCR sector projection analyzer-safe
Hawkynt Aug 28, 2026
08bede4
Tighten nibble filesystem probe error handling
Hawkynt Aug 28, 2026
2e454f5
Test D64 mount-grade filesystem session
Hawkynt Aug 28, 2026
77f0163
Test CBM DOS mounts through G64 and NIB sector projection
Hawkynt Aug 28, 2026
358ae28
Add universal filesystem driver derivation layer
Hawkynt Aug 28, 2026
0823102
Test universal filesystem driver derivation
Hawkynt Aug 28, 2026
5e669b7
Add reusable native read-only filesystem session
Hawkynt Aug 28, 2026
ced5f72
Expose native read-only ReFS filesystem driver
Hawkynt Aug 28, 2026
bf1d794
Wire ReFS into filesystem driver contracts
Hawkynt Aug 28, 2026
bfe04d1
Expose filesystem driver derivation through registry
Hawkynt Aug 28, 2026
a2d7f3c
Add block-device adapters for filesystem driver composition
Hawkynt Aug 28, 2026
f186990
Test block-device composition adapters
Hawkynt Aug 28, 2026
fe3567d
Add sidecar filesystem driver adapter contract
Hawkynt Aug 28, 2026
ab7d7b3
Discover filesystem driver sidecars
Hawkynt Aug 28, 2026
1603174
Register generated filesystem driver sidecars
Hawkynt Aug 28, 2026
11264eb
Prefer generated filesystem driver sidecars
Hawkynt Aug 28, 2026
70e6d63
Add native FAT filesystem driver sidecar
Hawkynt Aug 28, 2026
d5cda6c
Test native FAT driver sidecar
Hawkynt Aug 28, 2026
14813af
Consolidate logical block-device provider contract
Hawkynt Aug 28, 2026
105c125
Separate filesystem objects from directory entries
Hawkynt Aug 28, 2026
d15152c
Add bounded spooling positional file handle
Hawkynt Aug 28, 2026
ea1adcc
Honor filesystem case semantics in snapshot sessions
Hawkynt Aug 28, 2026
93166f4
Add native ext filesystem driver sidecar
Hawkynt Aug 28, 2026
330ad64
Track structural filesystem driver coverage
Hawkynt Aug 29, 2026
bb14cb6
Make filesystem driver coverage a registry invariant
Hawkynt Aug 29, 2026
7de9fd8
Mark FileSystem descriptors during generated registration
Hawkynt Aug 29, 2026
1027069
Enforce driver path for every filesystem descriptor
Hawkynt Aug 29, 2026
d53c716
Test ext native filesystem driver sidecar
Hawkynt Aug 29, 2026
8dbcee6
Add native NTFS read-only filesystem driver sidecar
Hawkynt Aug 29, 2026
3234f9f
Test native NTFS filesystem driver sidecar
Hawkynt Aug 29, 2026
30e00af
Finalize registry only after driver coverage validates
Hawkynt Aug 29, 2026
5a6edd4
Add native XFS read-only filesystem driver sidecar
Hawkynt Aug 29, 2026
3224416
Test native XFS filesystem driver sidecar
Hawkynt Aug 29, 2026
0bfb36f
Add one-shot driver architecture cleanup
Hawkynt Aug 29, 2026
b9a2ea9
Run one-shot driver architecture cleanup
Hawkynt Aug 29, 2026
cd735fc
Add native APFS single-extent filesystem driver sidecar
Hawkynt Aug 29, 2026
6c87682
Test native APFS filesystem driver sidecar
Hawkynt Aug 29, 2026
1e3a163
Propagate registry initialization failures to all callers
Hawkynt Aug 29, 2026
acb43a3
Make filesystem project graph exhaustive in cleanup
Hawkynt Aug 29, 2026
65d9c6f
Classify filesystem descriptors from Roslyn namespaces
Hawkynt Aug 29, 2026
2566219
Add bounded temporary-file archive entry fallback
Hawkynt Aug 29, 2026
f5afb9d
Finish filesystem driver derivation cleanup
github-actions[bot] Aug 29, 2026
a327707
Remove whole-file RAM ceiling from default OpenEntry
Hawkynt Aug 29, 2026
9e0d753
Add native ZFS filesystem driver sidecar
Hawkynt Aug 29, 2026
5678746
Test native ZFS filesystem driver sidecar
Hawkynt Aug 29, 2026
9bad652
Add native Btrfs logical extent mapper
Hawkynt Aug 29, 2026
f8db37e
Add native Btrfs filesystem driver sidecar
Hawkynt Aug 29, 2026
978c0d5
Test native Btrfs filesystem driver sidecar
Hawkynt Aug 29, 2026
60b5ad1
Remove unused Btrfs driver root field
Hawkynt Aug 29, 2026
a00e3da
Document filesystem driver coverage and native tiers
Hawkynt Aug 29, 2026
18a0d89
Test Btrfs native driver fails closed on compressed extents
Hawkynt Aug 29, 2026
8fb18ab
Test ZFS native driver fails closed on unsupported pool versions
Hawkynt Aug 29, 2026
b57c455
fix(ext): compute inode allocation bytes without mixed signed arithmetic
Hawkynt Aug 29, 2026
b861b7a
# the branch did not compile: three leftovers from the promotion work
Hawkynt Aug 29, 2026
7815786
ci: apply ext arithmetic fix once
Hawkynt Aug 29, 2026
a2d6aef
ci: remove queued one-shot ext fixer
Hawkynt Aug 29, 2026
c0764c8
ci: stage merge-readiness capability fixes
Hawkynt Aug 29, 2026
9b8be33
ci: run merge-readiness capability audit once
Hawkynt Aug 29, 2026
a43e909
ci: wire generic archive optimizer during readiness pass
Hawkynt Aug 29, 2026
7a3cb54
ci: include generic optimizer wiring in readiness pass
Hawkynt Aug 29, 2026
6f702f2
ci: close purge verification handle in readiness pass
Hawkynt Aug 29, 2026
be5069b
ci: finish purge CLI readiness fix
Hawkynt Aug 29, 2026
8227537
ci: generate truthful maintenance matrices in readiness pass
Hawkynt Aug 29, 2026
8994bdf
ci: include package maintenance matrices in readiness pass
Hawkynt Aug 29, 2026
bcc3036
ci: enforce rebuild-backed CanModify consistency
Hawkynt Aug 29, 2026
5caaba9
ci: enforce modify capability consistency in readiness pass
Hawkynt Aug 29, 2026
312d283
ci: cancel obsolete readiness runs
Hawkynt Aug 29, 2026
9cf9207
ci: regenerate exhaustive maintenance coverage in readiness pass
Hawkynt Aug 29, 2026
16b2dd2
ci: include exhaustive coverage regeneration
Hawkynt Aug 29, 2026
214ae8e
* regenerate the package API reference for the added types
Hawkynt Aug 29, 2026
da76f63
# the one-shot optimizer wiring emitted names ArchiveOperations canno…
Hawkynt Aug 29, 2026
51532a2
add deterministic WPF screenshot mode
Hawkynt Aug 29, 2026
265986f
route documentation captures through screenshot mode
Hawkynt Aug 29, 2026
7736e82
add idempotent README screenshot section updater
Hawkynt Aug 29, 2026
0d7b60e
remove duplicate CI concurrency declaration
Hawkynt Aug 29, 2026
a0693e6
generate README screenshots on non-main pushes
Hawkynt Aug 29, 2026
e5152d7
docs: plan virtual filesystem mounting architecture
Hawkynt Aug 29, 2026
0652b2d
surface screenshot generation failures in CI
Hawkynt Aug 29, 2026
f0fc63c
print screenshot diagnostics on workflow failure
Hawkynt Aug 29, 2026
7ca6031
add mount-neutral capability resolver
Hawkynt Aug 29, 2026
ba1d820
ci: stabilize branch screenshot generation
Hawkynt Aug 30, 2026
1c4c155
+ NativeForms mount frontend shell
Hawkynt Aug 30, 2026
a6f81fd
ci: avoid bundling screenshot host
Hawkynt Aug 30, 2026
d9ae947
docs: refresh branch screenshots [skip ci]
github-actions[bot] Aug 30, 2026
06a09ac
+ mount resolver policy tests
Hawkynt Aug 30, 2026
d79ead5
+ mount image session launcher
Hawkynt Aug 30, 2026
465ba7c
+ Dokan runtime backend skeleton
Hawkynt Aug 30, 2026
e8915cd
+ compose NativeUI mount launcher
Hawkynt Aug 30, 2026
fd9e4d2
docs/ci: verify capability coverage from live registry
Hawkynt Aug 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/cli-purge-fix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from pathlib import Path
p = Path('Compression.CLI/Program.cs')
t = p.read_text()
old = ''' var before = ops.List(File.OpenRead(archive.FullName), null).Count(e => !e.IsDirectory);'''
new = ''' int before;\n using (var source = File.OpenRead(archive.FullName))\n before = ops.List(source, null).Count(e => !e.IsDirectory);'''
if old not in t:
raise SystemExit('generated purge read handle anchor not found')
p.write_text(t.replace(old, new, 1))
Path('.github/cli-purge-fix.py').unlink()
94 changes: 94 additions & 0 deletions .github/coverage-matrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from pathlib import Path
import re


def scan(root):
rows=[]
for path in sorted(Path(root).glob('**/*FormatDescriptor.cs')):
text=path.read_text(errors='ignore')
cm=re.search(r'public\s+(?:sealed\s+)?class\s+\w+FormatDescriptor\s*:\s*(.*?)\{',text,re.S)
mid=re.search(r'public\s+string\s+Id\s*=>\s*"([^"]+)"',text)
if not cm or not mid: continue
interfaces=set(re.findall(r'\bI[A-Za-z0-9_]+\b',cm.group(1)))
tunable='FormatOptionKind.Boolean' in text or re.search(r'AllowedValues\s*:\s*\[[^\]]*,',text,re.S)
optimize=('ILayoutOptimizable' in interfaces or
('IFormatOptionsSchema' in interfaces and tunable and
('IArchiveCreatable' in interfaces or 'IStreamFormatOperations' in interfaces)))
row={
'id':mid.group(1), 'ifaces':interfaces,
'defrag':'IArchiveDefragmentable' in interfaces,
'shrink':'IArchiveShrinkable' in interfaces,
'wipe':'IWipeEmpty' in interfaces,
'purge':'IArchivePurgeable' in interfaces or 'IArchiveModifiable' in interfaces,
'optimize':optimize,
'meta':'IFileInternalLayoutMap' in interfaces or 'IFileInternalChunkMover' in interfaces,
}
row['compact']=row['defrag'] or row['shrink'] or row['optimize']
rows.append(row)
return rows


def mark(v): return '✅' if v else '—'

def fs_section(rows):
lines=[
'## Filesystem descriptors', '',
'Generated from the descriptor capability interfaces in this tree. **Compact** is the composite '
'defrag → optimize → shrink action and is available when at least one of those primitives is executable. '
'A checkmark may represent a native in-place operation or a verified offline rebuild; mounted-driver R/W '
'is tracked separately by the filesystem-driver readiness model.', '',
'| Format | Optimize | Wipe / clean | Purge | Defrag | Shrink | Compact |',
'| --- | :---: | :---: | :---: | :---: | :---: | :---: |',
]
for r in sorted(rows,key=lambda r:r['id'].lower()):
lines.append(f"| {r['id']} | {mark(r['optimize'])} | {mark(r['wipe'])} | {mark(r['purge'])} | {mark(r['defrag'])} | {mark(r['shrink'])} | {mark(r['compact'])} |")
return '\n'.join(lines)


def archive_section(rows):
visible=[r for r in rows if any(r[k] for k in ('optimize','wipe','purge','defrag','shrink','compact','meta'))]
lines=[
'## Archive / stream descriptors with at least one operation', '',
'Archive **defrag** includes verified repack/relayout (including solid-stream regrouping where the format supports it). '
'**Optimize** includes layout tuning and finite compression/dictionary/solid-block parameter search; candidates only win '
'after round-trip verification.', '',
'| Format | Optimize | Wipe / clean | Purge | Defrag | Shrink | Compact | Meta reorder |',
'| --- | :---: | :---: | :---: | :---: | :---: | :---: | :---: |',
]
for r in sorted(visible,key=lambda r:r['id'].lower()):
lines.append(f"| {r['id']} | {mark(r['optimize'])} | {mark(r['wipe'])} | {mark(r['purge'])} | {mark(r['defrag'])} | {mark(r['shrink'])} | {mark(r['compact'])} | {mark(r['meta'])} |")
return '\n'.join(lines)

p=Path('docs/OPERATION_COVERAGE.md')
t=p.read_text()
# Canonical verb definitions and defaults.
t=re.sub(r'\| Purge\s*\| `IArchiveModifiable\.Remove`-all / empty `Create`\s*\|[^\n]*\|',
'| Purge | `IArchivePurgeable` | Erase all live user data, leaving a valid empty container/image; system metadata may be recreated as required by the format. |',t)
t=t.replace('`Remove(all)` is the **purge** verb.', '`IArchivePurgeable.Purge` is the **purge** verb; `IArchiveModifiable` inherits it because full modification includes removing all live user files.')
t=t.replace('A filesystem descriptor therefore gains shrink / defrag / purge by simply declaring\nthe interface', 'A filesystem descriptor therefore gains shrink / defrag / purge by declaring\nthe corresponding interface')
t=t.replace('> **`CanModify` is advertised when the format is a mutable container with a working modify\n> path.** It is withheld only from **read-only-by-design** formats (CramFS, SquashFS) and\n> **create-only** formats — even though a rebuild could synthesise a modified copy, those do\n> not present themselves as editable.',
'''**`CanModify` means the public API can edit an existing instance and produce a verified valid result.**\nThe physical strategy may be in-place, copy-on-write, relayout/repack, or verified rebuild. Native OS mount\nimmutability (for example CramFS/SquashFS/EROFS) is not the same thing as offline image-editor capability.\nMounted-driver write readiness is tracked separately and remains fail-closed until its durability model is proven.''')
t=t.replace('- **CramFS**, **SquashFS** — compressed *read-only* filesystems by design; not presented as editable.\n','')
t=t.replace('- A handful of niche/append-shift formats (MSA per-track RLE; Wrapster/PFS0 header-at-start;\n OVA manifest-over-all-members; MFS-1 bespoke catalog) keep the rebuild-backed verb without\n advertising R/W.\n','- Formats whose correct edit necessarily rewrites headers, manifests, tracks, or whole images still advertise R/W when the verified rebuild is their supported existing-instance mutation strategy.\n')
# Broaden optimize definition to match the runtime implementation.
t=re.sub(r'\| Optimize\s*\| `ILayoutOptimizable`\s*\|[^\n]*\|',
'| Optimize | `ILayoutOptimizable` / tunable `IFormatOptionsSchema` | Search executable layout/compression parameters and keep the smallest/best verified result. |',t)

fs=scan('FileSystems')
arc=scan('FileFormats')
start=t.index('## Filesystem descriptors')
na=t.index('## N/A notes',start)
t=t[:start]+fs_section(fs)+'\n\n'+archive_section(arc)+'\n\n'+t[na:]

# Replace stale totals with source-derived advertised counts so the document and interfaces cannot drift in this pass.
allrows=fs+arc
counts={k:sum(1 for r in allrows if r[k]) for k in ('defrag','wipe','purge','shrink','optimize','meta')}
t=re.sub(r'\| Defragment\s*\| \d+ \|',f"| Defragment | {counts['defrag']} |",t)
t=re.sub(r'\| Wipe\s*\| \d+ \|',f"| Wipe | {counts['wipe']} |",t)
t=re.sub(r'\| Purge\s*\| \d+ \|',f"| Purge | {counts['purge']} |",t)
t=re.sub(r'\| Shrink\s*\| \d+\s*\|',f"| Shrink | {counts['shrink']} |",t)
t=re.sub(r'\| Optimize \(layout\)\| \d+\s*\|',f"| Optimize | {counts['optimize']} |",t)
t=re.sub(r'\| Metadata-reorder\s*\| \d+\s*\|',f"| Metadata-reorder | {counts['meta']} |",t)
t=t.replace('(Counts are `GetArchiveOps(id) is IXxx` over the registered descriptors — i.e.\nwhat the UI/CLI actually gate on.', '(Counts above are regenerated from explicit descriptor capability interfaces in this tree. Runtime marker/flag consistency is enforced by CI; the UI/CLI gate on the same capability contracts.')
p.write_text(t)
Path('.github/coverage-matrix.py').unlink()
66 changes: 66 additions & 0 deletions .github/merge-readiness.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from pathlib import Path


def replace(path, old, new, count=1):
p = Path(path)
text = p.read_text()
if old not in text:
raise SystemExit(f'expected text not found in {path}: {old[:120]!r}')
p.write_text(text.replace(old, new, count))

# ReFS: wire the already-implemented offline editor into the public descriptor.
replace('FileSystems/FileSystem.Refs/RefsFormatDescriptor.cs',
''' IFormatDescriptor,\n IArchiveFormatOperations,\n IFilesystemExtentMap,''',
''' IFormatDescriptor,\n IArchiveFormatOperations,\n IArchiveModifiable,\n IFilesystemExtentMap,''')
replace('FileSystems/FileSystem.Refs/RefsFormatDescriptor.cs',
''' FormatCapabilities.CanList | FormatCapabilities.CanExtract | FormatCapabilities.CanTest |\n FormatCapabilities.SupportsMultipleEntries;''',
''' FormatCapabilities.CanList | FormatCapabilities.CanExtract | FormatCapabilities.CanTest |\n FormatCapabilities.CanModify | FormatCapabilities.SupportsMultipleEntries;''')
replace('FileSystems/FileSystem.Refs/RefsFormatDescriptor.cs',
''' public string Description => "Microsoft ReFS 3.x volume image with native read-only driver projection, namespace/allocation parsing, and offline filesystem-metadata placement support.";''',
''' public string Description => "Microsoft ReFS 3.x volume image with native read-only driver projection, namespace/allocation parsing, offline existing-file replacement/removal, and filesystem-metadata placement support.";''')
anchor = '''\n private static List<ArchiveEntryInfo> ListDiagnosticSurface(Stream stream) {'''
insert = '''\n public void Add(Stream archive, IReadOnlyList<ArchiveInputInfo> inputs)\n => RefsOfflineModifier.Add(archive, inputs);\n\n public void Remove(Stream archive, string[] entryNames)\n => RefsOfflineModifier.Remove(archive, entryNames);\n'''
replace('FileSystems/FileSystem.Refs/RefsFormatDescriptor.cs', anchor, insert + anchor)

# Maintenance marker tests: purge is its own explicit capability.
replace('Compression.Tests/Operations/MarkerInterfaceCoverageTests.cs',
''' ("purge/modify", typeof(IArchiveModifiable)),''',
''' ("purge", typeof(IArchivePurgeable)),\n ("modify", typeof(IArchiveModifiable)),''')

# Generic purge test must exercise the Purge contract, not approximate it with Remove(all).
p = Path('Compression.Tests/Operations/GenericPurgeRoundTripTests.cs')
t = p.read_text()
t = t.replace('''/// Safety net for the broad rollout of the default <see cref="IArchiveModifiable"/>\n/// (verified extract → edit → re-create rebuild). For every filesystem descriptor\n/// using the DEFAULT Remove, this builds a small image, purges it (Remove every\n/// entry), and asserts the result is a valid, listable, empty container — the\n/// <em>purge</em> verb. The rebuild only commits a result that re-lists, so a writer\n/// limitation surfaces as a clean throw (original untouched) rather than corruption.''',
'''/// Safety net for the explicit <see cref="IArchivePurgeable"/> capability. For every\n/// purgeable descriptor that can create a representative probe, this builds an image,\n/// invokes Purge, and asserts the user's live files are gone while the result remains\n/// listable. A descriptor that advertises purge must not silently fail or corrupt.''')
t = t.replace(''' private static IEnumerable<string> ModifiableDefaultIds() =>\n Compression.Tests.Support.CapabilityImplementers.RegisteredIdsExposing(typeof(IArchiveModifiable))\n .Where(id => FormatRegistry.GetArchiveOps(id) is IArchiveCreatable\n && Enum.TryParse<FormatDetector.Format>(id, out _)\n && !Compression.Tests.Support.CapabilityImplementers.DeclaresOwn(id, "Remove", typeof(Stream), typeof(string[])));\n\n [TestCaseSource(nameof(ModifiableDefaultIds))]''',
''' private static IEnumerable<string> PurgeableIds() =>\n Compression.Tests.Support.CapabilityImplementers.RegisteredIdsExposing(typeof(IArchivePurgeable))\n .Where(id => FormatRegistry.GetArchiveOps(id) is IArchiveCreatable\n && Enum.TryParse<FormatDetector.Format>(id, out _));\n\n [TestCaseSource(nameof(PurgeableIds))]''')
t = t.replace(''' var modifiable = (IArchiveModifiable)fmtOps;\n try {\n modifiable.Remove(ms, [.. before]);\n } catch (NotSupportedException) {\n Assert.Pass($"{formatId}: purge cleanly NotSupported (no corruption).");\n return;\n } catch (Exception ex) {\n Assert.Ignore($"{formatId}: purge rebuild failed non-destructively ({ex.GetType().Name}).");\n return;\n }''',
''' var purgeable = (IArchivePurgeable)fmtOps;\n Assert.DoesNotThrow(() => purgeable.Purge(ms),\n $"{formatId}: advertises IArchivePurgeable but purge failed for its own representative image.");''')
p.write_text(t)

# Add a root-level purge verb to the CLI. It is deliberately destructive and requires --yes.
program = Path('Compression.CLI/Program.cs')
t = program.read_text()
insert_after = '''replaceCmd.SetAction((ParseResult ctx) => {\n var archive = ctx.GetValue(replaceArchiveArg)!;\n var name = ctx.GetValue(replaceNameArg)!;\n var file = ctx.GetValue(replaceFileArg)!;\n if (!archive.Exists) { Console.Error.WriteLine($"File not found: {archive.FullName}"); return 1; }\n if (!file.Exists) { Console.Error.WriteLine($"File not found: {file.FullName}"); return 1; }\n\n var opts = new CompressionOptions {\n Method = MethodSpec.Parse(ctx.GetValue(methodOpt)),\n Level = ctx.GetValue(levelOpt),\n Password = ctx.GetValue(passwordOpt),\n };\n\n Console.Write($"Replacing '{name}' in {archive.Name}...");\n var sw = Stopwatch.StartNew();\n ArchiveOperations.Replace(archive.FullName, name, file.FullName, opts);\n sw.Stop();\n Console.WriteLine($" done ({sw.ElapsedMilliseconds}ms)");\n return 0;\n});\n'''
purge_block = '''\n// ── purge ────────────────────────────────────────────────────────────\n\nvar purgeArchiveArg = new Argument<FileInfo>("archive") { Description = "Archive or filesystem image to empty" };\nvar purgeYesOpt = new Option<bool>("--yes", "-y") { Description = "Confirm destructive purge without prompting" };\nvar purgeCmd = new Command("purge", """\n Remove all live user entries while leaving a valid empty container/image.\n This is different from 'wipe': purge removes content; wipe preserves content\n and sanitizes only unused/dead bytes. The format must advertise IArchivePurgeable.\n\n Examples:\n cwb purge disk.d64 --yes\n cwb purge archive.zip --yes\n """) { purgeArchiveArg, purgeYesOpt };\npurgeCmd.SetAction((ParseResult ctx) => {\n var archive = ctx.GetValue(purgeArchiveArg)!;\n if (!archive.Exists) { Console.Error.WriteLine($"File not found: {archive.FullName}"); return 1; }\n if (!ctx.GetValue(purgeYesOpt)) {\n Console.Error.WriteLine("Purge is destructive. Re-run with --yes to remove all live user entries.");\n return 2;\n }\n\n try {\n FormatRegistration.EnsureInitialized();\n var formatId = FormatDetector.Detect(archive.FullName).ToString();\n var ops = FormatRegistry.GetArchiveOps(formatId);\n if (ops is not IArchivePurgeable purgeable) {\n Console.Error.WriteLine($"Format {formatId} does not advertise purge support.");\n return 1;\n }\n\n var before = ops.List(File.OpenRead(archive.FullName), null).Count(e => !e.IsDirectory);\n Console.Write($"Purging {archive.Name} ({formatId}, {before} live file(s))...");\n var sw = Stopwatch.StartNew();\n using (var stream = File.Open(archive.FullName, FileMode.Open, FileAccess.ReadWrite, FileShare.None))\n purgeable.Purge(stream);\n sw.Stop();\n using var verify = File.OpenRead(archive.FullName);\n var after = ops.List(verify, null).Count(e => !e.IsDirectory);\n Console.WriteLine($" done ({sw.ElapsedMilliseconds}ms; {before} -> {after} live file(s))");\n return 0;\n } catch (Exception ex) {\n Console.Error.WriteLine($"Purge failed: {ex.Message}");\n return 1;\n }\n});\n'''
if insert_after not in t:
raise SystemExit('replace command anchor not found in Program.cs')
t = t.replace(insert_after, insert_after + purge_block, 1)
t = t.replace(''' cwb wipe-empty disk.img Zero all unused space in image''',
''' cwb purge disk.img --yes Remove all live user entries\n cwb wipe-empty disk.img Zero all unused space in image''', 1)
t = t.replace(''' listCmd, extractCmd, createCmd, testCmd, addCmd, removeCmd, replaceCmd, infoCmd,''',
''' listCmd, extractCmd, createCmd, testCmd, addCmd, removeCmd, replaceCmd, purgeCmd, infoCmd,''', 1)
program.write_text(t)

# Correct stale operation-model prose without touching generated tables.
coverage = Path('docs/OPERATION_COVERAGE.md')
t = coverage.read_text()
t = t.replace('''No archive descriptor currently implements a dedicated `IArchivePurgeable` interface; purge is represented by `IArchiveModifiable.Remove` over all live entries.''',
'''Purge is an explicit `IArchivePurgeable` capability. `IArchiveModifiable` inherits it because removing all live user entries is a required subset of full modification; generic purge is staged and verified before commit.''')
t = t.replace('''CramFS and SquashFS remain read-only/WORM because their on-disk formats are immutable by design.''',
'''CramFS, SquashFS and EROFS remain read-only when mounted by their native operating-system drivers, but CompressionWorkbench exposes verified offline rebuild-backed modification for supported profiles. Mounted-driver R/W is a separate capability and remains fail-closed where the native format is immutable or crash-consistent mutation is not implemented.''')
coverage.write_text(t)

# Remove the one-shot machinery in the commit it creates.
Path('.github/merge-readiness.py').unlink()
Path('.github/workflows/merge-readiness-once.yml').unlink()
Loading
Loading