local/ held six unrelated things under a name that stopped being true once it
became the production runtime: the service, the frontend, the tests, the ops
commands, the container definitions and the dependency lock, 65 files with
nothing to tell them apart.
app/ the service: api/ routers, core/ for identity, database, models,
prices and secret loading, and the worker, bootstrap and schema
tests/ the twelve suites, no longer inside the shipped package
ops/ backup, readiness, dependency audit, security summary
infra/ Dockerfiles, gateway templates, ClamAV and storage configuration,
the requirements and their hash lock
web/ the Site, Kanban and portal pages with their scripts
deploy/Dockerfile.api now copies app/ alone, so the tests stop shipping to
production; the local image still carries them, because the suites run inside
the stack's network.
Five kinds of reference had to follow, and each was found by something different
rather than by reading. Imports of the form "from . import db" survived a rewrite
that only matched "from .db import". Tests kept relative imports of modules that
had left the package. A mock.patch target names its module in a string, where no
import rewriting can see it. The browser test resolves a fixture by path. And the
release gate's markers pointed at local/runtime.py and local/worker.py, which is
the decay its new marker test exists to catch — it caught it.
Verified from docker compose down -v: the stack starts, all six integration
suites, both browser suites and the twenty-nine unit tests pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Site's page sat at the repository root while its scripts lived in
local/static, a split with no reason behind it. They are together in web/ now,
with the page as index.html, which is also what the image serves.
app.py held the adapters, the configuration, the shared query helpers and
nineteen routes; customer.py held fourteen more but could not import from it
without a cycle, so it was wired by passing nine callables into install_routes.
Configuration and shared helpers move to local/runtime.py, the rules for
attaching artwork to an order move to local/artwork.py where a customer
correction and an operator final-file set can share them, and the routes become
seven routers under local/api. app.py is 48 lines that create the application,
apply the middleware and include them. Routers import downwards only.
Three faults came out of the extraction and are worth recording, because each
passed a check that looked sufficient. ast reports a function's line at the def,
so every decorator on the line above fell outside the extracted range: twelve
routes and the security middleware were defined but never registered, and the
files still imported and parsed cleanly. Names the old closure renamed on the
way in, and a Jsonb import, were missing in three modules. A name-resolution
pass over every new module found those; the route count matching the original
exactly, 32, is what confirmed the first.
The release gate's marker for the fake payment adapter pointed at app.py and the
adapter moved to runtime.py, so the gate passed while the condition it guards was
unchanged. That is the same silent decay 2.5 set out to fix. A test now asserts
every marker still matches something in its file, so the next move fails loudly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous commit used git add -A and swept in four binaries that were
deliberately untracked: the week-1 client report as .docx and .pdf, a duplicate
of it under output/documents, and imagem-teste.jpg, an input dropped in to test
with. None of them are the repository's to version. They are untracked here,
left on disk, and covered by .gitignore so the mistake cannot repeat.
Three files had no sensible home. The meeting notes sat at the repository root
under a 78-character name with spaces and accents, the roadmap generator lived
in tmp/ — a directory otherwise ignored as scratch — and its output in output/,
which is otherwise generated evidence. They are now docs/reuniao-2026-09-09-
anotacoes.pdf, tools/generate_dtf_report.py and docs/roadmap-cliente.pdf, with
CONTEXT.md and ROADMAP.md updated to match and a docs/README.md saying what each
document is for.
.gitignore no longer needs four rules to keep one generator out of an ignored
directory; tmp/ is scratch again.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
portal/, kanban/ and agente/ were 2,034 lines implementing the original
Tiny-first model: token upload links, a second SQLite Kanban, a factory agent.
Nothing imported or started any of it, and several endpoints took the acting
user from the request body with no authentication at all. Their real cost was
that a reader arriving at this repository found two Kanbans and two portals and
had to work out which one was real. The root schema.sql and .env.exemplo went
with them: both code paths load local/schema.sql, and having .env.exemplo beside
.env.example differing by one letter was a trap rather than a convenience.
The documents describing that model are archived rather than deleted. They
record decisions and reasoning the current documents do not repeat, so they are
worth keeping as background, with a header saying plainly that they are not
instructions.
README.md keeps its business case — the capacity figures and the cost argument
are still the reason this project exists — but now states where the prototype
documentation begins and that the code it describes is gone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>