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.
- patchcraft-0.5.4/.pre-commit-config.yaml +45 -0
- patchcraft-0.5.4/AGENTS.md +160 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/CHANGELOG.md +232 -1
- patchcraft-0.5.4/CITATION.cff +51 -0
- patchcraft-0.5.4/CODE_OF_CONDUCT.md +83 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/CONTRIBUTING.md +44 -4
- {patchcraft-0.5.2 → patchcraft-0.5.4}/MANIFEST.in +7 -2
- patchcraft-0.5.4/MAP.md +107 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/PKG-INFO +4 -4
- {patchcraft-0.5.2 → patchcraft-0.5.4}/README.md +4 -3
- {patchcraft-0.5.2 → patchcraft-0.5.4}/README.pt-BR.md +4 -3
- {patchcraft-0.5.2 → patchcraft-0.5.4}/README.pypi.md +3 -3
- patchcraft-0.5.4/SECURITY.md +73 -0
- patchcraft-0.5.4/STATUS.md +97 -0
- patchcraft-0.5.4/docs/ADR/0003-reversibility-classes.md +139 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/ADR/0004-precision-and-effort-parameters.md +61 -78
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/AUXILIARY.md +4 -2
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/FOCO-1.0.md +15 -3
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/GUIDE.md +7 -7
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/SCOPE.md +1 -1
- patchcraft-0.5.4/docs/STUDIES/2026-09-03-precision-measurements.md +98 -0
- patchcraft-0.5.4/docs/STUDIES/2026-09-03-strata-conformity.md +110 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/THEORY.md +5 -3
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/USAGE.md +37 -26
- {patchcraft-0.5.2 → patchcraft-0.5.4}/pyproject.toml +4 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/_version.py +3 -3
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/cache.py +5 -1
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/geometry.py +3 -1
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/reconstruct.py +14 -5
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/resize.py +33 -3
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/stitch.py +4 -3
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft.egg-info/PKG-INFO +4 -4
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft.egg-info/SOURCES.txt +11 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft.egg-info/scm_file_list.json +18 -0
- patchcraft-0.5.4/src/patchcraft.egg-info/scm_version.json +8 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_cache.py +29 -0
- patchcraft-0.5.4/tests/test_citation.py +51 -0
- patchcraft-0.5.4/tests/test_docs_usage.py +34 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_reconstruct.py +36 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_resize.py +73 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tools/check_dist.py +4 -2
- patchcraft-0.5.2/docs/ADR/0003-reversibility-classes.md +0 -108
- patchcraft-0.5.2/src/patchcraft.egg-info/scm_version.json +0 -8
- {patchcraft-0.5.2 → patchcraft-0.5.4}/.gitignore +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/.python-version +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/LICENSE +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/accel/Cargo.lock +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/accel/Cargo.toml +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/accel/README.md +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/accel/src/kernel.rs +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/accel/src/lib.rs +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/ADR/0001-patch-extraction-api.md +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/ADR/0002-patchify-transform.md +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/PERFORMANCE.md +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/ROADMAP.md +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/STUDIES/2026-08-04-patch-techniques.md +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/design/2026-09-01-fase1-bugs-performance.md +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/design/2026-09-01-fase2-acelerador.md +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/docs/design/2026-09-01-fase3-predicado.md +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/setup.cfg +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/setup.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/__init__.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/_accel.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/_foldgeom.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/extract.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/metrics.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/pair.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft/py.typed +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft.egg-info/dependency_links.txt +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft.egg-info/requires.txt +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/src/patchcraft.egg-info/top_level.txt +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/__init__.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/_datasets.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/_rng.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/conftest.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_accel.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_datasets_helper.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_exactness.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_extract.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_geometry.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_import.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_metrics.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_pair.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_public_api.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_reference.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_rng.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tests/test_stitch.py +0 -0
- {patchcraft-0.5.2 → patchcraft-0.5.4}/tools/benchmark.py +0 -0
- {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.
|
|
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
|