Efficiency and reliability audit of 2026-10-30: index #1108

Open
opened 2026-10-31 03:04:01 +00:00 by tiagoagueda · 0 comments
Owner

The efficiency and reliability audit of 2026-10-30: everything in Postulo and its plugins that could use fewer resources (server, CI and build, the developer's machine, the browser), be more reliable, or make the work easier, with no loss of features and none of accessibility. Not a security review.

Three scouts read the core, the CI and image, the front end and the plugins on 2026-10-30 and proposed 104 candidates. Each was then verified against the code, measured where that was cheap, and checked against the tracker. The maintainer settled the 20 decisions that were theirs on 2026-10-31; each issue says what was decided. Every issue carries the performance label.

Filed

Finding Issue What Win
M1 #1109 Maps: matching a country name loads Babel's territory names for all 85 languages into every process large
M3 #1110 ESCO: the whole 6.3 MB classification, in all 28 languages, is parsed and kept in every process medium
M5 #1111 Container: gunicorn loads the app separately in each of three workers and reloads the data caches every 500 requests medium
Q1 #1112 Plugins: each plugin decision costs two queries, so a company page spends 21 of its 69 on policy medium
Q2 #1113 Metrics: every scrape builds a migration graph to say whether migrations are applied small
Q3 #1114 Dashboard: the counters widget runs four COUNTs where one aggregate would do small
Q4 #1115 Every page: two EXISTS queries ask whether the instance has accounts and whether the person has groups small
Q5 #1116 Search: each group counts its matches and then fetches them, two full scans per group with SQLite's Python LIKE small
B1 #1117 Worker: an idle worker writes two log lines and a heartbeat file every two seconds, and its log pushes real errors out of the kept log in about a day large
B2 #1118 Scheduler: the upload scrub rereads and rehashes 25 files every five minutes however recently they were checked medium
B3 #1119 Scheduler: the orphan sweep walks the whole media tree every five minutes for files that must be a day old to qualify small
B4 #1120 Scheduler: the gone-quiet check runs two queries per account every five minutes and reloads applications it already announced small
B5 #1121 Scheduler: finished background task rows are kept for ever small
B6 #1122 Errands: with the worker down, an errand page says the work carries on and polls every two seconds for up to a day small
D3 #1123 Backups: an archive is written under its final name, so one cut short looks like a backup and costs a good one at retention medium
D4 #1124 Backups: the archive is gzipped at level 9, which costs about 45% more CPU on the database than level 6 for 4% less size small
D5 #1125 Data export: the archive is built in /tmp and then copied, and each kept file is read whole into memory on the way medium
D6 #1126 Docker: the compose files set no log size limit, and the healthcheck adds an access line every 30 seconds medium
D7 #1127 Map data: the downloaded GeoNames text tables are kept beside the lookup databases built from them small
D8 #1128 SQLite: no journal size limit, no PRAGMA optimize and no way to compact the file small
R1 #1129 SQLite: read-only views that serve a picture, a script or a poll take the write lock for the whole request medium
R2 #1130 Leases: on PostgreSQL two processes can both take an expired scheduler, backup or sync lease small
R3 #1131 Backups: a backup killed mid-way blocks scheduled backups for 6 hours and Back up now for 24 small
R4 #1132 Database: every request and every worker poll opens a new database connection medium
P1 #1133 Server Overview: every visit reloads fontconfig and probes every offered script small
S2 #1134 Search on SQLite: Postulo's Unicode LIKE spends about 1 microsecond per row and column, twice what an ASCII fast path needs small
C1 #1135 CI: a plain uv run after uv sync --no-group e2e installs the browser group again small
C7 #1136 CI: the weekly schedule runs the whole of CI to get a dependency audit and an ESCO check small
C10 #1137 CI: a release still needs Every Python started by hand on the merged commit small (workflow)
C11 #1138 CI: install and download steps have no time limit and curl does not retry small (reliability)
I3 #1139 Dev image: Debian's apt layer runs again on every push to main, not once a day medium
I4 #1140 Image: a README change re-installs every dependency, and each re-install downloads every wheel again small-medium
I5 #1141 Image: .dockerignore patterns match only at the root, so local builds send and ship the downloaded datasets and every pycache small-medium
I6 #1142 Image: the stylesheet stage copies all of src, so a translation-only commit rebuilds the CSS small
I8 #1143 Image: no workflow starts the image before publishing it, and the start check depends on GeoNames answering medium (reliability)
T1 #1144 Tests: every local run and every plugin suite migrates Postulo's 231 migrations before its first test medium
W3 #1145 Catalogue tool: compile parses every catalogue twice and builds each .mo by repeated byte concatenation small-medium
W4 #1146 Contributing: a branch per issue is asked for, but nothing says how to work on a second one beside the first small
W6 #1147 Wiki: nothing runs wiki_site.py stale --strict or builds the translated pages, so a moved English source is noticed only by hand small
PC8 #1148 Extension CI: postulo-chromium builds twice per run, and postulo-firefox keeps its release package only as an expiring artifact small
A1 #1149 Front end: app.js ships its 137 kB of comments to every browser; serve a built copy and keep the commented source medium
A4 #1150 Password meter: zxcvbn's 870 kB of dictionaries load with the page, before anybody touches the password field large on the pages that carry it
A5 #1151 Board: every card inlines its own copy of eleven status icons; reference them from one sprite medium
A6 #1152 Deployment: pages and fragments leave the container uncompressed, and the documentation does not say the proxy should compress them medium
A7 #1153 Map: with no tile server, the world's outline is inlined into every map page instead of being a cached static file small
A8 #1154 Errands: the state fragment is polled every two seconds for as long as the errand is unfinished, up to a day if the worker died medium
A10 #1155 Browser notices: every open tab collects once a minute; let one tab collect for all of them small-medium
A11 #1156 Unsaved work: a form sent with htmx stops being guarded the moment it is sent, so a failed save can lose what was typed reliability
A12 #1157 Save as you go: a field that failed to save is not sent again when the connection returns, and a server error is reported as no connection small
A15 #1158 Pictures: avatar and logo tiles load eagerly with no size, and their stamped addresses are cached for only a day small
X1 #1159 CI: the monthly dependency run's propose job has a job-level if, which this runner turns into a failure small (reliability)
X2 #1160 Tests: the browser suite's conftest turns Django's async-safety guard off for every unit test small (reliability)
X3 #1161 Repository: the GeoNames lookup databases are not ignored, so one git add -A commits 30 MB small
PR3 #1162 Sync connections: a run that fails is tried again every interval for ever, and a sync cannot retire its connection medium (reliability)
PR8 #1163 Notifications: a send that fails at a notifier is lost, while a webhook delivery is retried for ten hours medium (reliability)
PR13 #1164 Calendar entries from a plugin: add_reminder rewrites an unchanged entry on every sync, so the dashboard's figures are worked out again each hour small
PR2 #1165 Plugin surface: a sync cannot keep any state between runs, so postulo-imap reads unrecognised mail again on every pass medium
C3+C4 #1166 CI: the Security job installs the whole project and every npm package only to audit two lock files small; small
D1+D2 #1167 Caches: expired entries of the database cache and the rate-limit file cache are never removed until 200,000 pile up, and every write counts or lists them all medium; medium
Q6+Q7 #1168 Documents: index the file columns and the check date that file deletes and the scheduled scrub look up small; small
W1+W2 #1169 Catalogue tool: extract --check finds the catalogue sets 29 times, and extract rewrites all 2,352 catalogues even when none changed medium; small
PC7+PC1+PC6 #1170 Plugin CI: one shared definition for the seven plugin workflows, with uv's cache and no second run on a tag medium (maintenance and reliability); large per run; small
PR5 postulo-dav#45 Sync: each changed or unclaimed card and event is fetched with a GET of its own, and in read-only mode every foreign event is fetched on every run medium
PR6 postulo-dav#46 Writing a card or an event: a server that answers a PUT without an ETag makes the plugin list the whole collection again medium-large on the servers concerned
PR7 postulo-dav#47 Sync: every contact costs two queries per run for its primary number and link, and every old interview one more small
PR9 postulo-paperless#27 Store: each document repeats three or four name lookups, a slow consumption uploads the file again, and the scheduler waits up to 20 s per document medium
PR10 postulo-chromium#53 Toolbar badge: a delivery of N captures recounts every capture N times, and an unreachable Postulo is asked twice every five minutes small
PR11 postulo-chromium#54 Captures in this browser: sent captures keep their whole reading for ever, and each log entry rewrites the whole log small (reliability)
PR2-imap postulo-imap#34 Remember the folder's UIDVALIDITY and the last UID read, once the core keeps a sync's state the other half of PR2
PR7-core #1171 Phone numbers and web links: primary_for ignores rows already prefetched, so a sync pays two queries per contact the other half of PR7
PR9-core #1172 Stores: sending document copies has no time budget, so a slow store holds the scheduler pass before webhooks and syncs the other half of PR9
PR13-ft postulo-francetravail#21 Count an entry as pushed only when it was created or changed the other half of PR13

Already tracked

  • C8: #1033 (item 4; PR #1083 adds a partial clone)
  • C9: #1033 (item 4; PR #1083 caches the .mo files)
  • I1: #1033 (item 5)
  • I2: #1033 (item 4)
  • I7: #1033 (item 5)
  • M2: #1086 (measurements added as a comment)
  • T2: #1033 (item 2; measurement added as a comment)
  • PR1: postulo-imap#20 (closed, fixed)
  • PR4: postulo-dav#13 (residual added as a comment)
  • A9: merged into R1

Refuted

  • A2: Minifying the stylesheet saves 1.9 kB gzip and is 387 bytes larger under Brotli (#159 decided the same).
  • A3: brotli is already installed through WeasyPrint, so WhiteNoise already writes .br files.
  • A14: svgo would save 0.7% of the flags' gzip size; they are lazy-loaded and cached for good.
  • C2: The PostgreSQL job's uv download takes about 6.5 s, no slower than a cache restore.
  • C5: npm ci takes about 4 s; a cache or an axe-only install saves nothing worth having.
  • C6: The job image already ships WeasyPrint's libraries; the apt step installs one 1.9 MB package.
  • C12: pytest -n 2 without loadgroup ran green until #1085; the only out-of-memory kill was with three workers.
  • I9: The paired build overlays are deliberate (#403) and held by tests.
  • M4: No theme or template uses theme assets today.
  • P2: A shared WeasyPrint font configuration cannot forget a font, so fonts would leak between renders (#343).
  • T3: A plugin's catalogue checks take 1.5 s and catch stale catalogues locally.
  • PC2: On the server's main uv is pinned by version and sha256 in every plugin.
  • PC3: postulo-apprise's weekly run is green; a job-level if without needs: works on this runner.
  • PC4: postulo-mcp runs in node:22-bookworm with --locked on the server's main.
  • PC5: Every plugin reads messages_tool.selected() from the pinned core (#347).
  • PR12: The MCP library runs synchronous tools on a worker thread.

Recorded, not filed

  • A13: Help drawers add about 4.8 kB gzip to Your details; too small to file.
  • A16: No back/forward cache on signed-in pages: deliberate (#787, #226).
  • P3: A capture rendering starts its own process: deliberate isolation.
  • W5: The screenshot check stays a release step, as #353 decided.

Already good, so the next audit need not look again

  • Core: GeoNames lookups on an on-disk index; ESCO skills bounded to four languages; phonenumbers imported lazily; the cached template loader; WhiteNoise's manifest with Brotli; site settings and plugin decisions remembered per request; token last-use written at most every five minutes; board counts in SQL; company detail prefetches; the export in a fixed number of queries (#557); insights cached by fingerprint (#231, #396); draft PDFs cached; one Chromium session per run of documents; CV exports de-duplicated; 29 slow views outside the request transaction; claims by conditional UPDATE; a time budget on syncs; heartbeats and the GeoNames build written atomically; backup uploads through .part; file deletes after commit; a sweep that refuses to delete most of the store; retention that keeps what the offsite copy lacks; SQLite in WAL with IMMEDIATE and a 20 s timeout; a bounded rotating log; everything else pruned; no naive datetimes.
  • CI: caches that hit (ESCO by revision, uv exactly on pull requests); superseded runs cancelled; step-level timeouts on tests; --durations; sign-in by cookie; the long walks first; the step marker; uv, zizmor and pip-audit pinned; the multi-stage Dockerfile; the dev image waits for CI.
  • Front end: vendor libraries loaded only by the pages that use them; the system font stack; Tailwind purged against the templates; a service worker that caches only the offline page; the dashboard in one request; delegated handlers; passive scroll listeners; icon sources cached; errand polling stops and has a no-script link.
  • Extensions and plugins: no content scripts in the extension, slimmed pages, Web Locks and idempotency keys, Retry-After honoured; postulo-mcp one call per tool; postulo-imap fetches in batches and marks mail with a keyword; postulo-dav writes only on a matching ETag; postulo-apprise caps its deadlines; postulo-francetravail caches its tables.

Checked against main at daee30b15 (2026-10-30).

The efficiency and reliability audit of 2026-10-30: everything in Postulo and its plugins that could use fewer resources (server, CI and build, the developer's machine, the browser), be more reliable, or make the work easier, **with no loss of features and none of accessibility**. Not a security review. Three scouts read the core, the CI and image, the front end and the plugins on 2026-10-30 and proposed 104 candidates. Each was then verified against the code, measured where that was cheap, and checked against the tracker. The maintainer settled the 20 decisions that were theirs on 2026-10-31; each issue says what was decided. Every issue carries the `performance` label. ## Filed | Finding | Issue | What | Win | | --- | --- | --- | --- | | M1 | #1109 | Maps: matching a country name loads Babel's territory names for all 85 languages into every process | large | | M3 | #1110 | ESCO: the whole 6.3 MB classification, in all 28 languages, is parsed and kept in every process | medium | | M5 | #1111 | Container: gunicorn loads the app separately in each of three workers and reloads the data caches every 500 requests | medium | | Q1 | #1112 | Plugins: each plugin decision costs two queries, so a company page spends 21 of its 69 on policy | medium | | Q2 | #1113 | Metrics: every scrape builds a migration graph to say whether migrations are applied | small | | Q3 | #1114 | Dashboard: the counters widget runs four COUNTs where one aggregate would do | small | | Q4 | #1115 | Every page: two EXISTS queries ask whether the instance has accounts and whether the person has groups | small | | Q5 | #1116 | Search: each group counts its matches and then fetches them, two full scans per group with SQLite's Python LIKE | small | | B1 | #1117 | Worker: an idle worker writes two log lines and a heartbeat file every two seconds, and its log pushes real errors out of the kept log in about a day | large | | B2 | #1118 | Scheduler: the upload scrub rereads and rehashes 25 files every five minutes however recently they were checked | medium | | B3 | #1119 | Scheduler: the orphan sweep walks the whole media tree every five minutes for files that must be a day old to qualify | small | | B4 | #1120 | Scheduler: the gone-quiet check runs two queries per account every five minutes and reloads applications it already announced | small | | B5 | #1121 | Scheduler: finished background task rows are kept for ever | small | | B6 | #1122 | Errands: with the worker down, an errand page says the work carries on and polls every two seconds for up to a day | small | | D3 | #1123 | Backups: an archive is written under its final name, so one cut short looks like a backup and costs a good one at retention | medium | | D4 | #1124 | Backups: the archive is gzipped at level 9, which costs about 45% more CPU on the database than level 6 for 4% less size | small | | D5 | #1125 | Data export: the archive is built in /tmp and then copied, and each kept file is read whole into memory on the way | medium | | D6 | #1126 | Docker: the compose files set no log size limit, and the healthcheck adds an access line every 30 seconds | medium | | D7 | #1127 | Map data: the downloaded GeoNames text tables are kept beside the lookup databases built from them | small | | D8 | #1128 | SQLite: no journal size limit, no `PRAGMA optimize` and no way to compact the file | small | | R1 | #1129 | SQLite: read-only views that serve a picture, a script or a poll take the write lock for the whole request | medium | | R2 | #1130 | Leases: on PostgreSQL two processes can both take an expired scheduler, backup or sync lease | small | | R3 | #1131 | Backups: a backup killed mid-way blocks scheduled backups for 6 hours and *Back up now* for 24 | small | | R4 | #1132 | Database: every request and every worker poll opens a new database connection | medium | | P1 | #1133 | Server Overview: every visit reloads fontconfig and probes every offered script | small | | S2 | #1134 | Search on SQLite: Postulo's Unicode LIKE spends about 1 microsecond per row and column, twice what an ASCII fast path needs | small | | C1 | #1135 | CI: a plain `uv run` after `uv sync --no-group e2e` installs the browser group again | small | | C7 | #1136 | CI: the weekly schedule runs the whole of CI to get a dependency audit and an ESCO check | small | | C10 | #1137 | CI: a release still needs Every Python started by hand on the merged commit | small (workflow) | | C11 | #1138 | CI: install and download steps have no time limit and curl does not retry | small (reliability) | | I3 | #1139 | Dev image: Debian's apt layer runs again on every push to main, not once a day | medium | | I4 | #1140 | Image: a README change re-installs every dependency, and each re-install downloads every wheel again | small-medium | | I5 | #1141 | Image: .dockerignore patterns match only at the root, so local builds send and ship the downloaded datasets and every __pycache__ | small-medium | | I6 | #1142 | Image: the stylesheet stage copies all of src, so a translation-only commit rebuilds the CSS | small | | I8 | #1143 | Image: no workflow starts the image before publishing it, and the start check depends on GeoNames answering | medium (reliability) | | T1 | #1144 | Tests: every local run and every plugin suite migrates Postulo's 231 migrations before its first test | medium | | W3 | #1145 | Catalogue tool: `compile` parses every catalogue twice and builds each .mo by repeated byte concatenation | small-medium | | W4 | #1146 | Contributing: a branch per issue is asked for, but nothing says how to work on a second one beside the first | small | | W6 | #1147 | Wiki: nothing runs `wiki_site.py stale --strict` or builds the translated pages, so a moved English source is noticed only by hand | small | | PC8 | #1148 | Extension CI: postulo-chromium builds twice per run, and postulo-firefox keeps its release package only as an expiring artifact | small | | A1 | #1149 | Front end: app.js ships its 137 kB of comments to every browser; serve a built copy and keep the commented source | medium | | A4 | #1150 | Password meter: zxcvbn's 870 kB of dictionaries load with the page, before anybody touches the password field | large on the pages that carry it | | A5 | #1151 | Board: every card inlines its own copy of eleven status icons; reference them from one sprite | medium | | A6 | #1152 | Deployment: pages and fragments leave the container uncompressed, and the documentation does not say the proxy should compress them | medium | | A7 | #1153 | Map: with no tile server, the world's outline is inlined into every map page instead of being a cached static file | small | | A8 | #1154 | Errands: the state fragment is polled every two seconds for as long as the errand is unfinished, up to a day if the worker died | medium | | A10 | #1155 | Browser notices: every open tab collects once a minute; let one tab collect for all of them | small-medium | | A11 | #1156 | Unsaved work: a form sent with htmx stops being guarded the moment it is sent, so a failed save can lose what was typed | reliability | | A12 | #1157 | Save as you go: a field that failed to save is not sent again when the connection returns, and a server error is reported as no connection | small | | A15 | #1158 | Pictures: avatar and logo tiles load eagerly with no size, and their stamped addresses are cached for only a day | small | | X1 | #1159 | CI: the monthly dependency run's `propose` job has a job-level `if`, which this runner turns into a failure | small (reliability) | | X2 | #1160 | Tests: the browser suite's conftest turns Django's async-safety guard off for every unit test | small (reliability) | | X3 | #1161 | Repository: the GeoNames lookup databases are not ignored, so one `git add -A` commits 30 MB | small | | PR3 | #1162 | Sync connections: a run that fails is tried again every interval for ever, and a sync cannot retire its connection | medium (reliability) | | PR8 | #1163 | Notifications: a send that fails at a notifier is lost, while a webhook delivery is retried for ten hours | medium (reliability) | | PR13 | #1164 | Calendar entries from a plugin: `add_reminder` rewrites an unchanged entry on every sync, so the dashboard's figures are worked out again each hour | small | | PR2 | #1165 | Plugin surface: a sync cannot keep any state between runs, so postulo-imap reads unrecognised mail again on every pass | medium | | C3+C4 | #1166 | CI: the Security job installs the whole project and every npm package only to audit two lock files | small; small | | D1+D2 | #1167 | Caches: expired entries of the database cache and the rate-limit file cache are never removed until 200,000 pile up, and every write counts or lists them all | medium; medium | | Q6+Q7 | #1168 | Documents: index the file columns and the check date that file deletes and the scheduled scrub look up | small; small | | W1+W2 | #1169 | Catalogue tool: `extract --check` finds the catalogue sets 29 times, and `extract` rewrites all 2,352 catalogues even when none changed | medium; small | | PC7+PC1+PC6 | #1170 | Plugin CI: one shared definition for the seven plugin workflows, with uv's cache and no second run on a tag | medium (maintenance and reliability); large per run; small | | PR5 | postulo-dav#45 | Sync: each changed or unclaimed card and event is fetched with a GET of its own, and in read-only mode every foreign event is fetched on every run | medium | | PR6 | postulo-dav#46 | Writing a card or an event: a server that answers a PUT without an ETag makes the plugin list the whole collection again | medium-large on the servers concerned | | PR7 | postulo-dav#47 | Sync: every contact costs two queries per run for its primary number and link, and every old interview one more | small | | PR9 | postulo-paperless#27 | Store: each document repeats three or four name lookups, a slow consumption uploads the file again, and the scheduler waits up to 20 s per document | medium | | PR10 | postulo-chromium#53 | Toolbar badge: a delivery of N captures recounts every capture N times, and an unreachable Postulo is asked twice every five minutes | small | | PR11 | postulo-chromium#54 | Captures in this browser: sent captures keep their whole reading for ever, and each log entry rewrites the whole log | small (reliability) | | PR2-imap | postulo-imap#34 | Remember the folder's UIDVALIDITY and the last UID read, once the core keeps a sync's state | the other half of PR2 | | PR7-core | #1171 | Phone numbers and web links: `primary_for` ignores rows already prefetched, so a sync pays two queries per contact | the other half of PR7 | | PR9-core | #1172 | Stores: sending document copies has no time budget, so a slow store holds the scheduler pass before webhooks and syncs | the other half of PR9 | | PR13-ft | postulo-francetravail#21 | Count an entry as pushed only when it was created or changed | the other half of PR13 | ## Already tracked - **C8:** #1033 (item 4; PR #1083 adds a partial clone) - **C9:** #1033 (item 4; PR #1083 caches the .mo files) - **I1:** #1033 (item 5) - **I2:** #1033 (item 4) - **I7:** #1033 (item 5) - **M2:** #1086 (measurements added as a comment) - **T2:** #1033 (item 2; measurement added as a comment) - **PR1:** postulo-imap#20 (closed, fixed) - **PR4:** postulo-dav#13 (residual added as a comment) - **A9:** merged into R1 ## Refuted - **A2:** Minifying the stylesheet saves 1.9 kB gzip and is 387 bytes larger under Brotli (#159 decided the same). - **A3:** brotli is already installed through WeasyPrint, so WhiteNoise already writes .br files. - **A14:** svgo would save 0.7% of the flags' gzip size; they are lazy-loaded and cached for good. - **C2:** The PostgreSQL job's uv download takes about 6.5 s, no slower than a cache restore. - **C5:** `npm ci` takes about 4 s; a cache or an axe-only install saves nothing worth having. - **C6:** The job image already ships WeasyPrint's libraries; the apt step installs one 1.9 MB package. - **C12:** `pytest -n 2` without loadgroup ran green until #1085; the only out-of-memory kill was with three workers. - **I9:** The paired build overlays are deliberate (#403) and held by tests. - **M4:** No theme or template uses theme assets today. - **P2:** A shared WeasyPrint font configuration cannot forget a font, so fonts would leak between renders (#343). - **T3:** A plugin's catalogue checks take 1.5 s and catch stale catalogues locally. - **PC2:** On the server's `main` uv is pinned by version and sha256 in every plugin. - **PC3:** postulo-apprise's weekly run is green; a job-level `if` without `needs:` works on this runner. - **PC4:** postulo-mcp runs in `node:22-bookworm` with `--locked` on the server's `main`. - **PC5:** Every plugin reads `messages_tool.selected()` from the pinned core (#347). - **PR12:** The MCP library runs synchronous tools on a worker thread. ## Recorded, not filed - **A13:** Help drawers add about 4.8 kB gzip to *Your details*; too small to file. - **A16:** No back/forward cache on signed-in pages: deliberate (#787, #226). - **P3:** A capture rendering starts its own process: deliberate isolation. - **W5:** The screenshot check stays a release step, as #353 decided. ## Already good, so the next audit need not look again - **Core:** GeoNames lookups on an on-disk index; ESCO skills bounded to four languages; phonenumbers imported lazily; the cached template loader; WhiteNoise's manifest with Brotli; site settings and plugin decisions remembered per request; token last-use written at most every five minutes; board counts in SQL; company detail prefetches; the export in a fixed number of queries (#557); insights cached by fingerprint (#231, #396); draft PDFs cached; one Chromium session per run of documents; CV exports de-duplicated; 29 slow views outside the request transaction; claims by conditional UPDATE; a time budget on syncs; heartbeats and the GeoNames build written atomically; backup uploads through `.part`; file deletes after commit; a sweep that refuses to delete most of the store; retention that keeps what the offsite copy lacks; SQLite in WAL with IMMEDIATE and a 20 s timeout; a bounded rotating log; everything else pruned; no naive datetimes. - **CI:** caches that hit (ESCO by revision, uv exactly on pull requests); superseded runs cancelled; step-level timeouts on tests; `--durations`; sign-in by cookie; the long walks first; the `step` marker; uv, zizmor and pip-audit pinned; the multi-stage Dockerfile; the dev image waits for CI. - **Front end:** vendor libraries loaded only by the pages that use them; the system font stack; Tailwind purged against the templates; a service worker that caches only the offline page; the dashboard in one request; delegated handlers; passive scroll listeners; icon sources cached; errand polling stops and has a no-script link. - **Extensions and plugins:** no content scripts in the extension, slimmed pages, Web Locks and idempotency keys, `Retry-After` honoured; postulo-mcp one call per tool; postulo-imap fetches in batches and marks mail with a keyword; postulo-dav writes only on a matching ETag; postulo-apprise caps its deadlines; postulo-francetravail caches its tables. Checked against `main` at `daee30b15` (2026-10-30).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#1108
No description provided.