Skip to content

Known gaps

The owner, and any developer or operator taking the project over. Read this before promising anything to a client and before trusting a note inside the repositories.

  • Before the next event: there is no working fallback AI provider; the server is shared with another project; backups stay on the same server; parallel load was never measured.
  • Legal: storing personal data of Uzbekistan citizens on a server abroad is an unanswered legal question.
  • Product: the bot and the kiosk are Russian only; the Uzbek landing text has not been proofread by a native speaker; station branding is hard-coded for one event.
  • Landing: mobile LCP is about 3.5 s, limited by first-screen JavaScript.
  • Notes vs code: the in-repo notes are partly stale. This site follows the code; the differences are listed below so nobody “fixes” the code to match an old note.

Severity: High — can stop an event or create legal exposure. Medium — degrades quality, cost or maintainability. Low — cosmetic or stale documentation.

Priority Gap Owner
1 R1 No working fallback AI provider Owner (account), engineer (test)
2 R2 Shared droplet Owner (cost), engineer (move)
3 R3 Backups not offsite, R5 no external uptime check Engineer
4 R4 Parallel load never measured Engineer
5 L1 Legal answer on data location Owner, lawyer
6 P1 Bot and kiosk Russian only — if the audience is not Russian-speaking Owner, engineer

Planned order: Roadmap. Choices still open: Open decisions.

# Gap Severity Detail Where to read
R1 No working fallback AI provider High The pipeline supports a second provider, but no key is configured and the second provider’s image and vision branches have never run against the real API. A provider outage is a product outage. ADR-001
R2 Shared droplet High The server also hosts an unrelated project with its own database. It competes for memory and CPU, and a broken web-server file of the neighbour plus a restart takes HTTPS down for both. Runbook
R3 Backups are not offsite High Daily database copies are kept on the same server. Losing the droplet loses the database and its backups, unless a manual snapshot exists. Backups & data lifecycle
R4 Parallel load never measured on the server High The load test completed 7 packs in a stream and stopped; the burst of 3 never ran. Memory with two or more packs in flight is unknown, and the repository’s notes disagree on whether an earlier three-pack measurement was parallel or sequential. Capacity
R5 No external uptime monitoring Medium Alerts are sent by the bot process itself. If the server or the bot is down, nothing alerts. Monitoring & alerts
R6 Telegram limits for pack creation unknown Medium No measurement above one pack at a time; pack creation has no flood-limit handling of its own. Capacity
R7 Delivery depends on the wording of a public Telegram page Medium “Pack exists” is read from the page title of t.me/addstickers/<name>. If Telegram changes it, every delivery stalls and fails at 15 minutes. ADR-010
R8 File clean-up runs only while the bot service runs Medium A stopped bot stops the 72-hour deletion. After an event the service must stay up, or files must be removed by hand. ADR-005
R9 No money ceiling per event or per day Medium There is a cap per job and a daily quota counter, but nothing stops total spend except the provider balance. Cost
R10 Rate limiters and login lockouts are in memory Low A restart of the API resets them. Security
R11 The 15-minute delivery deadline is a constant Medium It covers queue time too, so a burst larger than about 14–28 jobs fails its tail. Capacity
R13 A wrong leads secret turns into “too many requests” Medium After five rejected calls from the landing server within ten minutes the endpoint answers 429, which the landing treats as throttling with no fallback. With mismatched secrets visitors see “too many requests” and the request reaches nobody. Rotate the secret in the right order — see Secrets.
R14 A link that could not be sent is not re-sent Low If all three send attempts fail, the job still ends as done; the link is in the admin panel.
R15 A failed reminder is not retried Low Deliberate: the reminder is marked as sent even when sending fails.
R16 The kiosk API has no authentication or rate limit Medium Anyone can create sessions (the rows are never pruned), and anyone with a Telegram account can agree in the bot and upload an arbitrary image through the API. The damage is bounded by one pack per account and the cost cap per job.
R17 Loopback trust on the shared server Low The API trusts forwarded headers from the local machine, so a local process of the neighbouring project could call it directly and spoof client addresses for the lockouts.
R18 The instance lock is per machine Medium It stops a second bot process on the same server, not a copy started on a laptop with the same token. That case shows up as a Telegram conflict error and jobs split between the two.
R19 The deploy scripts carry the pilot’s domain and server as defaults Medium The API domain is a literal in the web-server site file, the update script, one legacy check and a deploy test; the push script defaults to the pilot server. On a new installation always pass the server explicitly and change the domain in those files.
R20 An updated backup script does not reach the timer by itself Low The first-time setup installs copies of the backup and update scripts system-wide; routine updates refresh only unit files.
R12 Interactive API documentation is not disabled Low The framework’s automatic /docs, /redoc and /openapi.json are left on in the API application. They reveal endpoint shapes, not data. API reference
# Gap Severity Detail
L1 Data localisation for citizens of Uzbekistan High The national personal-data law requires personal data of citizens processed with information technology to be stored on equipment physically located in the country and in a registered database. The server is with a foreign cloud provider and the cards live in Telegram. This applies to lead contact data and to guest data. The team decided to launch without waiting for a lawyer and recorded the risk. A masked-card switch (LEADS_CARD_PII=0) exists as a partial mitigation. Needs a lawyer’s answer.
L2 Server region unconfirmed Medium The notes say the region “should be checked”.
L3 Contact data is visible to the whole sales group Medium Including other products’ bots that can read group messages. /stickers_new prints contacts even when masked cards are switched on.
L4 Lead data stays in backups for up to 14 copies after erasure Low By design; stated to the person on erasure.
L5 Minors Medium No age check and no stated policy on who consents for a minor.
L6 The consent text says “72 hours” as fixed text Low It does not follow PHOTO_TTL_HOURS. Harmless while the setting is 72 or lower, since the text is then an upper bound.
L7 AI vendor’s handling of submitted photos Medium Governed by the vendor’s terms; not documented in the repositories.
L9 Guest rows have no automatic retention Medium Photos expire, but the rows in users, sessions and jobs (Telegram id, profile name, pack link) stay until an admin resets the guest. Sessions that were never linked to a guest are never deleted. After a reset the Telegram id and old pack links remain in the audit log.
L10 Automatic database copies are never removed Medium The copies made before a migration or a guest reset stay in the data folder; the 72-hour clean-up does not cover them, and they contain whatever the database held.
L11 An appearance summary stays in logs longer than the photo Medium Per-job log lines record skin and hair colour, facial hair, age band, glasses and face shape; logs are kept longer than the 72-hour photo lifetime, and another line ties the job to a Telegram user id.
L12 Anonymisation keeps two free-text fields Low The event date and guest count of a lead survive erasure.
L8 Private notes inside the source repositories High if the repositories are public The repositories’ internal notes, some comments and a few tool docstrings contain a server address, Telegram identifiers, names, client names and commercial terms. This site does not copy them, but it links to those repositories. Clean them or keep the repositories private.
# Gap Severity Detail
P1 Bot and kiosk are Russian only High for non-Russian audiences The landing page sells in three languages. The kiosk has a ?lang= hook with one language; the bot has none.
P2 Station branding is hard-coded for one event Medium The brand layer, logo mark-up and a logo file name tied to a partner are committed code. Re-branding is a code change. See Multi-tenant.
P3 Likeness versus consistency is unresolved Medium Three modes exist; which one should be the default is not decided, and classic and hybrid were validated on few faces. The mode on a server is whatever its .env says. See ADR-008.
P4 Pose 10 often cuts the arm Medium The flat-cut detector catches it and regenerates, but the regenerated figure is often cut too, and then the original is kept. The pose or its prompt needs changing, with a live check.
P5 Very light skin from an overexposed photo Low after the fix Skin shift is capped; outliers in tone are corrected less completely as the price.
P6 Five of eleven catalogue entries are placeholders Low Hidden behind SHOW_PLACEHOLDER_TEMPLATES=0. Composite mode has a real body only for one outfit; the others reuse it.
P7 A style reference image is still a TODO in the worker Low The file is optional and absent.
P8 Pack limit is global, not per event Medium A returning guest at a second event on the same installation is refused.
P9 No guest-facing waiting estimate under load Low The bot always says “a couple of minutes”.
P10 Some settings cannot be set to zero Low DISPLAY_UTC_OFFSET_HOURS=0 and CONSISTENCY_SKIN_DE_MAX=0 silently fall back to the defaults.
P11 One paid tool has no hard money cap Medium tools/consistency_run.py shows an estimate and asks for confirmation, but does not stop at a budget, although the project rule requires a hard stop.
P12 tests/quick_test.py is not under the event guard Low It reads its key from the operating-system environment only, not from .env.
P13 Guest texts live in two places Low Most are in bot/texts.py; five are in the start and consent handlers.
P14 Fallback requests are not in the database Medium By design, but they are invisible to statistics, reminders and export.
# Gap Severity Detail
W1 Uzbek text not proofread by a native speaker Medium The Uzbek page is published and indexed, while the dictionary’s own header calls the proofread a blocker.
W2 Mobile LCP about 3.5–3.6 s Medium Limited by about 172 KB (gzip) of first-screen JavaScript; fonts and HTML are already optimised. The specification recorded a mobile performance score of 87 against a target of 90; the repository’s rules call “90+” accepted. No script enforces a score, LCP or JavaScript size.
W3 The audit script sends a real request Medium Its form check submits a valid request. Run against a server with leads or bot variables set, it reaches the real group.
W4 Demo sticker sources are in no repository Medium The source images of the demo packs are git-ignored on both sides. A fresh clone cannot rebuild the showcase assets or the social preview collage.
W5 Adding a seventh showcase person is not “one folder and one entry” Low Tests and scripts pin exactly six people. Replacing one of the six is simple.
W6 Form rules exist twice Low Landing schema and server validator must change together. The server accepts any UUID version; the landing sends version 4.
W7 Stale comments and notes Low See the table below.
# Gap Severity Detail
N1 The second product’s landing still posts straight to Telegram Medium No storage, statuses or statistics, and its message does not name the product, so it cannot be told apart in a shared group. Its form fields do not match the leads server’s required fields. See the playbook.
N2 The leads module is not reusable as is Medium The prefix and product name are literals in about two dozen places.

Where the repositories’ notes disagree with the code

Section titled “Where the repositories’ notes disagree with the code”

This site follows the code. Do not change code to match these notes; fix the notes.

Note says Code does
CLAUDE.md, “Status”: no bot, no server, no live run yet All three exist; the section is historical.
CLAUDE.md: the database has three tables Nine tables, including cost accounting, audit log, settings and three leads tables. The same stale sentence is in the schema module’s docstring.
CLAUDE.md: the API logs to the console only The API also writes logs/api.log.
CLAUDE.md, rule 2: photo lifetime “48–72 hours” Any value from 1 to 72 hours is accepted; the default is 72.
CLAUDE.md, rule 6: the AI draws only heads, code composes them on a body True for composite mode only. In figure mode — the configured production mode — the AI draws whole figures and code cuts and finishes them.
CLAUDE.md: the admin handler is /stats only There are also /admin_stats, /pause and /resume.
CLAUDE.md: body templates are placeholders Six outfits are ready; five entries are placeholders.
deploy/README.md: production runs four worker slots The code default, the example environment file, the hand-over notes and the runbook say two. A comment in the configuration module also still says four.
deploy/README.md and the example environment file: sheet aspect 3:2 The code default is 2:3; with the default 3x4 layout sheets are square and the setting is unused.
Deploy notes, the update script’s messages and the event runbook: a job interrupted by a restart starts again with the attempt counted A graceful stop returns the job to the queue without spending an attempt, for the first two interruptions; only a hard kill costs one.
Hand-over notes: the log marker for waiting on an image slot The code emits separate markers for image slots and cutting slots, under different names.
A docstring: “out of money” is raised when all providers with a key report it It is raised when any provider reported it and none succeeded.
Deploy notes and hand-over notes: a second bot “with the same token” will not start The lock is a file on the same machine only.
Example environment file: changing the IP salt resets repeat detection Repeats are detected by Telegram username or phone; the salt affects the per-visitor rate limit.
Event runbook: four kinds of alert The code sends eleven distinct alerts — see Monitoring & alerts.
Leads specification and the bot’s reply: erased data leaves backups within 14 days True for the nightly backups only; automatic pre-migration and pre-reset copies and droplet snapshots are not rotated.
Hand-over notes, “Architecture”: two sheets generated one after another The configured default is consistency mode with three parallel sheets.
Hand-over notes: figure mode has never run live Later sections of the same file describe many live figure runs.
Event runbook: production currently runs the classic likeness mode Not verifiable from the repository; the load test describes consistency mode. Check the start-up log line.
Load-test report: an earlier measurement ran three packs at once The hand-over notes describe the same measurement as one pack at a time.
Example environment file: with placeholders hidden the guest sees one outfit The guest sees six.
Example environment file: the event guard also blocks tests/quick_test.py It does not.
Example environment file: the admin list gates the statistics commands It also gates pause and resume.
A catalogue comment names the fallback switch after its log marker The variable is FIGURE_FALLBACK_ENABLED.
A tool docstring says the next pack gets a numeric suffix Packs are named pack<N>_….
A tool docstring says a library is not in the requirements It is pinned there.
A comment in the backup script: the backup holds only guest metadata It copies the whole database, including lead contact data.
The product specification: a pack is created with one call plus one call per further sticker All 12 stickers go in a single creation call.
Leads specification: the server action generates the request id on submit The form generates it when it opens; the action only falls back to generating one.
Leads specification: response list The endpoint also answers 413 for an oversized body.
Leads specification: schema The repeat-requests table and one index were added later and are missing from the specification.
Leads specification: deploy to production awaits approval Whether the module is live cannot be read from the repository. The landing code that calls it is merged.
Note says Code does
“Stack”: an animation library is the only motion dependency No such dependency is installed; all motion is CSS and browser APIs.
The site shares nothing with the product repository It posts requests to the product’s leads endpoint with a shared secret.
The domain is not chosen The owner states the production address is pack.eventtalks.uz; it is configured only in the hosting settings.
The test suite checks both values of the Uzbek flag It also asserts the flag is currently on, so switching it off fails the tests until that assertion is edited.
Two different audit subsets for the Uzbek page and for themes Both options run the same two groups.
Device tilt is at most 5° 5° turn plus 3.5° nod; the audit allows 6°.
The hero cycle is about 26 s (rules) or 28 s (a code comment) The step durations sum to 25.8 s.
Durations come only from tokens Several animations use literal durations.
A comment in the motion module: no springs on the page The stylesheet defines and uses one spring easing. Some exports of that module are unused.
A stylesheet comment: one height animation on the page There are two documented exceptions.
An audit comment: sections use content-visibility They do not; the rules forbid it.
Sticker-gap fixes are listed for one demo pack The script lists a different pack and different stickers.
README: the test command covers four areas It runs six test files.
Earlier amendments: two packs, then three people in the showcase The showcase has six demo people.

Last verified: 2026-11-13 against ai-sticker-pack@b715418 and ai-stickers-landing@e350ff5.