bindery-cli 0.48.0__tar.gz → 0.48.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/PKG-INFO +1 -1
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/patchnotes.md +36 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/pyproject.toml +1 -1
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/src/bindery/__init__.py +1 -1
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/src/bindery/cli.py +77 -25
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_help.py +13 -3
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/uv.lock +1 -1
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/.github/FUNDING.yml +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/.github/workflows/ci.yml +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/.github/workflows/publish.yml +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/.gitignore +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/AGENTS.md +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/CLAUDE.md +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/LICENSE +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/README.md +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/docs/screenshots/library-sweep.png +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/logo.svg +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/plugin/__init__.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/project.done +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/roadmap.md +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/run_tests.sh +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/scripts/FastSweep.java +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/scripts/build_plugin.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/scripts/fast_sweep.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/spec.md +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/src/bindery/__main__.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/src/bindery/audit.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/src/bindery/epub.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/src/bindery/library.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/src/bindery/pagination.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/src/bindery/reserialize.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/src/bindery/transforms.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/src/bindery/validate.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/src/bindery/watermark.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_audit.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_cli.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_comment_hyphens.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_cover_tocdrift.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_epub.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_flags_real_core.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_flags_wiring.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_library.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_matcher_hardening.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_pagination.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_plugin.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_reserialize.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_space_renames.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_transforms.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_validate.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_version.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_watermark.py +0 -0
- {bindery_cli-0.48.0 → bindery_cli-0.48.1}/tests/test_xml_hardening.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: bindery-cli
|
|
3
|
-
Version: 0.48.
|
|
3
|
+
Version: 0.48.1
|
|
4
4
|
Summary: Audit EPUB body text and repair broken EPUBs with deterministic, epubcheck-gated fixes; atomic in-place replacement in a Calibre library.
|
|
5
5
|
Project-URL: Homepage, https://github.com/VirInvictus/bindery-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/VirInvictus/bindery-cli
|
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# bindery-cli Patch Notes
|
|
2
2
|
|
|
3
|
+
## v0.48.1 (2026-10-06)
|
|
4
|
+
|
|
5
|
+
### The 12-agent audit: the exit contracts tell the truth
|
|
6
|
+
|
|
7
|
+
Three waves of four lenses audited this help surface. The flag
|
|
8
|
+
inventory verified perfect (30/30 repair flags, zero misses, zero
|
|
9
|
+
fabrications); the defects were the exit contracts and the safety
|
|
10
|
+
silences:
|
|
11
|
+
|
|
12
|
+
- **repair --help stated an exit contract the verb has never honored**
|
|
13
|
+
(trouble returns 1, not 2; a PARTIAL repair writes its output and
|
|
14
|
+
returns 0). The page and --help-json now tell the code's truth, per
|
|
15
|
+
verb: repair 0/written + 1, audit's deliberate inverse contract
|
|
16
|
+
(0 clean; 1 findings; 2 usage/environment; 3 findings plus a failed
|
|
17
|
+
report write), library and the run slices on the unified 0/2/1.
|
|
18
|
+
calibre_gates names the third metadata.db door (run phase3, guarded
|
|
19
|
+
transitively), and the contract admits the missing-stack exit-2 and
|
|
20
|
+
the exit-3 class.
|
|
21
|
+
- **The library page states the no-backup default**: --apply without
|
|
22
|
+
--backup/--backup-inplace replaces in place and keeps no copy of the
|
|
23
|
+
original. --no-validate says the write still happens (status
|
|
24
|
+
unvalidated). --backup names the outside-the-library refusal.
|
|
25
|
+
--only fatals names its --audit/--sweep precondition.
|
|
26
|
+
- audit and doctor got real descriptions (the eight analyzers, the CWD
|
|
27
|
+
library resolution, the one-id --json rule; doctor's always-exit-0
|
|
28
|
+
contract); the --help-repairs preamble names the five always-on
|
|
29
|
+
fixes, defines the gate metric, enumerates the status vocabulary,
|
|
30
|
+
and states the no-worse classes; phase1's exit list gains
|
|
31
|
+
audit-flagged books; repair's positionals, default output name, and
|
|
32
|
+
never-modifies-the-input are stated; the --json envelopes name all
|
|
33
|
+
keys including books; the root help points at doctor/README/spec.
|
|
34
|
+
- One code fix: the no-backup lossy warning now covers
|
|
35
|
+
--strip-stub-docs (it warned for its three siblings only).
|
|
36
|
+
|
|
37
|
+
15 pins updated/added (639 total).
|
|
38
|
+
|
|
3
39
|
## v0.48.0 (2026-10-06)
|
|
4
40
|
|
|
5
41
|
### The closed-Calibre guard, stated contracts, and --help-json
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "bindery-cli"
|
|
7
|
-
version = "0.48.
|
|
7
|
+
version = "0.48.1"
|
|
8
8
|
description = "Audit EPUB body text and repair broken EPUBs with deterministic, epubcheck-gated fixes; atomic in-place replacement in a Calibre library."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
classifiers = [
|
|
@@ -11,7 +11,7 @@ Calibre plugin built from this core.
|
|
|
11
11
|
Single source of truth for the version. pyproject.toml must match VERSION below.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
VERSION = "0.48.
|
|
14
|
+
VERSION = "0.48.1"
|
|
15
15
|
__version__ = VERSION
|
|
16
16
|
|
|
17
17
|
from .epub import RepairReport, repair_epub # noqa: E402
|
|
@@ -420,17 +420,33 @@ class _HelpJsonAction(argparse.Action):
|
|
|
420
420
|
"tool": "bindery",
|
|
421
421
|
"version": __version__,
|
|
422
422
|
"exit_contract": {
|
|
423
|
-
"0": "clean",
|
|
424
|
-
"1": "
|
|
425
|
-
"
|
|
426
|
-
"
|
|
427
|
-
"
|
|
428
|
-
"
|
|
423
|
+
"0": "clean (repair: also written, including partial)",
|
|
424
|
+
"1": "library/run slices: trouble found; repair: trouble "
|
|
425
|
+
"(refused/failed/unreadable) or invocation problem; audit: "
|
|
426
|
+
"FINDINGS (the inverse contract, per spec.md); "
|
|
427
|
+
"lock-class refusals (Calibre open at a metadata.db door)",
|
|
428
|
+
"2": "library/run slices: trouble found (rejected/error/"
|
|
429
|
+
"unreadable/partial books; unscoped phase3 sweep); audit: "
|
|
430
|
+
"usage/environment errors; argparse usage errors; missing "
|
|
431
|
+
"vir_tui/cquarry on Python < 3.14",
|
|
432
|
+
"3": "audit: findings PLUS a failed --json/--tag write",
|
|
429
433
|
"130": "interrupted",
|
|
434
|
+
"per_verb": {
|
|
435
|
+
"repair": "0 clean/written (partial writes, exit 0); "
|
|
436
|
+
"1 trouble or invocation problem; never 2",
|
|
437
|
+
"audit": "0 clean; 1 findings; 2 usage/environment; "
|
|
438
|
+
"3 findings + failed report write",
|
|
439
|
+
"library": "0 clean; 2 trouble (rejected/error/"
|
|
440
|
+
"unreadable/partial); 1 invocation problem",
|
|
441
|
+
"run phase1": "0 clean; 2 trouble (audit-flagged, "
|
|
442
|
+
"rejected, partial, error, unreadable); 1 invocation",
|
|
443
|
+
},
|
|
430
444
|
},
|
|
431
445
|
"calibre_gates": [
|
|
432
446
|
"audit --tag",
|
|
433
447
|
"library --apply --install-to-calibre",
|
|
448
|
+
"run phase3 (transitively: it wraps library --apply "
|
|
449
|
+
"--install-to-calibre)",
|
|
434
450
|
],
|
|
435
451
|
"subcommands": [],
|
|
436
452
|
"repair_reference": [
|
|
@@ -632,7 +648,12 @@ def run_library(args) -> int:
|
|
|
632
648
|
if (
|
|
633
649
|
args.apply
|
|
634
650
|
and not wants_backup
|
|
635
|
-
and (
|
|
651
|
+
and (
|
|
652
|
+
args.strip_pagination
|
|
653
|
+
or args.strip_broken_tags
|
|
654
|
+
or args.strip_watermarks
|
|
655
|
+
or args.strip_stub_docs
|
|
656
|
+
)
|
|
636
657
|
):
|
|
637
658
|
print(
|
|
638
659
|
"WARNING: the --strip-* modes are lossy; strongly consider --backup DIR "
|
|
@@ -1841,12 +1862,21 @@ def _repair_flag_reference(color: bool | None = None) -> str:
|
|
|
1841
1862
|
+ " repair-flag reference (shared by `bindery repair` and "
|
|
1842
1863
|
"`bindery library`)",
|
|
1843
1864
|
"",
|
|
1844
|
-
"The always-on core pass is exactly five well-formedness fixes
|
|
1845
|
-
"
|
|
1846
|
-
"
|
|
1847
|
-
"
|
|
1848
|
-
"
|
|
1849
|
-
"
|
|
1865
|
+
"The always-on core pass is exactly five well-formedness fixes",
|
|
1866
|
+
"(strip prolog junk, drop duplicate xmlns, escape bare ampersands,",
|
|
1867
|
+
"fix named entities, self-close void tags), the NCX pipeline, and",
|
|
1868
|
+
"the mimetype fix. Everything below is opt-in, and every repair is",
|
|
1869
|
+
"epubcheck-gated: applied only when the measured result improved",
|
|
1870
|
+
"(strictly fewer findings than before); the lossy strips, the",
|
|
1871
|
+
"cover wiring repairs, and the space-entry renames are held to the",
|
|
1872
|
+
"weaker no-worse bar (not more findings). Library mode is dry-run",
|
|
1873
|
+
"by default; `--all` enables every flag below at once. Statuses a",
|
|
1874
|
+
"record can carry: accept (gate-applied), partial (written but",
|
|
1875
|
+
"still failing epubcheck), reject (the gate refused), error,",
|
|
1876
|
+
"unreadable, nochange (nothing to do), equal (already as good as",
|
|
1877
|
+
"the result), unvalidated (applied under --no-validate, never",
|
|
1878
|
+
"gate-checked). The README and spec.md carry the full rationale",
|
|
1879
|
+
"for each repair.",
|
|
1850
1880
|
]
|
|
1851
1881
|
for title, rows in _REPAIR_FLAG_TABLE.items():
|
|
1852
1882
|
lines.append("")
|
|
@@ -1943,12 +1973,15 @@ def _add_repair_flags(p: argparse.ArgumentParser) -> None:
|
|
|
1943
1973
|
g.add_argument(
|
|
1944
1974
|
"--all",
|
|
1945
1975
|
action="store_true",
|
|
1946
|
-
help="enable every opt-in fix flag (safe, structural, and lossy)"
|
|
1976
|
+
help="enable every opt-in fix flag (safe, structural, and lossy); "
|
|
1977
|
+
"this IS the lossy consent here (run phase1's separate "
|
|
1978
|
+
"--apply-lossy door is the phase1 equivalent)",
|
|
1947
1979
|
)
|
|
1948
1980
|
g.add_argument(
|
|
1949
1981
|
"--no-validate",
|
|
1950
1982
|
action="store_true",
|
|
1951
|
-
help="skip the epubcheck gate"
|
|
1983
|
+
help="skip the epubcheck gate: the repair still writes/appends "
|
|
1984
|
+
"with status unvalidated (no gate check runs)",
|
|
1952
1985
|
)
|
|
1953
1986
|
p.add_argument(
|
|
1954
1987
|
"--help-repairs",
|
|
@@ -2051,9 +2084,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2051
2084
|
"core pass: the five well-formedness fixes, the NCX pipeline, and the "
|
|
2052
2085
|
"mimetype fix run always; every other repair is opt-in (grouped "
|
|
2053
2086
|
"below) and applied only when epubcheck confirms the result improved. "
|
|
2054
|
-
"Never prompts. Exit codes: 0 clean
|
|
2055
|
-
"
|
|
2056
|
-
"
|
|
2087
|
+
"Never prompts. Exit codes: 0 clean or written (a PARTIAL repair "
|
|
2088
|
+
"writes its best output and exits 0; the --json record's status "
|
|
2089
|
+
"says so), 1 trouble (repair refused by the gate, epubcheck "
|
|
2090
|
+
"failure, or unreadable input) or invocation problem (bad args, "
|
|
2091
|
+
"or an existing output without --force). This verb does not use "
|
|
2092
|
+
"the tool-wide trouble=2 contract; library and the run slices do.",
|
|
2057
2093
|
epilog="examples:\n"
|
|
2058
2094
|
" bindery repair book.epub out.epub\n"
|
|
2059
2095
|
" bindery repair book.epub out.epub --all\n"
|
|
@@ -2084,12 +2120,25 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2084
2120
|
"doctor",
|
|
2085
2121
|
help="check the environment: Python stack tier, epubcheck, Java, "
|
|
2086
2122
|
"html5lib, and Calibre-library discovery",
|
|
2123
|
+
description="Check the environment and exit 0 ALWAYS: the output "
|
|
2124
|
+
"is the diagnosis, never a failure (run this first). Checks: the "
|
|
2125
|
+
"Python stack tier, epubcheck, Java, html5lib, and Calibre-library "
|
|
2126
|
+
"discovery.",
|
|
2087
2127
|
)
|
|
2088
2128
|
doc.set_defaults(func=run_doctor)
|
|
2089
2129
|
|
|
2090
2130
|
audit = sub.add_parser(
|
|
2091
2131
|
"audit",
|
|
2092
2132
|
help="audit EPUB body text to detect non-schema content flaws (OCR damage, hardcoded page numbers, empty books, non-English text)",
|
|
2133
|
+
description="Read-only content audit (eight analyzers: monolithic "
|
|
2134
|
+
"scans, completeness, cover wiring, toc drift, and the body-text "
|
|
2135
|
+
"battery over OCR damage, hardcoded page numbers, empty/thin "
|
|
2136
|
+
"books, and non-English text). Library mode resolves the Calibre "
|
|
2137
|
+
"library from the CURRENT DIRECTORY's metadata.db, or pass a "
|
|
2138
|
+
"directory, or --id for specific books (--id plus --json takes "
|
|
2139
|
+
"exactly one id). Exit codes are the audit inverse: 0 clean, "
|
|
2140
|
+
"1 FINDINGS (flagged books), 2 usage or environment errors, "
|
|
2141
|
+
"3 findings plus a failed --json/--tag write. Never prompts.",
|
|
2093
2142
|
)
|
|
2094
2143
|
audit.add_argument(
|
|
2095
2144
|
"mode",
|
|
@@ -2141,7 +2190,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2141
2190
|
"--id",
|
|
2142
2191
|
metavar="BOOK_IDS",
|
|
2143
2192
|
default=None,
|
|
2144
|
-
help="audit library book(s) by Calibre id
|
|
2193
|
+
help="audit library book(s) by Calibre id: one id or a comma-separated "
|
|
2145
2194
|
"list (fetched via cquarry's single-entity get_book; cannot be "
|
|
2146
2195
|
"combined with a directory)",
|
|
2147
2196
|
)
|
|
@@ -2163,10 +2212,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2163
2212
|
"repair them in place. Dry run by default; --apply atomically "
|
|
2164
2213
|
"replaces accepted books. Shares the repair-flag set with `bindery "
|
|
2165
2214
|
"repair` (--all enables every opt-in). Never prompts: --apply is "
|
|
2166
|
-
"the only confirmation.
|
|
2167
|
-
"
|
|
2168
|
-
"
|
|
2169
|
-
"
|
|
2215
|
+
"the only confirmation. --apply WITHOUT --backup or --backup-inplace "
|
|
2216
|
+
"replaces in place and keeps NO copy of the original. Exit codes: "
|
|
2217
|
+
"0 all clean, 2 any flagged/rejected/error/unreadable/partial book, "
|
|
2218
|
+
"1 invocation problem. --apply --install-to-calibre writes "
|
|
2219
|
+
"metadata.db and demands a closed Calibre (refused, exit 1, while "
|
|
2220
|
+
"it runs).",
|
|
2170
2221
|
epilog="examples:\n"
|
|
2171
2222
|
" bindery library ~/docs/Calibre\\ Library --sweep\n"
|
|
2172
2223
|
" bindery library ~/docs/Calibre\\ Library --id 1234 --apply "
|
|
@@ -2185,7 +2236,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2185
2236
|
choices=("fatals", "ncx", "all"),
|
|
2186
2237
|
default="all",
|
|
2187
2238
|
help="restrict to books with fatals, the NCX-001 mismatch "
|
|
2188
|
-
"(toc.ncx dtb:uid != the OPF unique-identifier), or all (default)"
|
|
2239
|
+
"(toc.ncx dtb:uid != the OPF unique-identifier), or all (default); "
|
|
2240
|
+
"fatals requires --audit CSV or --sweep",
|
|
2189
2241
|
)
|
|
2190
2242
|
rc.add_argument(
|
|
2191
2243
|
"--limit", type=int, help="process at most N candidates (for sampling)"
|
|
@@ -2241,7 +2293,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2241
2293
|
out.add_argument(
|
|
2242
2294
|
"--json",
|
|
2243
2295
|
metavar="FILE",
|
|
2244
|
-
help="write a machine-readable JSON report of the run to FILE Top-level envelope keys: mode (apply|dry-run), root, only, validate, candidates, summary",
|
|
2296
|
+
help="write a machine-readable JSON report of the run to FILE. Top-level envelope keys: mode (apply|dry-run), root, only, validate, candidates, summary, books (one record per candidate)",
|
|
2245
2297
|
)
|
|
2246
2298
|
out.add_argument(
|
|
2247
2299
|
"--manual-list",
|
|
@@ -104,14 +104,18 @@ class TestHelpTruth(unittest.TestCase):
|
|
|
104
104
|
def test_repair_page_states_the_contract(self):
|
|
105
105
|
text = self._sub(self._parser(), "repair").description
|
|
106
106
|
self.assertIn("Never prompts", text)
|
|
107
|
-
self.assertIn("
|
|
107
|
+
self.assertIn("PARTIAL repair writes its best output and exits 0", text)
|
|
108
|
+
self.assertIn("does not use", text)
|
|
108
109
|
self.assertIn("without --force", text)
|
|
109
110
|
|
|
110
111
|
def test_library_page_states_gate_and_codes(self):
|
|
111
112
|
text = self._sub(self._parser(), "library").description
|
|
112
113
|
self.assertIn("Never prompts", text)
|
|
113
114
|
self.assertIn("closed Calibre", text)
|
|
114
|
-
self.assertIn(
|
|
115
|
+
self.assertIn(
|
|
116
|
+
"0 all clean, 2 any flagged/rejected/error/unreadable/partial", text
|
|
117
|
+
)
|
|
118
|
+
self.assertIn("keeps NO copy of the original", text)
|
|
115
119
|
|
|
116
120
|
def test_install_to_calibre_names_the_gate(self):
|
|
117
121
|
parser = self._parser()
|
|
@@ -165,9 +169,15 @@ class TestHelpJson(unittest.TestCase):
|
|
|
165
169
|
list(cli._REPAIR_FLAG_TABLE),
|
|
166
170
|
)
|
|
167
171
|
self.assertEqual(
|
|
168
|
-
data["calibre_gates"],
|
|
172
|
+
data["calibre_gates"][0:2],
|
|
169
173
|
["audit --tag", "library --apply --install-to-calibre"],
|
|
170
174
|
)
|
|
175
|
+
# the third door is phase3, guarded transitively through library
|
|
176
|
+
self.assertIn("run phase3", data["calibre_gates"][2])
|
|
177
|
+
# the contract now carries the per-verb truth
|
|
178
|
+
self.assertEqual(data["exit_contract"]["per_verb"]["repair"][-7:], "never 2")
|
|
179
|
+
self.assertIn("1 findings", data["exit_contract"]["per_verb"]["audit"])
|
|
180
|
+
self.assertIn("3", data["exit_contract"])
|
|
171
181
|
self.assertEqual(payload_text, self._dump())
|
|
172
182
|
|
|
173
183
|
def _dump(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|