Changelog — Odladěné problémy
Položky zde = nesmí se rozbít. Při jakékoliv změně v oblasti ověřit, že regrese nenastala.
2026-05-07
DEPLOY: billit hub+prod 9fa75ce — bundle cc81773..9fa75ce (CR-025 + REQ-043 + REQ-044 specs + api-keys fix)
- Služba: Billit hub (15:55 CEST) + prod (16:09 CEST) — schválení po jednom (
deploy ok billit hub,deploy ok billit prod) - Verze:
cc81773→9fa75ce(master), 5 commitů - Změny:
9fa75cefix(billit-web): UX — full-form subject create dialog + click-to-copy API key8939d9bfix(billit-api): api-keys POST response shape — flatten{key, rawKey}→{...entity, key: rawKey}ecaef2e+a475bd7docs(specs): REQ-044 update — Cortex payload schemaddd117cdocs(specs): REQ-044 draft — evidence přijatých faktur (received_invoices)0416e0dfeat(billit): CR-025 + REQ-043 — orders security harden (BE) + webhook UI spec- Migrace: žádné (deklarováno
db_change=migrationaleNo migrations are pending— billit deklaračně zachovalmigration_from=0c1e2db, prakticky nic nového) - HC:
billit.s60hub.cz/health+billit.cz/health200 OK, kontejnery recreated, sentinel labels OK - Source hash: VERIFIED na obou env (
9d688fe6…na prod) - Anomálie: první spuštění
deploy.shna hub i prod skončilo silent exit v[6/8] Docker deploy(žádný výstup, EXIT=1 přesset -euo pipefail); druhý pokus na obou pak prošel bez problémů. Stejný symptom jako 2026-05-03 první prod pokus. Příčina neznámá (pravděpodobně transient SSH/docker race), neblokující ale opakující se. TODO sentinelu: přidat retry logic nebo logování do[6/8]step.
2026-05-03
DEPLOY: billit hub+prod cc81773 — bundle 0c1e2db..cc81773 (CR-022 + CR-023 + REQ-042 banking)
T-20260502-042 → billit
- Služba: Billit hub (2026-05-02 22:50 CEST) + prod (2026-05-03 06:38 CEST) — schválení po jednom (nejdřív
DEPLOY OK billit hub, pakDEPLOY OK billit prod) - Verze:
1aa8dee→cc81773(master) - Změny: CR-022 + CR-023 + REQ-042 (Fio sync metadata), banking-filter feature, REQ-042/CR-024 specs
- Migrace:
REQ042AddFioSyncMeta1772690000000(typeorm) —ALTER TABLE bank_accounts— proběhla úspěšně na hub i prod - HC:
billit.s60hub.cz/health+billit.cz/health200 OK, kontejnery healthy s labelemcc81773 - Source hash: VERIFIED na obou env po finálním deployi (
659f54a3a58be9d3c476168812a1bd1ana prod, hub po fixu deploy.sh)
Diagnóza source hash mismatch (původně FAIL):
- Orphan
/opt/billit/s60-auth-sdk/na obou env — pozůstatek z doby před přechodem billit-web na vendored tarball (./vendor/s60-auth-sdk-0.1.0.tgzvpackage.json). Rsync--deletenemohl smazat (uvnitř.git/sample hooks readonly). - hub-alfa: jen
.git/(sample hooks), žádný build artifact - prod-alfa:
.git/+ reálnédist/*.{js,d.ts,json}z 11.3.2026 (24+ souborů) — funkčně dead, kontejner mounts[], SDK je v Docker imagi z buildu, ne mountovaný za běhu -
Fix:
ssh root@<host> rm -rf /opt/billit/s60-auth-sdkna obou env. Před prod akcí proveden safety checkfind ... -type f -not -path '*/.git/*'(našeldist/), evidence ne-použití (mounts[], vendored tgz v package.json) ověřena → smazáno. -
.claude/settings.jsonv hash výpočtu deploy.sh — soubor je commitován v billit repu, ale rsync ho vylučuje (--exclude='.claude'vdeploy.sh:614). Hash výpočet ho ale zahrnoval → trvalý mismatch (lokální verze ≠ stará remote verze). - Fix v sentinel repu: přidán
| grep -v '\.claude'doLOCAL_SRC_HASH/REMOTE_SRC_HASHfind pipe (deploy.sh:901-903), aby exclude byl symetrický s rsync. - Dopad: všechny budoucí deploy budou source hash count s
.claude/ignorovaným. Funkčně pro běh aplikace 0 —.claude/je config Claude Code agenta, nikdy se nedeployuje.
Lekce:
- Před rm -rf na remote <dir> vždy spustit find <dir> -type f -not -path '*/\.git/*' | head jako safety check (orthogonal feedback feedback_remote_rm_safety_check.md).
- Rsync exclude listy musí být symetrické s hash výpočtem v deploy.sh; jakýkoli další exclude přidaný do rsyncu (__pycache__, .vscode, atp.) musí jít i do hash pipe.
CR-141 multi-tenant infra — DO Spaces, PG super_admin role, DNS migrace, DO PG cleanup
T-20260502-021/022/023, T-20260502-024/029/043 → pulse
1) PG super_admin role (3× per env, BYPASSRLS) na pg-alfa (Hetzner self-hosted PG):
- s60_pulse_super_admin_dev + GRANTS na s60_pulse_dev
- s60_pulse_super_admin_hub + GRANTS na s60_pulse_hub
- s60_pulse_super_admin_prod + GRANTS na s60_pulse_prod
- ALTER DEFAULT PRIVILEGES (future tables auto-granted)
- Hesla: /root/secrets/pulse/.env.{hub,prod} jako SUPER_ADMIN_DATABASE_URL, dev creds doručeno přes Tailscale (T-20260502-024)
- Existující users (s60_pulse_<env>_user) měly rolbypassrls=f už od defaultu — žádný ALTER potřeba
2) DO Spaces bucket s60-pulse-tenants + CDN endpoint:
- Bucket vytvořen Liborem v DO control panelu (region fra1, Restrict File Listing ON, Enable CDN ON)
- CDN endpoint id: a754b348-96aa-4bf3-b7b8-b80e3803a802
- Custom doména: cdn.pulselab.cz (Libor explicit rozhodnutí 2026-05-02 — pulse-platform branded, ne shared cdn.studio60.cz)
- SSL: Let's Encrypt cert získán přes Certbot DNS-01 (CF API token), uploaded do DO jako custom cert (id 3d686908-...)
- Scoped access key: RW jen na s60-pulse-tenants (DO801... prefix)
- Creds: /root/secrets/pulse/.env.{hub,prod} jako DO_SPACES_*
- CORS: blokované DO API (AccessDenied i s fullaccess key) — Libor doplní ručně přes control panel pokud potřeba
- Smoke: PUT/GET/DELETE přes scoped key, fetch přes https://cdn.pulselab.cz/<file> 200 OK end-to-end
3) DNS infra (Forge přes Cloudflare):
- super.pulselab.cz A → 178.104.36.211 (DNS only)
- 5pd.cz migrace Wedos → CF (zone 70e52dea40177d2a6b3ed994c820d299), A @/CNAME www → 178.104.36.211
- 5pd.cz NSSET v CZ-NIC: WEDOS → CFNS-WEBSTER-1775625015 (paityn, todd... resp. bruce, yolanda dle account)
- cdn.pulselab.cz CNAME → DO Spaces CDN endpoint (DNS only, kvůli DO custom doména routing)
- Pulselab.cz nemá wildcard, studio60.cz má wildcard * → merlin (legacy, nedotýkat)
4) DO managed Postgres s60-postgres-dev SMAZÁN:
- Cluster nebyl používán žádnou aktivní službou (všechny prod containers ověřeny → pg-alfa Hetzner)
- 30 DBs, ~600 MB raw, 0 active connections per DB
- Backup: /root/projects/sentinel/backups/do-postgres/20260502-145145/ (69 MB compressed pg_dump custom format, 30 DBs + roles inventory)
- API DELETE → HTTP 204, cluster not_found. Úspora ~$15/měs.
5) DO DNS zóny SMAZÁNY:
- s60dev.cz + s60hub.cz v DO DNS byly legacy balast — domény dlouho v Cloudflare (paityn/todd NS aktivní)
- Backup: /root/projects/sentinel/backups/do-dns/*.json
- API DELETE → HTTP 204, zone count 0
Sentinel scope ovedená rule:
Sentinel commitoval ranější (pre-2026-04-27) deploy stamps do pulse repo (deploy(pulse): hub+prod ...). Toto chování bylo retired — sentinel nyní jen pulluje cizí repos, commit jen do s60-sentinel repo (orthogonal feedback_never_change_code.md enforcement).
2026-04-30
DEPLOY: pulse 5ff6982 — hub + prod (CR-095 + CR-129 + CR-132 follow-ups, žádné migrace)
- Služba: Pulse hub + prod
- Verze:
1e1f1b5→5ff6982(T-20260430-010, 4 commity) - Změny (commit-by-commit):
5ff6982CR-132 follow-up — reopen tlačítko nad výstupem (Chat + Profile + Admin) + noCredits guard (toast + redirect/profile)7005ba9CR-129 follow-up — request-context awaregetBaseUrl(). AsyncLocalStorage, McpController obalí handler dorequestContext.run(host, proto). Fix prod bugu kde MCP responses na pulselab.cz vracely.s60dev.czURL (env var fallback). Mappingmcp.pulselab.cz → pulselab.cz,mcp.s60hub.cz → pulse.s60hub.cz332e39cCR-095 bugfix — admin viděl "1/21 metodik" místo "1/12" (canSeeHidden=true admin role pouštěl archived methodologies do client endpoint count). Fix: archived VŽDY filtrované vbuildClientMethodologiesList, hidden categories zůstávají gatované přescanSeeHidden7481f4bNFR-007 — ESLint rule tests + CI gate + PR template (sentinel autor)ec0b24bCR-139 — MCP templates E2E test scriptscripts/cr139-mcp-test.js(sentinel autor)- db_change=false — žádné DB migrace v této dávce, čistý code-only deploy
- Hub:
5ff6982VERIFIED, QA Gate RELEASE tier PASS 15/15 (smoke 2 + regression 13). Audit FAIL false-positive (DEPLOY.mddb_change: migrationz dřívější deklarace, ale 088 už applied → diff odb454919..5ff6982nemá nové migrace; non-blocking). - Prod:
5ff6982VERIFIED. Smoke OK: pulselab.cz/health200/mcp401 + WWW-Authenticate (regrese SSE blok)/api/template/render/methodology-detail-v1/methodology/brand-identity200 (CR-139 templates regrese)
2026-04-29
DEPLOY: pulse 1e1f1b5 — hub + prod (heroes Radka golden/iron/wood + SPEC.md fix)
- Služba: Pulse hub + prod
- Verze:
6e68e51→ hubb454919→ prod1e1f1b5(T-20260429-086, T-20260429-087) - Změny:
- CR-093 follow-up — heroes Radka golden/iron/wood mapping
- Migrace
1710000000088 HeroImagesRadkaGoldenIronWood:hero_levels.imagePngUPDATE (3 rows): Spark→wood-level3, Bolt→iron-level3, Storm→golden-level3, Pulsar=NULLhero_sublevels.imagePngUPDATE (12 rows): Spark→wood, Bolt→iron, Storm→golden, Pulsar=NULL- Idempotentní (UPDATE per name), down migration vrací staré spark/bolt/storm jména
- 12 PNG souborů v gitu (
specs/wireframes/hero/{wood,iron,golden}-level{0,1,2,3}.png, commit4088a51) - Static serving přes
/specs/hero/*(main.ts existsSync gate) src/templates/SPEC.md(= fix TODO T-20260429-075 ze včerejšího CR-139 deploye → spec-compliance test pass, čistý průchod bez--skip-ci)- REQ-005 regression test extended (
test/regression/auth/invite-token-req005-extended.spec.ts, commit1e1f1b5push během prod deploye, jen test soubor) - Hub:
b454919VERIFIED, QA Gate PATCH tier PASS 15/15 (smoke 2 + regression 13) - Prod:
1e1f1b5VERIFIED, smoke OK pulselab.cz/health200- Migrace 088 applied
hero_levelsupdated (Bolt→iron-l3, Spark→wood-l3, Storm→golden-l3, Pulsar=NULL)- Static PNG files reachable: wood-level0.png 3.5MB, iron-level3.png 4.3MB, golden-level2.png 4.6MB (200 OK)
- Bezchybný deploy — žádné migration permission issues, žádný spec-compliance fail, čistý průchod
DEPLOY: pulse 6e68e51 — hub + prod (CR-139 templates system)
- Služba: Pulse hub + prod
- Verze:
e296646→6e68e51(T-20260429-074) - Změny: CR-139 — generic Handlebars region templates + 13 MCP tools + per-tenant CSS overlay
- Commity:
76ebaa9(CR-139 main),6e68e51(CR-139 follow-up: dynamic credit cost + public page refactor + visual check) - Migrace (2):
1710000000086 CR139TemplatesSystem— vytvořila tabulkytemplates/template_content/tenant_theme_css+ seedmethodology-detail-v1(10 regionů per Mišin návrh) +methodology-detail-legacy(markdown fallback) + brand-identity content z Mišina docx1710000000087 CR139MigrateMethodologiesToV1— auto-migrace 11 metodik zcontent_blocksmeta → 10 regionů v1 šablony (LEFT JOIN namethodology_metadata.creditCost— premium 3 / mid 2 / standard 1 dynamicky)- Idempotentní:
CREATE TABLE IF NOT EXISTS,INSERT ... ON CONFLICT DO UPDATE, žádnýgen_random_bytes(pulse aplikoval lekci z dopolední pgcrypto incident),gen_random_uuidjen built-in - CI bypass: Sentinel guard zachytil
tests/spec-compliance/spec-structure.spec.tsfail (6 missing SPEC.md > práh 5; CR-139 přidalsrc/templates/modul bez SPEC.md). Libor schválil--skip-ci. Pulse dostal TODO T-20260429-075 doplnitsrc/templates/SPEC.md. - Hub:
6e68e51VERIFIED, QA Gate RELEASE tier PASS 15/15. Templates (2) + template_content + tenant_theme_css tables verified. - Prod:
6e68e51VERIFIED, žádný incident, migrace prošly při container start.pulselab.cz/health200, templates=2, template_content=23 records, tenant_theme_css table exists.
DEPLOY: pulse e296646 — hub + prod (CR-124 až CR-135 batch + JPG hero upload)
- Služba: Pulse hub + prod
- Verze:
63ae3ab→e296646(kód commita83fa98; T-20260429-046, T-20260429-052, T-20260429-055, T-20260429-056, T-20260429-057, T-20260429-060) - Scope (12 položek): CR-124 (invite link safety + Y3 deterministic welcome), CR-125 (ReviewPanel rewrite), CR-126 (Email PDF download box), CR-127 (Completed dialog fallback), CR-128 (admin mobile responsive), CR-129 (
getBaseUrlhelper + ContentEditor cleanup), CR-130 (methodology_demos kurátorský systém + 6 MCP tools), CR-131 (landing 3. CTA), CR-132 (reopen metodiky + audit log), CR-134 (JSONB pollution hotfix), CR-135 (hero images upload — JPG/SVG/PNG whitelist), NFR-007 (ESLint pulse/no-zod-any-in-boundary) - Migrace (4):
1710000000077-CR125ReviewPanelDisplayType✅,1710000000082-CleanupPollutedMethodologyVersions✅ (cleanup 13 polluted methodology_versions na prod),1710000000083-CR130MethodologyDemos✅,1710000000084-CR132ReopenAndStableUrl(selhala na prod, vizFIX:dál) - Hub:
e296646VERIFIED, smoke + regression PASS 15/15. Audit FAIL (false-positive: porovnáválast_deployed..currentdiff, nové migrace už applied z předchozího4f73c55deploye → audit nevidí "nové migrace" v posledním diffu). - Iterace pulse push (důvod hodinového waste): pulse poslal deploy request, sentinel guard zablokoval (
db_change: nonev DEPLOY.md ale 4 migrace v diffu) → pulse aktualizoval DEPLOY.md, ale lint-staged hook vytvořil dodatečný commit (9fccd7d → 4f73c55) → sentinel guard pak rejected znovu protože pulse popsal změnu jen ve zprávě, ne v repo. Pulse pushl skutečný commit (4f73c55) → deploy proběhl. JPG support fix ve296646(Libor zjistil že hero upload bere jen SVG/PNG) následoval jako další iterace. Lesson: vždy ověřitgit ls-remote origin masterpo push (lint-staged může přidat commit nad ten který agent právě committed). - DB cleanup (CR-134): 13 řádků v
methodology_versionsnas60_pulse_prodvyčištěno — odstraněny numeric keys z JSONB content (slugy: brand-guide v2, brand-identity v3+v4+v5, competitor-analysis v2, content-creation v2, convert-to-sale v2, customer-path v2, feedback v2, idea-to-sale v3, pricing-authority v2, risk-radar v2, selling-web v2). "Remaining polluted: 0".
FIX: prod s60_pulse_prod — CREATE EXTENSION pgcrypto (drift mezi env)
- Problém: Migrace
1710000000084-CR132ReopenAndStableUrlselhala na prod spermission denied to create extension "pgcrypto". Container zasekl v TypeORM init retry loop, prod down ~10 minut. - Root cause: Extension drift mezi env.
s60_pulse_hubas60_pulse_devmělypgcryptoz dřívějška (manuálně nainstalovaná, nezdokumentovaná).s60_pulse_prodji neměla. Migrace 084 obsahovalaCREATE EXTENSION IF NOT EXISTS pgcrypto— na hub/dev no-op, na prod vyžadovalo superuser (kterým app users60_pulse_prod_usernení). - Fix:
su - postgres -c "psql -d s60_pulse_prod -c 'CREATE EXTENSION IF NOT EXISTS pgcrypto'"(po LiborověDB OK pgcrypto) - Verze extensions po fixu: všechny tři env (hub/prod/dev) mají teď
plpgsql + pgcrypto + uuid-ossp— drift odstraněn - Recovery: TypeORM se chytl při dalším retry (~10s), migrace 084 prošla, container start.
Nest application successfully started,pulselab.cz/health200, 78 stale credit holds released. - Runbook:
docs/docs/runbooks/pg-extension-drift.md(preventivní postup pro budoucí migrace sCREATE EXTENSION)
DB COPY: s60_pulse_prod → s60_pulse_dev (diagnostické zrcadlo)
- Žadatel: Libor (pulse agent diagnostika reálného stavu aplikace na devu)
- Postup (lokálně na pg-alfa Hetzner, sentinel přes SSH):
- Backup dev:
/tmp/pulse-dev-backup-20260429-pre-prodcopy.dump(3.3M) - Dump prod:
/tmp/pulse-prod-20260429.dump(5.7M,--no-owner --no-acl) - Terminate 5 idle dev connections
DROP DATABASE s60_pulse_dev+CREATE DATABASE OWNER s60_pulse_dev_userpg_restore --no-owner --no-acl -d s60_pulse_devz prod dumpu- Bug v 1. iteraci: Po
pg_restore --no-ownerměly všechny 30 tabulek + 1 sequence + schema public ownerpostgres(uživatel co spustil restore). Pulse migrace pak selhaly (ALTER TABLEvyžaduje owner, ne jen GRANT). - Fix: DO-block iterace
ALTER TABLE/SEQUENCE/SCHEMA OWNER TO s60_pulse_dev_userpro všechny public objekty. - Verifikace: clients=32, sessions=135 (match prod). Owner po fixu: tables=30/sequences=1/schema=1, vše
s60_pulse_dev_user. - GDPR: dev DB teď obsahuje reálné osobní údaje klientů. Pulse použít pouze pro diagnostiku.
- Lesson: při
pg_dump → pg_restoremezi env vždy doplnit ALTER OWNER krok (GRANT nestačí pro DDL operace).
2026-04-28
DEPLOY: pulse 63ae3ab — hub + prod (PDF page break wider coverage)
- Služba: Pulse hub + prod
- Verze:
3587e72→63ae3ab(release commitb7f1407+ DEPLOY.md update; T-20260428-429) - Změny:
@media printrule rozšířena (CSS-only fix vsrc/output/default-template.ts): - Širší
break-inside: avoidlisty (typo-samples, funnel-diagram, triangle-visual, shade-palette, gradient-library, type-scale-ladder, ui-component-preview, ui-card, personality-radar, archetype-compass, social-templates, social-item, takeaway, intro, ctx, outro-card) widows: 3; orphans: 3na body/p/li (min 3 řádky pohromadě)ul/ol/table { break-inside: auto }(povolit dělení dlouhých seznamů → menší bílé mezery)thead { display: table-header-group }(hlavička tabulky na každé stránce)- Důvod: Libor reportoval rozdělení bloku který by se neměl (brand-identity output
46ebaed5, str. 12-13) - db_change: none, migration: 0 pending
- Ověření hub + prod:
- QA Gate hub: PASS 15/15
- Hub + prod
63ae3abVERIFIED - Smoke (regrese):
/api/content/chat404 ✅,/mcp401 ✅,/health200 ✅
DEPLOY: pulse 3587e72 — hub + prod (CR-122 + CR-123 + PDF page break fix)
- Služba: Pulse hub + prod
- Verze:
686dd6c→3587e72(release marker53c4e1aschválený Liborem; pulse pushnul +1 follow-up commit do masteru — DEPLOY.md update bez funkční změny; T-20260428-415) - Změny:
- CR-122 Content Editor trim —
POST /api/content/chatodstraněn (AI chat z admin UI pryč, MCP přebírá editaci) - CR-123 Edge stavy retry/finish — non-ACTIVE sessions vrátí current state místo
BadRequestException. Řeší incident "Monika Webster" (retry na completed session → 400) - PDF page break fix (commit
53c4e1a) —@page marginsjednocen s Puppeteer/GotenbergpdfOptions.margin(25/15/20/15mm). Text přetečený na další stránky respektuje horní/dolní okraje, nepřekrývá header/footer. - 4× already-merged: nginx
/mcp(CR-116), GlitchTip DSN fix, TRIAL credits hold, CR-116 statusimplemented - db_change: none, migration: 0 pending
- Pulse pre-deploy: 835/835 tests pass, TS clean (backend + admin + client), dev smoke OK
- Ověření hub + prod:
- QA Gate hub: PASS 15/15 (2 smoke + 13 regression: pulse-spec-session 12/12, webhook-validation 3/3)
- Hub
3587e72VERIFIED, prod3587e72VERIFIED POST /api/content/chat→ 404 (CR-122 regrese) ✅POST /mcp→ 401 +WWW-Authenticate(regrese SSE blok beze změny) ✅GET /health→ 200 ✅POST /webhooks/billit→ 400 (signature validation regrese) ✅
DEPLOY: auth 9b9a228 — hub + prod (DCR auto-approve rozšířen na confidential PKCE)
- Služba: S60Auth hub + prod
- Verze:
b28d79c→9b9a228(auth schválil9ee56e2, master pushnul9b9a228=9ee56e2+dcr.service.spec.tsunit test, žádná funkční změna; T-20260428-326) - Změny: DCR auto-approve rozšířen z
token_endpoint_auth_method=nonena celý grantauthorization_code. Pokrývá nyní confidential klienty sclient_secret_post(jak Claude Desktop posílá). - db_change: none
- Ověření:
- Hub QA Gate: PASS,
9b9a228VERIFIED - Prod
9b9a228VERIFIED - Live test prod: DCR s
token_endpoint_auth_method=client_secret_post+ scopemcp:tools→registration_status=approved✅ (předtím by bylopending) - DB cleanup
s60_auth_prod.systems(Libor schválil DB OK): DELETE3 řádky — pending Claude Desktop retry zombies (s60-claude-ffaa2195,s60-claude-d1b5a0b7,s60-claude-802bc023)DELETE3 řádky — sentinel testovací klienti (s60-sentinel-*)- Final SELECT na
claude.ai/api/mcp/auth_callback: 0 rows (čisto pro fresh Claude Desktop registraci) - Sled bugů: B-1 (DCR dedup) → fixnuto v
b28d79c. B-2 (auto-approve jen pronone) → fixnuto v9b9a228. Sentinel scope kompletní.
DEPLOY: auth b28d79c — hub + prod (DCR dedup removed, auto-approve PKCE public)
- Služba: S60Auth hub + prod
- Verze:
336985a→b28d79c(T-20260428-313, T-20260428-317) - Změny (auth strana):
- DCR
POST /api/auth/clients/register— odstraněna deduplication podleredirect_uri. Každý request vytvoří nový unikátníclient_id. - DCR auto-approve pro
token_endpoint_auth_method=none(PKCE public klienti). - db_change: none, migration: ne pending
- Ověření:
- QA Gate hub: PASS (smoke 2/2)
- hub
b28d79cVERIFIED (commit + source hash + sentinel labels) - prod
b28d79cVERIFIED - DCR dedup test: 2 různé
client_idpro stejnéredirect_urina hub i prod ✅ - DB cleanup
s60_auth_prod.systems(na žádost auth, schváleno Liborem): DELETE FROM systems WHERE redirect_uris @> ARRAY['https://claude.ai/api/mcp/auth_callback'] AND oauth_client_id LIKE 's60-test-%';→ 1 řádek (s60-test-probe-30983d21)DELETE FROM systems WHERE redirect_uris @> ARRAY['https://claude.ai/api/mcp/auth_callback'] AND registration_status = 'pending';→ 1 řádek (s60-claude-e411dff7, dnešní Claude Desktop pokus)- Final SELECT: 0 rows
- Známý nedostatek (následný TODO auth, T-20260428-325):
- Auto-approve NEpokrývá
token_endpoint_auth_method=client_secret_post. Claude Desktop posílá confidential client + secret, ne PKCE public → DCR výsledekpending→ token endpoint 403. Sentinel scope nepokrývá kód auth — čeká se na druhý fix od auth agenta.
FIX: pulse.s60hub.cz + pulselab.cz — Nginx /mcp SSE-friendly location (MCP Streamable HTTP)
- Problém: Po opravě OAuth discovery (předchozí položka) login a redirect proběhly OK, ale Claude Desktop hlásil "not connected" — tichý fail bez errorů v logs ani GlitchTip.
- Root cause: Hub i prod Nginx config neměl specifický
location /mcpblok — request padal do genericlocation /s defaultproxy_buffering onaproxy_read_timeout 120s. Streamable HTTP transport (MCP spec 2025-11-25) drží connection a streamuje events; default buffering Nginxu drží initialize response v bufferu, klient ho nikdy nedostane. - Fix: Přidán
location /mcp { ... }blok před genericlocation /na hub-alfa i prod-alfa s: proxy_buffering off+proxy_cache off+chunked_transfer_encoding on(SSE)proxy_read_timeout 3600s+proxy_send_timeout 3600s(long-lived connection)proxy_set_header Connection ""(HTTP/1.1 keepalive)- Ověření:
curl -X POST https://pulse.s60hub.cz/mcp→ 401 +WWW-Authenticate: Bearer realm="mcp", resource_metadata="https://pulse.s60hub.cz/.well-known/oauth-protected-resource"✅curl -X POST https://pulselab.cz/mcp→ 401 + analogická WWW-Authenticate ✅- Regrese:
GET https://pulselab.cz/→ 200 (landing OK),/.well-known/oauth-protected-resource→ 200 - Backupy:
pulse.s60hub.cz.bak.20260428-mcp(hub),pulselab.cz.bak.20260428-mcp(prod) - Dev (
pulse.s60dev.czna cerebro): mimo sentinel scope — TODO infra agent - Reportoval: pulse (T-20260428-299)
FIX: auth.studio60.cz — Nginx /.well-known/ proxy (MCP OAuth discovery)
- Problém:
https://auth.studio60.cz/.well-known/oauth-authorization-servervracel SPA HTML místo RFC 8414 JSON → Claude Desktop MCP připojení napulselab.cz/mcpselhávalo na 3. kroku discovery flow - Root cause: Nginx config na prod-alfa (
/etc/nginx/sites-enabled/auth.studio60.cz) měl jen exact-matchlocation = /.well-known/openid-configurations rewrite na/api/auth/oauth/openid-configuration. Ostatní/.well-known/*cesty padaly dolocation /→ SPA index.html. Hub-alfa měl správně genericlocation /.well-known/ { proxy_pass http://127.0.0.1:3002; }. - Backend OK: S60Auth backend (CR-116 F1, deployed 2026-04-22) podporoval
/.well-known/oauth-authorization-serveri/.well-known/openid-configurationpřímo na portu 3002 — chyběl jen Nginx route. - Fix: Sjednoceno s hub-alfa —
location = /.well-known/openid-configurationblok nahrazen zalocation /.well-known/sproxy_pass http://127.0.0.1:3002(bez rewrite). - Ověření:
curl https://auth.studio60.cz/.well-known/oauth-authorization-server→ JSON 200 ✅curl https://auth.studio60.cz/.well-known/openid-configuration→ JSON 200 ✅ (regrese OK)- Backup:
/etc/nginx/sites-enabled/auth.studio60.cz.bak.20260428(na prod-alfa) - Reportoval: pulse (T-20260428-284 QUESTION)
2026-03-26
FEAT: Pre-deploy snapshots v deploy.sh (ADR-007)
- Co: Každý deploy automaticky vytvoří snapshot (app.tar.gz + db.sql.gz + env.backup + manifest.json)
- Kde:
/var/backups/deploy-snapshots/<project>/<env>/<timestamp>-<commit>/ - Rotace: <24h all, 1-7d daily, 7-30d weekly, >30d delete (cron 02:00 na hub+prod)
- Rollback:
./deploy.sh <project> <env> --rollback latestnebo--rollback <snapshot-id> - Otestováno: Pulse hub dry-run — snapshot 2.1MB (app 1.9M + DB 192K)
- Motivace: Ztráta Pulse methodology dat kvůli rsync --delete
DEPLOY: Pulse 3aaa1c2 — hub + prod (bugfixy + debug API + migration:run)
- Služba: Pulse hub + prod
- Verze: 0593344 → 3aaa1c2
- Změny: metodiky v kategoriích bez active version fix, debug API endpointy, migration:run npm script, data-source.ts, CR-014 spec
- Migrace: deploy.sh migration_command funguje (npm run migration:run OK, no pending)
- Ověření: Health 200, migration OK
- QA: Hub 10 PASS / 2 FAIL (PDF size + empty session — pre-existing)
DEPLOY: Pulse 0593344 — hub + prod (slug migrace + diacritics fix)
- Služba: Pulse hub + prod
- Verze: a81febc → 0593344 (1 commit)
- Změny: Migrace 1710000000015-FixDiacriticSlugs — přegeneruje špatné slugy v clients + methodology_categories
- Migrace: Proběhla automaticky (migrationsRun:true), deploy.sh migration_command stále failuje (chybí npm script)
- Ověření: Health 200, migrace potvrzena v DB na hub i prod, slugy opravené
DEPLOY: Pulse a81febc — hub + prod (slug fix + synchronize disabled)
- Služba: Pulse hub + prod
- Verze: 8ce2887 → a81febc (3 commity)
- Změny: methodology.controller slug fix (NFD normalize), synchronize:false, slugify regression testy
- Migrace: žádná nová (migration:run script chybí v package.json — TODO pulse)
- Ověření: Health 200 na hub i prod, summaryPdfPath error vyřešen (ALTER COLUMN)
- QA: Hub 11 PASS / 1 FAIL (PDF size — pre-existing)
FIX: Pulse summaryPdfPath column mismatch — hub
- Problém: Migrace vytvořila
summary_pdf_path(snake_case), entity čekalasummaryPdfPath(camelCase) → 500 error na outputs - Root cause:
synchronize: truena hubu vytvořil sloupce v camelCase, migrace pak přidala snake_case duplikát - Fix: ALTER TABLE RENAME COLUMN na hubu; prod byl OK
- Systémový fix: deploy.sh nyní spouští migration_command z deploy.yml; TODO pulse: odstranit synchronize:true
DEPLOY: Pulse 8ce2887 — hub + prod
- Služba: Pulse hub + prod
- Verze: 7634e01 → 8ce2887 (6 commitů, efektivně 2 změny)
- Změny: slugify strips diacritics (admin + backend), Safari layout reverty (net zero), CLAUDE.md Relay URL update
- Migrace: žádná
- Ověření: Health 200 na hub i prod (:3100/health)
- QA: Hub 11 PASS / 1 FAIL (progress endpoint 500 — pre-existing), prod --skip-ci
- Poznámka: Jest ESM failures (6 suites, @exodus/bytes) — TODO poslán pulse agentovi
2026-03-24
DEPLOY: Pulse 7634e01 — hub + prod (CR-013)
- Služba: Pulse hub + prod
- Verze: 541c2a7 → 7634e01 (4 commity)
- Změny: CR-013 Kategorie UX redesign + visibility flag, EntityNotFoundError→404 fix
- Migrace: 1710000000012-AddCategoryVisible (ALTER TABLE methodology_categories ADD COLUMN visible)
- Ověření: Health 200 na hub i prod (:3100/health), migrace ověřena na obou DB
- QA: Hub QA gate pre-existing failures (summaryPdfPath, Venom down), CR-013 OK. Prod --skip-ci (pre-existing test failures)
DEPLOY: Pulse 541c2a7 — hub + prod
- Služba: Pulse hub + prod
- Verze: c33d32c → 541c2a7 (18 commitů)
- Změny: CR-001 credit ledger + tier→subscription, CR-007 summary PDF, CR-008 workshop split-screen, CR-009 categories + prerequisites, CR-010 web URL analysis, CR-011 project-first UI, CR-012 gamification (badges, certificates), spec-compliance testy (31), TS build fix
- Migrace: ano (1710000000014-GamificationTables + další)
- Ověření: Health 200 na hub i prod (:3100/health)
- QA: QA gate false positive (špatné URLs v testech), QA agent opravil testy
DEPLOY: BillIt debe805 — hub + prod
- Služba: BillIt hub + prod
- Verze: 4fea85f → debe805 (1 commit)
- Změny: CR-008 — XLSX export, subjectId filter, async export >1000
- Migrace: ne
- Ověření: Health 200 na hub i prod (billit-api :3200, billit-web :3201)
- QA: skipped (pulse testy false positive, QA agent timeout)
2026-03-23
DEPLOY: BillIt 4fea85f — hub + prod (T-20260323-001, T-20260323-004)
- Služba: BillIt hub + prod
- Verze: bcd8d32 → 4fea85f (3 commity)
- Změny:
- 1b2e3d9 CR-001–011: 11 CR implementací (Stripe idempotence, ARES cache+DPH, Stats cache, Recurring dedup, PDF branding, Email tracking, Exports XLSX, Admin defaults, CSV import, Webhooks 5 retries)
- 8a2f646 DEC-009: BankEmailConsumer (BullMQ worker pro bank-email-inbound)
- 4fea85f npm audit fix (HIGH vulnerabilities)
- Migrace: ano (stripe_events + email_logs tabulky)
- Ověření: Health 200 na hub i prod (billit-api :3200, billit-web :3201)
- QA: skipped
2026-03-22
DEPLOY: Pulse c33d32c — hub + prod (T-20260322-076)
- Služba: Pulse hub + prod
- Verze: bfe405b → c33d32c (1 commit)
- Změny: ACTIVE sessions visibility fix — findResumableByClient vracela jen PAUSED, nyní ACTIVE + PAUSED
- Důvod: URGENT — klienti neviděli rozpracované sessions (Helena 6 sessions, 4 ACTIVE)
- Ověření: Health 200 na hub i prod
- QA: skipped (urgent bugfix, QA agent neodpověděl)
SECURITY: Odebrán cerebro SSH klíč z hub-alfa + prod-alfa (T-20260320-122)
- Služba: hub-alfa, prod-alfa (authorized_keys)
- Příčina: Auth agent z cerebra nasadil 7236286 přímo na prod BEZ sentinelu, BEZ QA gate, PŘED hubem. Cerebro SSH klíč (root@cerebro) byl v authorized_keys na obou serverech → jakýkoli agent na cerebru měl root přístup k produkci.
- Fix:
sed -iodebrání cerebro klíče z /root/.ssh/authorized_keys na hub-alfa i prod-alfa - Ověření: Sentinel SSH OK, cerebro → prod connection refused
- Zbývající klíče: 2 na každém serveru (sentinel + Libor)
DEPLOY: Pulse 4462e58 — hub + prod (T-20260320-122)
- Služba: Pulse hub + prod
- Verze: 6c99b82 → 4462e58 (2 commity)
- Změny: REQ-029 readonly debug API s dvouvrstvou autorizací, REQ-017 pricing integration testy
- Ověření: Health 200 na hub i prod
DEPLOY: Pulse 6c99b82 — hub + prod (T-20260320-122)
- Služba: Pulse hub + prod
- Verze: 10c0b88 → 6c99b82 (~50 commitů)
- Změny: Discord removal (DEC-005), Freelo escalation (REQ-026), cancel webhook (REQ-018), pause/resume (REQ-003), PDF print CSS fix, DB migrace (projects, missing columns, billitInvoiceId), QA readonly user, regression testy, spec catalog
- Blocker: Migrace CreateQaReadonlyUser vyžadovala CREATE ROLE — provedena ručně přes doadmin na hub i prod, zapsána do TypeORM migrations tabulky
- Readiness warn: deploy.yml hledá AUTH_URL/AUTH_CLIENT_ID/AUTH_CLIENT_SECRET, kód používá S60_AUTH_* prefix — obojí funguje (známý problém)
- Ověření: Health 200 na hub i prod
2026-03-20
FIX: Pulse QA user — missing role, tier, credits (T-20260320-122)
- Služba: S60Auth hub + Pulse hub (s60_auth_hub, s60_pulse_hub)
- Příčina: QA user
qa-pulse@s60dev.czse prihlasil pres S60Auth (access OK), ale nemel zadnou roli vuser_system_roles→ JWT obsahovalrolesBySystem: {}→ Pulse auth guard vracel 401 na vsechny authenticated endpointy. Navic v Pulse DBclientstabulce meltier: NULLacreditsRemaining: 1. - Fix (hub):
INSERT INTO user_system_roles (user_id, system_id, role_name) VALUES ('<qa-user-id>', '<pulse-system-id>', 'client')v s60_auth_hubUPDATE clients SET tier = 'triple', "creditsRemaining" = 100v s60_pulse_hub- Prod: QA user jeste neexistuje — musi se nejdriv prihlasit, pak pridat roli + tier
- Pulse system_id:
15912518-c5f1-4926-bf6e-b16948d4c585(stejny hub i prod) - Overeni: QA user se musi znovu prihlasit pro novy JWT s rolemi
INFRA: Gotenberg PDF microservice — hub + prod (T-20260320-092)
- Služba: Gotenberg (gotenberg/gotenberg:8) na hub-alfa + prod-alfa
- Změna: Nový kontejner
s60-gotenbergv/opt/gotenberg/docker-compose.yml, port 127.0.0.1:3050→3000, s60-network - Nginx: pdf.s60hub.cz (hub-alfa), pdf.studio60.cz (prod-alfa) — Tailscale IP whitelist (100.64.0.0/10), deny all
- SSL: Certbot, auto-redirect HTTP→HTTPS
- Pulse integrace: GOTENBERG_URL=http://s60-gotenberg:3000 v .env (Docker network, ne veřejná URL)
- Fallback: Pulse padá na lokální Puppeteer pokud GOTENBERG_URL není nastaveno
- Důvod: Izolovaný Chromium pro PDF generování, stabilnější než in-process Puppeteer
DEPLOY: Pulse 2a8a75e — mobile UX fix (T-20260320-092)
- Služba: Pulse hub + prod
- Commity: 2e457a4, 931f60d, bc9eb9b, f9ed892, 2a8a75e
- Změny: Cookie session 1h→30d (mobilní klientky), scroll fix (wake/tab switch), textarea autoComplete off, Gotenberg PDF integrace, feedback mailto fallback
- Důvod: Klientky na mobilu ztrácely session, UX problémy
DNS: s60hub.cz — převzetí zóny na hub-alfa (T-20260320-085)
- Změna: Přesměrováno *.s60hub.cz, s60hub.cz, www.s60hub.cz z 164.90.182.148 (starý server) na 178.104.40.167 (hub-alfa). Smazáno portal.s60hub.cz, wiki.s60hub.cz.
- Důvod: Sentinel přebírá DNS správu pro hub + prod. Celá zóna s60hub.cz nyní ukazuje na hub-alfa.
- Stav: 11 A záznamů, vše → 178.104.40.167
CONFIG: Pulse nginx — odebrán location /outputs/ block (T-20260320-063)
- Služba: Pulse prod (pulselab.cz, prod-alfa)
- Změna: Odebrán
location /outputs/ { alias /var/www/public/pulse/; }z/etc/nginx/sites-enabled/pulselab.cz - Důvod: Po deployi 6e9ba7d NestJS servíruje
/outputs/:hash/:slug.htmlz DB. Nginx location block měl vyšší prioritu a blokoval NestJS. Hub-alfa tento block neměl. - Ověření: health 200,
/outputs/vrací 404 z NestJS (ne 403 autoindex off)
FIX: Pulse dev DB ownership — rename + dedicated user (T-20260320-019)
- Služba: Pulse dev (pulse.s60dev.cz, cerebro)
- Příčina: TypeORM synchronize padal — "must be owner of table sessions". Tabulky patřily doadmin, app běžela jako s60_infra_user
- Fix: Vytvořen
s60_pulse_dev_user, DB přejmenovánas60_pulse→s60_pulse_dev, ownership všech tabulek/sekvencí/schématu převeden na nového usera - Ověření:
curl http://127.0.0.1:3100/health→{"status":"ok","db":"ok"} - Credentials:
/root/secrets/pulse/.env.dev.db
2026-03-18
CONFIG: BillIt systems record — access_policy=open, default_roles={user}
- Služba: s60-auth (hub + prod)
- DB: s60_auth_hub, s60_auth_prod — tabulka
systems - Změna:
access_policyrestricted→open,managed_byadmin→api,default_roles→{user} - Důvod: BillIt je SaaS — každý přihlášený uživatel dostane auto-přístup s rolí 'user'
- Požadavek od: auth agent
FIX: Auth CORS — custom domény blokovány
- Služba: s60-auth-backend (prod + hub)
- Příčina: CORS whitelist v main.ts povoloval jen *.s60dev/hub/studio60.cz
- Fix: Přidány billit.cz, pulselab.cz, learnia.cz, kvantovaterapie.cz do allowedOrigins
- Ověření: Billit login flow funguje, token exchange projde
- POZOR: Fix je přímo na serverech, auth agent musí commitnout do repo
FIX: Auth missing DB columns — marketing_html, purchase_url, is_favorite
- Služba: s60-auth-backend (prod + hub)
- Příčina: Auth agent přidal @Column do entit bez TypeORM migrace
- Fix: ALTER TABLE systems ADD COLUMN marketing_html, purchase_url; ALTER TABLE user_system_access ADD COLUMN is_favorite
- Ověření: getPortalSystems endpoint vrací seznam aplikací bez QueryFailedError
FIX: Billit prod redirect na s60dev.cz
- Služba: billit-web (prod)
- Příčina: Chybějící VITE_AUTH_CLIENT_ID/SECRET/REDIRECT_URI/PUBLIC_URL v .env.prod → fallback na dev defaults
- Fix: Přidány VITE vars do /root/secrets/billit/.env.prod, opraven home_url v auth DB, rebuild
- Ověření: Žádné s60dev reference v buildu, login flow správně redirectuje na billit.cz
FIX: Billit auth DB home_url
- Služba: s60_auth_prod.systems
- Příčina: BillIt system record měl home_url=https://billit.s60dev.cz
- Fix: UPDATE systems SET home_url='https://billit.cz' WHERE name='BillIt'
2026-03-15
FIX: Pulse HEALTHCHECK port 3200→3100
- Služba: s60-pulse (hub + prod)
- Příčina: Dockerfile HEALTHCHECK cílil na port 3200, app běží na 3100
- Fix: Změna v Dockerfile na hub+prod, rebuild
- Ověření: Container healthy, HTTP 200 na /health
FIX: billit-web HEALTHCHECK IPv6
- Služba: billit-web (hub + prod)
- Příčina: HEALTHCHECK používal localhost (resolves to ::1 IPv6), nginx jen IPv4
- Fix: localhost→127.0.0.1 v Dockerfile
- Ověření: Container healthy
FIX: PG backup Argus — chybějící IP + heslo
- Služba: Argus backup cron
- Příčina: DO PG trusted sources reset bez Argus IP + doadmin heslo změněno
- Fix: Přidána Argus IP 37.205.14.239, aktualizován PGPASSWORD + PGSSLMODE=require
- Ověření: 12/12 DB backup OK
FIX: deploy.sh — docker compose down zbytečný downtime
- Služba: deploy/deploy.sh
- Příčina: deploy.sh dělal
docker compose down && up -d→ zbytečný downtime - Fix: Odstraněn
down, používá seup -d --build/--force-recreate
FIX: deploy.sh — QA gate arg order + path
- Služba: deploy/deploy.sh
- Příčina: qa-gate.sh volaný se špatným pořadím argumentů + špatná cesta
- Fix: Opraveno na
qa-gate.sh $ENV $SERVICE --tier $QA_TIER, cesta na /root/projects/qa/qa-gate.sh
2026-03-29 — billit 8771b10 (CRITICAL)
Commit: 8771b10 — fix(billit): email-first matching in tenant_users
Env: hub + prod
Status: ✅ DEPLOYED
Migration: none (db_change: none)
Změny:
- tenants.service.ts: findAll email-first, user_id jako fallback
- KnownUserInterceptor: auto-fix stale user_id na matching email
- KnownUserInterceptor: auto-fix placeholder emails na matching user_id
Dopad: Liborovy tenanty se automaticky opraví při přihlášení.
Health: billit-api + billit-web 200 OK na obou serverech.
2026-03-30 — billit 83c9835 (hub + prod)
- Commit: 83c9835 — fix(billit): add missing migration for enabled_payment_methods + auto_emails
- Migration: 1772410000000-AddTenantPaymentMethodsAndAutoEmails
- ALTER TABLE tenants ADD COLUMN IF NOT EXISTS enabled_payment_methods JSONB DEFAULT '["bank","card"]'
- ALTER TABLE tenants ADD COLUMN IF NOT EXISTS auto_emails JSONB DEFAULT '{...7 events...}'
- Hub: deployed + migrace OK, health 200
- Prod: deployed + migrace OK, health 200
- Poznámka: hub DB migrations table byla prázdná (0 záznamů) — vloženy fake záznamy pro 16 předchozích migrací před spuštěním nové