This repository is in private pre-alpha development by EasyByte. External contributions open when the repo does.
- English everywhere: code, comments, commits, issues.
- Conventional commits (
feat:,fix:,docs:,chore:…). - Definition of done:
ruff check . && mypy . && pytestgreen. No exceptions. uv.lockis the source of truth and CI installs from it:uv sync --frozen --extra dev, exactly what the workflow runs, so the gates you see are the gates it sees. A dependency change is a reviewed commit to the lock rather than whatever PyPI served that morning..python-versionpins the interpreter for the same reason — the lock alone resolved 3.13 while CI said 3.12, and those are not the same environment.pip install -e ".[dev]"still works and is fine for a quick look. It resolves fresh, so if it ever disagrees with CI, the lock is right and your resolution is the difference.- Run
pyteston a host that has Docker, and read the skip lines. The sandbox tests are markedneeds_dockerand skip silently without a daemon, so a green run on a machine without one has not exercised the part of this product that builds containers. CI says so in its own log now, because two of those tests were broken for an unknown length of time and the person who found out was a stranger evaluating the product rather than anybody here. - They touch host-global Docker state. A test that leaves a volume or a network behind poisons
the next run in any checkout on that machine — that is how the two above hid each other. If you
add one, clean up in a
finally, and label what you create so the reaper can tell it from a live attempt's debris. - Every inbound surface (webhook, API endpoint) ships with its threat model noted in the PR description.
- Specs before code, per the project's constitution.
A capability is documented from the release that carries it, and not before. The published
documents are checked against docs/published-surface.json — every
hullwork … command and --flag they show has to be one the pinned image accepts.
The rule exists because the same defect happened three times, and every time a person running the artefact found it rather than a check:
0.1.0a1shipped without its optional extras, soHULLWORK_ERROR_DSNmade the container exit 3 and a Postgres URL died in a traceback — both documented in three places each;0.1.0a2predated the module that reports upstream, so it could not do what its own docs said;projects add --credential-filewas documented while every document pinned0.1.0a5, which answersunrecognized arguments.
Each sentence was true of main and false of the thing a reader pulls, and a check against the local
parser would have passed all three, because in all three the local parser was right.
So what you do depends on which you are changing:
| you are | then |
|---|---|
| adding a flag or command | ship it. Do not document it yet — the gate fails, and it is right |
| documenting one already released | nothing extra; the surface already has it |
| cutting a release | after it publishes, ./scripts/record-the-published-surface.py, move the pins, and the documentation you were holding can land in that commit |
| needing the artefact to measure something | that is what edge is for, and it is not a release (docs/releasing.md) |
docs/published-surface.json is generated by asking the image, never by reading this checkout —
a locally generated one would agree with the docs in exactly the cases that matter. Hand-editing it
is the one way to make this whole check lie, which is why the file says so in its first field.
We use the Developer Certificate of Origin 1.1 — the full text is
in DCO. Sign off every commit:
git commit -sThat appends a Signed-off-by: Your Name <your@email> line, which is your statement that you wrote the
contribution or otherwise have the right to submit it. CI checks the trailer on pull requests.
The pull request template carries the other half: a licence grant that lets us ship your contribution as part of Hullwork. You keep ownership of your work. Together, the sign-off and that grant are the whole agreement.
There is no form to sign, no bot, and no service collecting your signature. We looked hard at the alternative and decided against it, so here is the reasoning rather than just the rule:
- The authors of the licence this project ships under (Sentry, who wrote the FSL) do not use a CLA either.
- A CLA is not actually enforceable on Forgejo — the usual signature service is GitHub-only and declined support for other forges years ago. Adopting one would mean either building that infrastructure or quietly making the GitHub mirror the real home of contributions.
- The ecosystem this project lives in — Forgejo, Gitea, GitLab's open code — runs on DCO. Asking for a
signed agreement where you expect
git commit -sis friction we would rather not add.
The full decision record, including what this trade-off costs us, is DR-0001 — kept in the project's own repository rather than published, and available on request.
One honest wrinkle: the DCO text says "open source license", and the FSL is source-available rather than OSI-approved. The licence "indicated in the file" for this project is the FSL — that is what you are certifying you have the right to submit under, and it is why the grant in the pull request template exists alongside the sign-off.
Allowed, and we do it ourselves — this project is built partly by its own worker. Two rules:
- Credit the tool with a
Co-Authored-By:trailer. - The sign-off is yours, not the agent's. A
Signed-off-byline means a human read the change and takes responsibility for it. An agent cannot certify provenance; you can.