Commits
Schemas with AI, polite ingestion and a new public frontend
Sector schemas proposed by AI, historical reprocessing, anti-plagiarism controls,
The longest night of the phase: closing down external knowledge end-to-end and, along the way, redoing the entire public frontend.
Sector schemas proposed by AI
From the panel I can choose between 3 and 10 documents and ask the AI for validated sector fields: name, type, order, activation, expiration and revision, all visually editable. Saving creates a new version without destroying the previous ones, and subsequent ingestion only applies the active schema and discards invented keys or types. The real E2E proposed 12 fields from three controlled documents and saved the version correctly.
Historical reprocessing and autoposts with support
A new schema version can reprocess up to 20 current documents without touching historical ones: parallel revisions per schema, reused vectors and idempotent execution. Autoposts receive a diversified dossier per domain with facts, dates and provenance, they filter by source and confidence and require a minimum of independent sources: if support is lacking, draft or cancel. Two anti-plagiarism controls: textual match by sentence and cosine similarity by paragraph. With this, the external knowledge phase is closed with full E2E in production and an operational guide.
Three review bugs
The audit of the sprint uncovered three real problems: run() re-executed already-running runs (overlapping cron and manual triggers processed the same article twice; now a running run is returned untouched), the dossier travelled as own_content and the AI received it as its own content (now it's a separate block: "cross-check, don't copy", with a 16k limit), and rate limits accumulated keys forever (unbounded memory; added a limit with purge of the oldest ones). Regression test and 38 tests green.
Polite batch ingestion
To avoid being blocked: a maximum of 5 new documents and 10 downloads per run, a random pause of 1.5–3.5 seconds, respected robots.txt (Disallow and Crawl-delay with a 10s cap), conditional requests with ETag/Last-Modified and immediate stop on 403/429/503. Two scares along the way: a migration with unsealed version columns (resolved with alembic stamp) and an indentation that split the SourceDiscovery class without a syntax error — the tests caught it. 41 tests.
The public website, from scratch
The landing page goes from demo to sales page: dark with lime accent, a live terminal that narrates the pipeline, real panel screenshots and an honest comparison with WordPress. The rest of the public frontend rises to the same level: editorial blog, terminal chat, legal pages and a real 404. Three fights: .rich-text was sizing to its max-width inside flex (capped with min(100%, …)), tables were breaking mobile (overflow-x: auto), and the key one: :global() in a plain CSS was silently disabling every entire rule per invalid selector — weeks of invisible bugs avoided by understanding it's scoped component syntax. The /[lang] vs /[seccion] collision was closed with a single product landing page: languages live in the blog.
At dawn, a list annotation was shadowing the built-in type and the API wouldn't start: from __future__ import annotations and production healthy. In the morning, second public iteration: vertical rhythm, custom diagrams, comparison without invented figures and chat in terminal format. Production verified at 1440/768/390, no overflow or JS errors.