patchcraft 0.5.2__tar.gz → 0.5.4__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.
Files changed (89) hide show
  1. patchcraft-0.5.4/.pre-commit-config.yaml +45 -0
  2. patchcraft-0.5.4/AGENTS.md +160 -0
  3. {patchcraft-0.5.2 → patchcraft-0.5.4}/CHANGELOG.md +232 -1
  4. patchcraft-0.5.4/CITATION.cff +51 -0
  5. patchcraft-0.5.4/CODE_OF_CONDUCT.md +83 -0
  6. {patchcraft-0.5.2 → patchcraft-0.5.4}/CONTRIBUTING.md +44 -4
  7. {patchcraft-0.5.2 → patchcraft-0.5.4}/MANIFEST.in +7 -2
  8. patchcraft-0.5.4/MAP.md +107 -0
  9. {patchcraft-0.5.2 → patchcraft-0.5.4}/PKG-INFO +4 -4
  10. {patchcraft-0.5.2 → patchcraft-0.5.4}/README.md +4 -3
  11. {patchcraft-0.5.2 → patchcraft-0.5.4}/README.pt-BR.md +4 -3
  12. {patchcraft-0.5.2 → patchcraft-0.5.4}/README.pypi.md +3 -3
  13. patchcraft-0.5.4/SECURITY.md +73 -0
  14. patchcraft-0.5.4/STATUS.md +97 -0
  15. patchcraft-0.5.4/docs/ADR/0003-reversibility-classes.md +139 -0
  16. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/ADR/0004-precision-and-effort-parameters.md +61 -78
  17. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/AUXILIARY.md +4 -2
  18. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/FOCO-1.0.md +15 -3
  19. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/GUIDE.md +7 -7
  20. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/SCOPE.md +1 -1
  21. patchcraft-0.5.4/docs/STUDIES/2026-09-03-precision-measurements.md +98 -0
  22. patchcraft-0.5.4/docs/STUDIES/2026-09-03-strata-conformity.md +110 -0
  23. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/THEORY.md +5 -3
  24. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/USAGE.md +37 -26
  25. {patchcraft-0.5.2 → patchcraft-0.5.4}/pyproject.toml +4 -0
  26. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/_version.py +3 -3
  27. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/cache.py +5 -1
  28. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/geometry.py +3 -1
  29. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/reconstruct.py +14 -5
  30. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/resize.py +33 -3
  31. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/stitch.py +4 -3
  32. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft.egg-info/PKG-INFO +4 -4
  33. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft.egg-info/SOURCES.txt +11 -0
  34. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft.egg-info/scm_file_list.json +18 -0
  35. patchcraft-0.5.4/src/patchcraft.egg-info/scm_version.json +8 -0
  36. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_cache.py +29 -0
  37. patchcraft-0.5.4/tests/test_citation.py +51 -0
  38. patchcraft-0.5.4/tests/test_docs_usage.py +34 -0
  39. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_reconstruct.py +36 -0
  40. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_resize.py +73 -0
  41. {patchcraft-0.5.2 → patchcraft-0.5.4}/tools/check_dist.py +4 -2
  42. patchcraft-0.5.2/docs/ADR/0003-reversibility-classes.md +0 -108
  43. patchcraft-0.5.2/src/patchcraft.egg-info/scm_version.json +0 -8
  44. {patchcraft-0.5.2 → patchcraft-0.5.4}/.gitignore +0 -0
  45. {patchcraft-0.5.2 → patchcraft-0.5.4}/.python-version +0 -0
  46. {patchcraft-0.5.2 → patchcraft-0.5.4}/LICENSE +0 -0
  47. {patchcraft-0.5.2 → patchcraft-0.5.4}/accel/Cargo.lock +0 -0
  48. {patchcraft-0.5.2 → patchcraft-0.5.4}/accel/Cargo.toml +0 -0
  49. {patchcraft-0.5.2 → patchcraft-0.5.4}/accel/README.md +0 -0
  50. {patchcraft-0.5.2 → patchcraft-0.5.4}/accel/src/kernel.rs +0 -0
  51. {patchcraft-0.5.2 → patchcraft-0.5.4}/accel/src/lib.rs +0 -0
  52. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/ADR/0001-patch-extraction-api.md +0 -0
  53. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/ADR/0002-patchify-transform.md +0 -0
  54. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/PERFORMANCE.md +0 -0
  55. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/ROADMAP.md +0 -0
  56. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/STUDIES/2026-08-04-patch-techniques.md +0 -0
  57. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/design/2026-09-01-fase1-bugs-performance.md +0 -0
  58. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/design/2026-09-01-fase2-acelerador.md +0 -0
  59. {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/design/2026-09-01-fase3-predicado.md +0 -0
  60. {patchcraft-0.5.2 → patchcraft-0.5.4}/setup.cfg +0 -0
  61. {patchcraft-0.5.2 → patchcraft-0.5.4}/setup.py +0 -0
  62. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/__init__.py +0 -0
  63. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/_accel.py +0 -0
  64. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/_foldgeom.py +0 -0
  65. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/extract.py +0 -0
  66. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/metrics.py +0 -0
  67. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/pair.py +0 -0
  68. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/py.typed +0 -0
  69. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft.egg-info/dependency_links.txt +0 -0
  70. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft.egg-info/requires.txt +0 -0
  71. {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft.egg-info/top_level.txt +0 -0
  72. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/__init__.py +0 -0
  73. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/_datasets.py +0 -0
  74. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/_rng.py +0 -0
  75. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/conftest.py +0 -0
  76. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_accel.py +0 -0
  77. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_datasets_helper.py +0 -0
  78. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_exactness.py +0 -0
  79. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_extract.py +0 -0
  80. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_geometry.py +0 -0
  81. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_import.py +0 -0
  82. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_metrics.py +0 -0
  83. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_pair.py +0 -0
  84. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_public_api.py +0 -0
  85. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_reference.py +0 -0
  86. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_rng.py +0 -0
  87. {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_stitch.py +0 -0
  88. {patchcraft-0.5.2 → patchcraft-0.5.4}/tools/benchmark.py +0 -0
  89. {patchcraft-0.5.2 → patchcraft-0.5.4}/uv.lock +0 -0
@@ -0,0 +1,45 @@
1
+ # Runs the gates CI runs, before the commit rather than after it.
2
+ #
3
+ # Deliberately absent: `ruff-format`. This project has never used it and
4
+ # adopting it here would reformat 36 of 65 files in a commit that changed no
5
+ # behaviour. `ruff check` is the gate; formatting is not.
6
+ #
7
+ # `mypy` runs through the project environment rather than through a pre-commit
8
+ # virtualenv, because it needs torch to resolve any annotation in `src/` and
9
+ # installing torch into an isolated hook environment would cost hundreds of
10
+ # megabytes per hook update.
11
+ #
12
+ # Install: see the "Run tests" section of CONTRIBUTING.md. On a machine with a
13
+ # global `core.hooksPath`, `pre-commit install` writes to `.git/hooks/`, which
14
+ # git then ignores, and the hook is silently inert. CONTRIBUTING says how to
15
+ # check.
16
+
17
+ repos:
18
+ - repo: https://github.com/pre-commit/pre-commit-hooks
19
+ rev: v6.0.0
20
+ hooks:
21
+ - id: check-added-large-files
22
+ args: ["--maxkb=512"]
23
+ - id: check-merge-conflict
24
+ - id: check-toml
25
+ - id: check-yaml
26
+ - id: end-of-file-fixer
27
+ - id: trailing-whitespace
28
+ - id: mixed-line-ending
29
+ args: ["--fix=no"]
30
+
31
+ - repo: https://github.com/astral-sh/ruff-pre-commit
32
+ rev: v0.16.5
33
+ hooks:
34
+ - id: ruff-check
35
+ args: ["src", "tests", "tools"]
36
+ pass_filenames: false
37
+
38
+ - repo: local
39
+ hooks:
40
+ - id: mypy-strict
41
+ name: mypy --strict src
42
+ entry: mypy --strict src
43
+ language: system
44
+ types: [python]
45
+ pass_filenames: false
@@ -0,0 +1,160 @@
1
+ # PatchCraft: the canonical guide
2
+
3
+ > **Single source, agent-agnostic.** The branded files ([`CLAUDE.md`](CLAUDE.md))
4
+ > point here and do not duplicate content. On any disagreement, this file wins.
5
+ >
6
+ > **Route**: this guide = *the rules* · [`MAP.md`](MAP.md) = *where things are* ·
7
+ > [`CONTRIBUTING.md`](CONTRIBUTING.md) = *how to run the gates and release* ·
8
+ > [`STATUS.md`](STATUS.md) = *where things stand now* ·
9
+ > [`docs/FOCO-1.0.md`](docs/FOCO-1.0.md) = *what is left before 1.0*.
10
+ > Do not repeat between them. Link.
11
+
12
+ ## 0. The invariants
13
+
14
+ Each of these was earned. Where one names an incident, the incident is real and
15
+ is in the changelog.
16
+
17
+ ### I1. Measure before you write it down
18
+
19
+ Every number in this repository has a command that reproduces it. A sentence
20
+ that says "faster", "exact" or "negligible" without one is a defect waiting to
21
+ be found, and several have been.
22
+
23
+ The failures this rule comes from: a predicate published as `k_max <= 4`,
24
+ measured false and retracted in public; a benchmark that reported the
25
+ byte-native path as nine times slower because `torch`'s `uint8.sum()` silently
26
+ promotes to `int64`, so it measured the opposite of what it claimed; a security
27
+ note asserting `Cache` runs `torch.load`, which it does not.
28
+
29
+ Recalling is not measuring. Reading the code is not measuring either, when the
30
+ question is numeric.
31
+
32
+ ### I2. `docs/THEORY.md` §9 is the arbiter
33
+
34
+ It says so itself. When two documents disagree about what a function accepts,
35
+ rejects or returns, §9 is the truth and the other document is the bug. When §9
36
+ and the code disagree, decide which is wrong before changing either, and say
37
+ which in the commit.
38
+
39
+ §9 has been wrong twice. That is not a reason to trust it less; it is why the
40
+ rule exists.
41
+
42
+ ### I3. A fix ships with a test that fails on the old code
43
+
44
+ Without one it is not a fix, it is a coincidence. The suite already contains
45
+ several tests whose only job is to prevent a specific defect from returning,
46
+ and each names the measurement in its docstring.
47
+
48
+ ### I4. The exactness suite exists to falsify, not to confirm
49
+
50
+ `tests/test_exactness.py` enumerates the legal geometry space independently of
51
+ the predicate and hunts for both counterexamples: a case inside the rule that
52
+ is not exact, and a case outside it that is exact by luck. If you change the
53
+ predicate, the enumerator must not learn about the change.
54
+
55
+ ### I5. The public surface is 20 names, frozen by test
56
+
57
+ `tests/test_public_api.py` pins the names, their signatures and the fields of
58
+ the four data carriers. Adding a name is a decision recorded in an ADR, never a
59
+ convenience taken mid-task. [`docs/FOCO-1.0.md`](docs/FOCO-1.0.md) says what
60
+ 1.0 freezes.
61
+
62
+ ### I6. Both code paths stay green
63
+
64
+ The library has a pure-torch path and, on five of six wheels, a Rust one. They
65
+ are bit-identical by test. Any change to the fold, the count map or the
66
+ accelerator must run both: `pytest -m "not gpu"` with the extension built, and
67
+ again with `PATCHCRAFT_ACCEL=0`.
68
+
69
+ ### I7. The version answers one question
70
+
71
+ Not "did a returned value change", because a fix changes values by definition.
72
+ The question is whether the **contract moved** (`0.Y.0`) or the
73
+ **implementation stopped violating it** (`0.y.Z`). The rule and its four worked
74
+ examples are in [`CONTRIBUTING.md`](CONTRIBUTING.md), and getting it wrong is
75
+ what nearly shipped 0.5.2 as 0.6.0.
76
+
77
+ ### I8. Nothing ephemeral lives in the project folder
78
+
79
+ Caches, build output and virtual environments are redirected by user-level
80
+ environment variables. `accel/target` reached 269 MiB inside the tree before
81
+ this was enforced. Never commit a machine path to fix it: that mistake is
82
+ already in the history, as a `cache_dir = "Z:\caches\pytest"` that created a
83
+ literal directory of that name on Linux.
84
+
85
+ ### I9. A tracked file ships to PyPI unless two files say otherwise
86
+
87
+ `setuptools-scm`'s file finder offers every git-tracked file to the sdist, so
88
+ `MANIFEST.in` is a blocklist over everything git knows about, and
89
+ `tools/check_dist.py` holds the matching allowlist. Adding a top-level
90
+ directory means updating both. The gate will tell you; do not silence it.
91
+
92
+ ### I10. An ADR carries the choice, not the research behind it
93
+
94
+ Before writing one, split what is being said into what is **determined** and
95
+ what is **chosen**, and put only the second in the ADR.
96
+
97
+ A name is determined: which identifier a parameter carries is a fact about the
98
+ ecosystem the caller already imports, and picking one by taste is a guess made
99
+ permanent by a frozen surface. A measurement is determined. A benchmark table is
100
+ determined. Those belong in `docs/STUDIES/`, dated, with the ADR linking to
101
+ them.
102
+
103
+ What is chosen is whether to do the thing at all, when, and what it costs the
104
+ surface. If that part comes out empty, it was never an ADR: it was a finding,
105
+ and a study is where it goes.
106
+
107
+ Both ADRs written in 2026-09 failed this on the first draft, in the same way
108
+ and for the same reason, which is that writing down everything you learned feels
109
+ like thoroughness and reads as a lab notebook. ADR 0003 mixed a rule with a
110
+ vocabulary for six transforms that never shipped; ADR 0004 mixed the names,
111
+ which precedent decides, with whether to spend a frozen surface on them, which
112
+ only the owner decides. Both came out clearer once separated, and both were
113
+ caught by the owner asking what the document was for.
114
+
115
+ ### I11. Do not invent what already exists
116
+
117
+ Check for an official or established solution before writing one. The
118
+ accelerator's packaging, the versioning rule, the citation format and the
119
+ outreach layout were all taken from prior art rather than designed here, and
120
+ each is better for it. Writing something new is fine when nothing fits; writing
121
+ it without looking is not.
122
+
123
+ ## 1. Writing
124
+
125
+ Prose in this repository has no em-dashes. Replace the semantic role with a
126
+ sentence that carries it, never with another symbol. Keep articles and
127
+ connectives; one idea per paragraph. `README.md` is a call page, not a manual:
128
+ the manual is [`docs/GUIDE.md`](docs/GUIDE.md), and complex examples belong
129
+ there.
130
+
131
+ Documents that state a version go stale. There is exactly one version string
132
+ left in the documentation, the BibTeX entry in GUIDE section 9, plus
133
+ [`CITATION.cff`](CITATION.cff). Do not add a third.
134
+
135
+ ## 2. Before you touch anything
136
+
137
+ ```
138
+ uv sync --locked --extra cache --extra dev
139
+ ruff check src tests tools
140
+ mypy --strict src
141
+ pytest -m "not gpu"
142
+ ```
143
+
144
+ For a change to the fold, the predicate or the accelerator, add:
145
+
146
+ ```
147
+ PATCHCRAFT_SWEEP_FULL=1 pytest tests/test_exactness.py
148
+ cargo test --manifest-path accel/Cargo.toml
149
+ ```
150
+
151
+ The same gates run as a pre-commit hook. On a machine with a global
152
+ `core.hooksPath`, that hook is installed but inert;
153
+ [`CONTRIBUTING.md`](CONTRIBUTING.md) says how to tell.
154
+
155
+ ## 3. What this project is not
156
+
157
+ No batch axis, no dataset, no dataloader, no model, no padding to make an
158
+ awkward geometry fit. Those are out by decision and the line is drawn in
159
+ [`docs/SCOPE.md`](docs/SCOPE.md) and [`docs/THEORY.md`](docs/THEORY.md) §0.
160
+ A request to add one of them is a scope conversation, not a feature.
@@ -6,6 +6,235 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.5.4] - 2026-09-04
10
+
11
+ ### Fixed
12
+
13
+ - **13 of the 25 resample-mode x integer-dtype combinations raised a raw torch
14
+ error** instead of resizing. THEORY §9.4 accepts any integer tensor on the
15
+ `torch` backend and promises interpolation in float with round + clamp on the
16
+ way back, unconditionally. `_resize_torch` guarded the promotion with
17
+ `mode in {"bilinear", "bicubic"}`, so an integer tensor reached
18
+ `F.interpolate` unpromoted under `nearest`, `area` and `nearest-exact` and
19
+ surfaced `NotImplementedError: "adaptive_avg_pool2d" not implemented for
20
+ 'Int'`, naming an internal op the caller never called. The outbound half of
21
+ the same function already did the right thing for every integer dtype with no
22
+ condition on the mode, so only the inbound guard was ever wrong.
23
+
24
+ The suite could not catch it because the two parametrizations that would have
25
+ never crossed: `test_torch_accepts_all_resamples` sweeps all five modes on
26
+ `torch.rand`, and every integer test used the default mode. The regression is
27
+ that crossing, 50 cases, plus the upsampling direction because `area` is
28
+ `adaptive_avg_pool2d` and behaves differently going up.
29
+
30
+ Fixing it by promoting everything through `float32` would have traded the
31
+ loud error for the silent truncation this library exists to prevent, so the
32
+ promotion width is now derived from the dtype's range: the narrowest float
33
+ that represents it exactly, `float32` for `uint8`, `int8` and `int16` and
34
+ `float64` for `int32`. That mirrors `patchcraft.metrics`, which already
35
+ promotes to `float64` for the same reason. **This also fixes a silent defect
36
+ that predates the one above**: on the bilinear path an `int32` tensor was
37
+ already being promoted through `float32`, so `2**24 + 1` came back as
38
+ `2**24`. Measured on 2026-09-04, on every mode. `int64` remains the one dtype
39
+ no float covers, and §9.4 now says so rather than implying otherwise.
40
+
41
+ - **`Cache("~/cache")` created a directory literally named `~`** in the working
42
+ directory instead of using the home directory. `Path(root)` was used without
43
+ `expanduser`. Every established library expands and then stops: verified that
44
+ `torch.hub.set_dir` calls `expanduser` and validates nothing, that pytest's
45
+ cacheprovider does not validate, and that pip passes `--cache-dir` through.
46
+ `Cache` now does the same one thing they do.
47
+
48
+ The question that found this was whether to reject a `namespace` containing
49
+ `..`. The answer is no, and the reason is the owner's: it is not this
50
+ library's job to invent path security, and no library in the ecosystem does
51
+ it. What it was missing was the one thing they all do. `SECURITY.md` and
52
+ THEORY §9.5 now say that the path is the caller's and that only `~` is
53
+ expanded.
54
+
55
+
56
+ ### Added
57
+
58
+ - The rule that produced the two rescopings is recorded as invariant I10 in
59
+ `AGENTS.md`: an ADR carries the choice, not the research behind it. Before
60
+ writing one, split what is determined from what is chosen and keep only the
61
+ second; if the chosen part comes out empty it was a finding, and a dated
62
+ study is where it goes. Both ADRs written this month failed it on the first
63
+ draft, in the same way, and both were caught by the owner asking what the
64
+ document was for. `docs/ADR/0001` and `0002` were checked and do not have the
65
+ problem: they are context, decision, consequences and alternatives, at a
66
+ hundred lines each.
67
+ - **ADR 0004 separates what is determined from what is chosen, and what is left
68
+ is nothing.** The owner's point settled the first half: naming is a fact about
69
+ the ecosystem the caller already imports, not a preference of this project,
70
+ and choosing by taste would be a guess made permanent by a frozen surface. So
71
+ the three names are stated as determined by precedent, verified on 2026-09-03
72
+ against the installed libraries: `numpy.sum` and `torch.sum` take `dtype=`,
73
+ `torch.div` takes `rounding_mode=` and documents exactly `None`, `"trunc"` and
74
+ `"floor"`, both `interpolate` and torchvision's `resize` take `antialias=`,
75
+ and `numpy.ufunc.accumulate` exists, which is the collision that rules out
76
+ `accumulate=`.
77
+
78
+ That narrowing left one apparent question, whether to add the knobs before the
79
+ 1.0 freeze or after, on the stated ground that afterwards each costs a minor
80
+ release. Applying I10 to it dissolved it, because the premise is false: every
81
+ one of these is a keyword-only argument with a default, so adding one is
82
+ additive and can never be a major, and `reconstruct` has no positional-only
83
+ parameters to make the order matter. Nothing about adding one later is harder
84
+ than adding one now. With the premise gone, YAGNI, the absence of any external
85
+ consumer and Strata §9's default verdict all point the same way, and the ADR's
86
+ own table had already marked every knob "after 1.0" before the question was
87
+ written on top of it. The document now records a design that is not built, and
88
+ stays `Proposed` because none of it is implemented rather than because
89
+ anything is pending.
90
+ - `docs/STUDIES/2026-09-03-precision-measurements.md`. ADR 0004 was carrying
91
+ its own evidence, which made a 227-line document that read as a lab notebook
92
+ where a decision was wanted. The measurements moved here, the ADR kept the
93
+ decision, the names and the order, and it is 176 lines. Each measurement now
94
+ says what it settled, so a number can be checked without reading the argument
95
+ that used it.
96
+
97
+ ### Changed
98
+
99
+ - **ADR 0004 step 0 is done**, which the ADR names as the only item that must
100
+ land before 1.0: the `reconstruct` docstring's "widening the dtype does not
101
+ help" now says *input* dtype, and that widening the accumulator does help,
102
+ with the measurement. It was the one live site; earlier counts of seven
103
+ included historical and study files that are not edited.
104
+ - **`WeightKind` is declared an open set** in the GUIDE symbol table and in
105
+ CONTRIBUTING's versioning policy. The choice was made and pinned by test long
106
+ ago; what did not exist was the sentence a consumer reads before writing an
107
+ exhaustive `match`.
108
+ - Four one-line corrections found by the 2026-09-04 audit: FOCO §1 counted
109
+ "four things" over a list of five; MAP and CONTRIBUTING said ten invariants
110
+ after I10 made it eleven; AGENTS.md claimed both rescoped ADRs "shrank by a
111
+ third" when ADR 0003 grew; and `tilings`' docstring promised an ordering by
112
+ `(patch_size, stride)` that the function does not produce. The docstring now
113
+ states the measured order (patch size ascending, exact tile first within each)
114
+ rather than the function changing, because nothing depends on the order and
115
+ a change would be behaviour.
116
+ - **ADR 0003 is accepted, after being rescoped to what it actually governs.**
117
+ Its first draft decided two things at once: a rule about how an exactness
118
+ claim is written, and a three-letter vocabulary plus naming policy for six
119
+ transforms that were under consideration. A month later the rule was
120
+ governing the code and the vocabulary had been used nowhere: `R1`, `R2` and
121
+ `R3` appear in no docstring, no contract section and no page a reader opens,
122
+ and none of rotation, flips, padding, DCT, quantization or dithering shipped.
123
+ What is now accepted is the rule, that an exactness claim is declared per
124
+ regime with a condition the caller can evaluate before calling, with the four
125
+ measured regime axes in its body including the accumulator amendment from
126
+ ADR 0004. The vocabulary is deferred until the first transform that needs it.
127
+ The document also states plainly what its title had obscured: inexact
128
+ operations are not restricted, since `resize` and windowed `stitch` are
129
+ exactly what users come for, and what is forbidden is a docstring implying an
130
+ exactness the operation does not have.
131
+
132
+
133
+ ## [0.5.3] - 2026-09-03
134
+
135
+ ### Added
136
+ - `docs/STUDIES/2026-09-03-strata-conformity.md`, a review of this repository
137
+ against Strata v1.2.2. Its §9 bounded the review before it bounded the
138
+ project: name the genre and apply its standard, and take no change as the
139
+ default verdict unless a defect pays for the fix. The genre is a library, so
140
+ the standard is tests, packaging and CI. The finding is conformance, one gap
141
+ and one piece of debris. The gap is the absence of a DOI, which is the only
142
+ item missing from the publishing pattern and cannot be applied to releases
143
+ already out. The debris was a stale `.pytest_cache`, removed, with the
144
+ redirect verified to hold.
145
+
146
+
147
+
148
+ - `STATUS.md`, the snapshot: what is released, what is unreleased, and who each
149
+ open item waits on. Writing it surfaced that `docs/FOCO-1.0.md` still listed
150
+ B1, B2, B3 and B6 as open although all four were closed in 0.5.0, the same
151
+ bookkeeping gap B5 had. Each is now recorded with what closed it, verified
152
+ rather than assumed: the retracted predicate survives only in the phase spec
153
+ and in the blocker's own text, the falsification suite enumerates 126,736
154
+ geometries, the surface is pinned with `inspect.signature`, and `tilings`
155
+ returns 73 specs with one whole-image entry and none labelled `overlap`.
156
+ B4 is the only blocker still open.
157
+ - `AGENTS.md`, the canonical agent-facing guide, and a thin `CLAUDE.md` that
158
+ points at it rather than duplicating it. Its ten invariants are the ones this
159
+ repository actually enforces, and each names the incident it came from: the
160
+ predicate published and retracted, the benchmark that measured the opposite
161
+ of what it claimed because `uint8.sum()` promotes silently, the 269 MiB of
162
+ Rust build output that lived in the tree, and the machine path once committed
163
+ to `pyproject.toml` that created a literal `Z:\caches\pytest` directory on
164
+ Linux. `AGENTS.md` ships in the sdist; `CLAUDE.md` does not, having no reader
165
+ there.
166
+ - Issue forms and a pull-request template. A report about this library is
167
+ irreproducible without the geometry, the dtype, the torch version and
168
+ `accel_available()`, so the forms make those required rather than suggested,
169
+ and every bug form asks whether the behaviour survives `PATCHCRAFT_ACCEL=0`,
170
+ which is the one answer that separates the native kernel from the library.
171
+ "Wrong numeric result" is a form of its own, because a wrong number is a
172
+ first-class defect here. The PR template asks whether a returned value moved
173
+ and offers the four answers the versioning rule distinguishes.
174
+ - `SECURITY.md`. It names the real edges instead of being boilerplate: the
175
+ native kernel writes through raw pointers and trusts the geometry the Python
176
+ side validated, which is the sharpest thing in the library; `Cache` joins its
177
+ `namespace` straight into a path, so a namespace derived from untrusted input
178
+ escapes the root; nothing in the shipped package opens a network connection;
179
+ and image decoding happens in Pillow before this library sees anything. It
180
+ also says what is not a security issue here, because a wrong numeric result
181
+ belongs in a public issue with its measurement.
182
+ - `CODE_OF_CONDUCT.md`, Contributor Covenant 2.1, taken verbatim from the
183
+ source repository rather than retyped.
184
+ - `MAP.md`, one page of wayfinding: an annotated tree of the repository and a
185
+ want-to/go-to table. It follows the shape the sibling projects use, and it
186
+ opens by saying what the repository actually is, which is 20 public names
187
+ over eight modules plus the evidence that they behave as documented. Every
188
+ link and anchor in it is checked, and every path it names exists.
189
+ - `.pre-commit-config.yaml`, running the gates CI runs. `ruff-format` is
190
+ deliberately absent: the project has never used it and adopting it would
191
+ reformat 36 of 65 files in a commit that changed no behaviour. Two things
192
+ surfaced while wiring it up. `docs/FOCO-1.0.md` had no trailing newline, now
193
+ fixed with the file's own CRLF rather than a bare LF, which is a mistake the
194
+ `mixed-line-ending` hook caught on the first attempt. And on a machine with a
195
+ global `core.hooksPath`, `pre-commit install` writes a hook git then ignores,
196
+ so it is inert without saying so; `CONTRIBUTING.md` gives the one command
197
+ that tells you which case you are in.
198
+ - `CITATION.cff`, validated against Citation File Format 1.2.0, so GitHub shows
199
+ the "Cite this repository" button and the metadata has one authoritative
200
+ home. It ships in the sdist. The BibTeX entry in GUIDE section 9 stays,
201
+ because `cffconvert`'s generated form drops the version and emits a
202
+ placeholder key, but the `.cff` is now the source and the release checklist
203
+ moves both. A test pins its version against the newest released section of
204
+ this file, since comparing it to `patchcraft.__version__` cannot work in a
205
+ checkout, where setuptools-scm resolves a development version.
206
+
207
+ ### Changed
208
+ - **`docs/USAGE.md` is executed instead of transcribed, which closes the last
209
+ 1.0 blocker.** The page claimed eighteen public names when there were twenty
210
+ and had spent three releases describing an API that had moved, and the script
211
+ that once generated it was never tracked, so it could not be regenerated.
212
+ Making the page run in place is the durable fix: `tests/test_docs_usage.py`
213
+ collects it as a doctest, so a stale line fails CI rather than misleading a
214
+ reader. Only five outputs were genuinely wrong; the other eight failures were
215
+ formatting, where the output touched the closing fence and doctest read the
216
+ fence as expected text. `ELLIPSIS` and `NORMALIZE_WHITESPACE` are enabled in
217
+ `pyproject.toml` so the page may still elide a temp path or wrap a long repr
218
+ for the reader. The version literal is gone from it: the version comes from
219
+ the git tag, so any literal is wrong the moment it is written.
220
+
221
+ ### Fixed
222
+ - **THEORY §9 justified promoting `bfloat16` with a fact that is false of it,
223
+ and §9 is the arbiter of the contract.** Both half formats were promoted to
224
+ a `float32` accumulator citing `float16`'s finite maximum of 65504. Only
225
+ `float16` overflows there: on the documented case its numerator reaches
226
+ `inf` in 144 of 256 pixels, while `bfloat16` carries `float32`'s exponent
227
+ and peaked at `9.0112e+04` against a finite maximum of `3.390e+38`. What the
228
+ promotion buys `bfloat16` is precision, since it has 8 mantissa bits to
229
+ `float16`'s 11, and on ordinary [0, 1] data it cut the maximum error from
230
+ `9.262e-03` to `1.935e-03`. §9.2 now carries one entry per format with the
231
+ measurement behind each, and the two docstrings and GUIDE section 4 say the
232
+ same. Two tests pin it.
233
+ - Closed blocker B5 in `docs/FOCO-1.0.md`. The contradiction it named, §9.2
234
+ accepting the half-precision promotion in one list and excluding it as out
235
+ of scope eight lines below, had already been removed in 0.3.0 and never
236
+ recorded. Checking that is what surfaced the `bfloat16` error above.
237
+
9
238
  ## [0.5.2] - 2026-09-03
10
239
 
11
240
  ### Fixed
@@ -666,7 +895,9 @@ First public release. Public API stable; signatures will only change in 1.x.
666
895
  - [`README.md`](README.md) covers installation, the car-vs-track metaphor,
667
896
  validation lab.
668
897
 
669
- [Unreleased]: https://github.com/LeoPR/PatchCraft/compare/v0.5.2...HEAD
898
+ [Unreleased]: https://github.com/LeoPR/PatchCraft/compare/v0.5.4...HEAD
899
+ [0.5.4]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.5.4
900
+ [0.5.3]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.5.3
670
901
  [0.5.2]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.5.2
671
902
  [0.5.1]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.5.1
672
903
  [0.5.0]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.5.0
@@ -0,0 +1,51 @@
1
+ cff-version: 1.2.0
2
+ title: "PatchCraft: image patch extraction, reconstruction, pairing and seam-aware stitching"
3
+ message: "If you use this software, please cite it using these metadata."
4
+ type: software
5
+ authors:
6
+ - given-names: Leonardo
7
+ family-names: Marques de Souza
8
+ email: leonardo.marques.souza@gmail.com
9
+ version: "0.5.4"
10
+ date-released: "2026-09-04"
11
+ repository-code: "https://github.com/LeoPR/PatchCraft"
12
+ license: MIT
13
+ abstract: >
14
+ PatchCraft cuts one image into patches and puts it back. One call takes one
15
+ (C, H, W) float tensor and returns one tensor: there is no batch axis, no
16
+ dataset, no dataloader and no model, and that boundary is documented as
17
+ binding rather than provisional. It owns the unfold and fold arithmetic, the
18
+ geometry validation and the seam blending, so a pipeline can own the rest.
19
+
20
+ Two silent defects motivate it. The intuitive reshape of F.unfold's output
21
+ gives the right shape with the wrong pixels, and a stride that does not cover
22
+ the image leaves pixels at zero without any error: on a 128 by 128 image with
23
+ patch 32 and stride 20, 3840 of 16384 pixels. Both are checked once here,
24
+ with tests around them.
25
+
26
+ The numeric contract is stated as a caller-evaluable predicate rather than a
27
+ promise. The extract and reconstruct round trip is bit-exact if and only if
28
+ every value of the overlap coverage map is a power of two, always true at
29
+ stride equal to patch size, and outside that rule the per-pixel error is
30
+ bounded by (k+1) times eps times the absolute value, with k the pixel's
31
+ coverage count. An earlier and weaker form of that claim was published, then
32
+ measured false and publicly retracted; a falsification suite now enumerates
33
+ the 126,736 legal geometries of the space independently of the predicate and
34
+ tries to break both halves of it.
35
+
36
+ Five of the six published wheels carry a Rust kernel for the overlapping
37
+ fold, compiled into the wheel itself rather than into a separate package, and
38
+ bit-identical to the pure-torch path by test. Every other platform installs a
39
+ universal wheel and runs the same operation in torch.
40
+
41
+ Pre-1.0: output values may change when the middle digit moves, and the
42
+ changelog records each change with the measurement behind it.
43
+ keywords:
44
+ - image-processing
45
+ - patches
46
+ - super-resolution
47
+ - pytorch
48
+ - tiling
49
+ - reconstruction
50
+ - seam-blending
51
+ - numerical-reproducibility
@@ -0,0 +1,83 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
6
+
7
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
+
9
+ ## Our Standards
10
+
11
+ Examples of behavior that contributes to a positive environment for our community include:
12
+
13
+ * Demonstrating empathy and kindness toward other people
14
+ * Being respectful of differing opinions, viewpoints, and experiences
15
+ * Giving and gracefully accepting constructive feedback
16
+ * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
+ * Focusing on what is best not just for us as individuals, but for the overall community
18
+
19
+ Examples of unacceptable behavior include:
20
+
21
+ * The use of sexualized language or imagery, and sexual attention or advances of any kind
22
+ * Trolling, insulting or derogatory comments, and personal or political attacks
23
+ * Public or private harassment
24
+ * Publishing others' private information, such as a physical or email address, without their explicit permission
25
+ * Other conduct which could reasonably be considered inappropriate in a professional setting
26
+
27
+ ## Enforcement Responsibilities
28
+
29
+ Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
30
+
31
+ Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
32
+
33
+ ## Scope
34
+
35
+ This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
36
+
37
+ ## Enforcement
38
+
39
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at leonardo.marques.souza@gmail.com. All complaints will be reviewed and investigated promptly and fairly.
40
+
41
+ All community leaders are obligated to respect the privacy and security of the reporter of any incident.
42
+
43
+ ## Enforcement Guidelines
44
+
45
+ Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
46
+
47
+ ### 1. Correction
48
+
49
+ **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
50
+
51
+ **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
52
+
53
+ ### 2. Warning
54
+
55
+ **Community Impact**: A violation through a single incident or series of actions.
56
+
57
+ **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
58
+
59
+ ### 3. Temporary Ban
60
+
61
+ **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
62
+
63
+ **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
64
+
65
+ ### 4. Permanent Ban
66
+
67
+ **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
68
+
69
+ **Consequence**: A permanent ban from any sort of public interaction within the community.
70
+
71
+ ## Attribution
72
+
73
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
74
+
75
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
76
+
77
+ For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
78
+
79
+ [homepage]: https://www.contributor-covenant.org
80
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
81
+ [Mozilla CoC]: https://github.com/mozilla/diversity
82
+ [FAQ]: https://www.contributor-covenant.org/faq
83
+ [translations]: https://www.contributor-covenant.org/translations