Skip to content
Draft
Show file tree
Hide file tree
Changes from 250 commits
Commits
Show all changes
443 commits
Select commit Hold shift + click to select a range
1b7111a
fix(assistant): stop the prose-number gate rejecting legit Bulgarian …
cefothe Jul 2, 2026
a6b3888
feat(assistant): add find_entity tool for Cyrillic-safe entity lookup
cefothe Jul 2, 2026
594b69b
Merge pull request #23 from lyubomir-bozhinov/feat/assistant-history-…
lyubomir-bozhinov Jul 2, 2026
609cd80
feat(assistant): server-side last-resort report finalizer
cefothe Jul 2, 2026
d7435d9
fix(assistant): mask emit_report tool-input-error; fold the internal-…
nikimilenkov Jul 2, 2026
f81e2f4
feat(assistant): humanize fallback report labels (Bulgarian, not raw …
cefothe Jul 2, 2026
24e1f48
style(assistant): prettier-format describe-schema (unblock repo-wide …
cefothe Jul 2, 2026
cfd0c02
docs(assistant): record resolution status on the question-sweep report
cefothe Jul 2, 2026
702a931
Merge fork/feat/ai-assistant-contracts into feat/assistant-stream-phases
nikimilenkov Jul 2, 2026
e7dc828
fix(assistant): close three UI-sweep failure modes (empty turn, bad %…
cefothe Jul 2, 2026
130b6bb
fix(assistant): don't flash "couldn't compose" during an emit retry t…
cefothe Jul 2, 2026
212927f
fix(assistant): enforce a date window on signed_at-bucketed series (n…
cefothe Jul 2, 2026
ac0c369
fix(assistant): keep entity name in single-row fallback, fix count fo…
cefothe Jul 3, 2026
f3931d1
refactor(assistant): harden report-finalizer wrapper and derive promp…
cefothe Jul 3, 2026
a98542a
style(assistant): apply prettier formatting to finalizer changes
cefothe Jul 3, 2026
d69e054
fix(assistant): reject a totals figure bound to a multi-row series
cefothe Jul 3, 2026
5d97c01
fix(assistant): treat bid/offer counts as numbers in report fallback
cefothe Jul 3, 2026
62d1c64
style(assistant): prettier-format the find_entity data-dictionary rule
cefothe Jul 2, 2026
3648fa2
feat(assistant): nudge a leading totals headline so list reports show…
nikimilenkov Jul 2, 2026
433d5d1
fix(assistant): scope the totals-headline rule to table/bar and pin s…
nikimilenkov Jul 2, 2026
1ba2815
Merge feat/ai-assistant-contracts into feat/assistant-report-fallback
lyubomir-bozhinov Jul 3, 2026
01abb47
Merge pull request #31 from lyubomir-bozhinov/feat/assistant-report-f…
lyubomir-bozhinov Jul 3, 2026
d4d1e0d
Merge remote-tracking branch 'fork/feat/ai-assistant-contracts' into …
nikimilenkov Jul 3, 2026
6f4a392
Merge pull request #24 from lyubomir-bozhinov/feat/assistant-stream-p…
lyubomir-bozhinov Jul 3, 2026
1907341
Merge upstream/main into fork main (app.css split + nav + risk-indica…
lyubomir-bozhinov Jul 3, 2026
0461dfc
Merge remote-tracking branch 'origin/main' into sync-contracts
lyubomir-bozhinov Jul 3, 2026
f709322
feat(assistant): Turnstile edge gate on /assistant/chat (H3, server-s…
Jul 3, 2026
f2bcc86
test(assistant): type-safe turnstile test — capture body in the fetch…
Jul 3, 2026
ed2b7fe
fix(web): report page bug fixes — mobile dock and partial column bind…
DiyanaDimitrova Jul 5, 2026
871caa8
feat(assistant): risk-scaled LLM verifier (role ④) behind the determi…
ydimitrof Jul 5, 2026
57d8761
feat(assistant): dock markdown, report dedup (Lane F), and launch-gat…
lyubomir-bozhinov Jul 5, 2026
cfc328c
Merge upstream/main into fork main (sync 2026-07-06)
lyubomir-bozhinov Jul 5, 2026
1f77eaa
docs: index dev-environments docs for the new docs-integrity gate
lyubomir-bozhinov Jul 5, 2026
daa783b
Merge pull request #44 from lyubomir-bozhinov/sync/main-2026-07-06
lyubomir-bozhinov Jul 5, 2026
3c0af38
Merge fork main into feat/ai-assistant-contracts (sync 2026-07-06)
lyubomir-bozhinov Jul 5, 2026
a49b2c1
Merge pull request #46 from lyubomir-bozhinov/sync/contracts-2026-07-06
lyubomir-bozhinov Jul 5, 2026
2d1e5db
feat(assistant): shed mid-stream gateway 429 with distinct overload m…
ydimitrof Jul 6, 2026
fc834e8
feat(assistant): wcag 2.2 aa accessibility pass for report blocks and…
ydimitrof Jul 6, 2026
73244e6
feat(assistant): mandate report details and unify insufficient-data m…
ydimitrof Jul 6, 2026
07b2a86
feat(assistant): route AI Gateway to BgGPT (mamay) custom provider (#53)
cefothe Jul 6, 2026
f064e4a
docs(assistant): reconcile RAG + circuit-breaker notes with shipped c…
lyubomir-bozhinov Jul 6, 2026
f63dd76
fix(assistant): canonical queries use is_synthetic flag, drop tenders…
DiyanaDimitrova Jul 7, 2026
2979b62
feat(assistant): set Turnstile widget site key + provision preview se…
cefothe Jul 7, 2026
6a93716
feat(assistant): client Turnstile bot gate — invisible widget + token…
mdaskalo Jul 7, 2026
75f6386
chore: register related-persons-data workflow on main (throwaway, rev…
lyubomir-bozhinov Jul 7, 2026
b03a817
fix(assistant): render reuse affordance for a dedup cache hit (#56)
lyubomir-bozhinov Jul 7, 2026
d2560ea
fix(assistant): block json_* aggregate/builder functions in the SQL g…
ydimitrof Jul 7, 2026
4819f68
fix(assistant): fail closed on missing Turnstile secret in production
ydimitrof Jul 7, 2026
e91158a
ci(deploy): pass github.ref via env to avoid shell interpolation
ydimitrof Jul 7, 2026
1bda180
feat(assistant): voice input lane (/assistant/transcribe)
nikimilenkov Jul 7, 2026
5810919
docs(assistant): index voice-transcribe plan for the docs-integrity gate
nikimilenkov Jul 7, 2026
b00e195
fix(assistant): cap the transcribe body read + formalize the DoW laun…
nikimilenkov Jul 7, 2026
d85cda8
chore: remove throwaway related-persons-data workflow from main (ETL …
lyubomir-bozhinov Jul 7, 2026
706bc87
feat(assistant): wire HMAC transcript signing end-to-end (§9.3)
lyubomir-bozhinov Jul 7, 2026
f858cac
test(assistant): end-to-end signer→ingest symmetry against real SDK p…
lyubomir-bozhinov Jul 7, 2026
bcac98e
fix(assistant): exclude synthetic orphans from reconcilable rollups a…
cefothe Jul 8, 2026
be43a04
fix(db): renumber the duplicate 0001 migration to 0003
cefothe Jul 8, 2026
01ba6b0
perf(assistant): wake single-flight waiters before persisting cache l…
cefothe Jul 8, 2026
a7738f9
docs(assistant): document the unsigned-transcript accepted risk in th…
cefothe Jul 8, 2026
b053bb9
docs(assistant): inventory required Cloudflare services and fix stale…
cefothe Jul 8, 2026
0f3c517
fix(assistant): block nested replace() string-bomb and deny the json_…
cefothe Jul 8, 2026
524df28
docs(assistant): clarify Turnstile fails closed on all deployed targe…
cefothe Jul 8, 2026
329f259
fix(assistant): COALESCE the is_synthetic backfill against a dangling…
cefothe Jul 8, 2026
a7e944a
feat(assistant): git-declared AI Gateway provisioning for the voice r…
lyubomir-bozhinov Jul 8, 2026
acf9718
Merge base feat/ai-assistant-contracts into voice-transcribe
lyubomir-bozhinov Jul 8, 2026
d10c2e6
fix(assistant): harden §9.3 HMAC — pin SDK, bind data-report-ready, f…
lyubomir-bozhinov Jul 8, 2026
7de0442
fix(assistant): AI-Gateway version body uses `elements`, not `data`
lyubomir-bozhinov Jul 8, 2026
fd8e77a
style: prettier-format ensure-voice-provider (repo config)
lyubomir-bozhinov Jul 8, 2026
80f67c7
feat(assistant): self-provision ASSISTANT_HMAC_KEY and stamp ENVIRONMENT
lyubomir-bozhinov Jul 8, 2026
e4673a1
fix(assistant): voice route is bggpt-primary-only; fallback moves to …
lyubomir-bozhinov Jul 8, 2026
aa30584
feat(assistant): add voice-fallback dynamic route (workers-ai) + mult…
lyubomir-bozhinov Jul 8, 2026
aa62a66
fix(assistant): refuse hollow single-row fallback reports
lyubomir-bozhinov Jul 8, 2026
c97785f
refactor(assistant): voice provisioning is provider-only; drop dynami…
lyubomir-bozhinov Jul 8, 2026
8837157
style: prettier-format test with the repo's own config (fix CI lint)
lyubomir-bozhinov Jul 8, 2026
653ee4b
fix(assistant): block string_agg/json_pretty and invert run_sql funct…
cefothe Jul 8, 2026
7a1ccc8
docs(adr-0011): record data-plane verification (both legs 200; cf-aig…
lyubomir-bozhinov Jul 8, 2026
5ab2bb9
fix(assistant): add answer_directly escape hatch so non-data turns sk…
lyubomir-bozhinov Jul 8, 2026
50e4210
feat(assistant): route voice STT through the AI Gateway (ADR-0011)
nikimilenkov Jul 8, 2026
c0075f9
fix(assistant): bound cross-CTE string-length amplification (replace/…
cefothe Jul 8, 2026
ec6c215
fix(assistant): harden voice STT after review (env-only URL, sanitize…
nikimilenkov Jul 8, 2026
42fff23
fix(assistant): bound string amplification by compounding, not flat o…
cefothe Jul 8, 2026
9cd34b7
test(assistant): adversarial per-class coverage for sanitizeTranscript
nikimilenkov Jul 8, 2026
291bd8c
docs(adr): renumber voice ADR 0011 -> 0013 (clash with PR #68 hmac ADRs)
nikimilenkov Jul 8, 2026
9bf80ca
fix(assistant): gate voice STT on the account-wide BgGPT circuit-brea…
nikimilenkov Jul 8, 2026
94eb2fb
fix(assistant): attach Turnstile token to voice transcribe requests
nikimilenkov Jul 8, 2026
8344b72
Merge pull request #69 from lyubomir-bozhinov/fix/assistant-fallback-…
lyubomir-bozhinov Jul 8, 2026
3645b16
Merge pull request #64 from lyubomir-bozhinov/fix/assistant-security-…
lyubomir-bozhinov Jul 8, 2026
dad9e25
Merge pull request #66 from lyubomir-bozhinov/feat/assistant-voice-tr…
lyubomir-bozhinov Jul 8, 2026
7a61b13
Merge feat/ai-assistant-contracts into feat/assistant-hmac-wiring
tradu-lboz Jul 8, 2026
cd598ec
fix(assistant): pass isProd to turnstileRejection in voice transcribe…
tradu-lboz Jul 8, 2026
dd3a657
Merge remote-tracking branch 'origin/feat/ai-assistant-contracts' int…
tradu-lboz Jul 8, 2026
3b5e176
Merge pull request #68 from lyubomir-bozhinov/feat/assistant-hmac-wiring
lyubomir-bozhinov Jul 8, 2026
0079753
fix(assistant): deny personal-contact columns in the run_sql guard (P…
DiyanaDimitrova Jul 8, 2026
114c01d
fix(web): a11y — watermark badge contrast + sr-only in text-only mode
DiyanaDimitrova Jul 8, 2026
1b11c3e
fix(web): redact prose telemetry + robots disallow /reports and /*.data
DiyanaDimitrova Jul 8, 2026
430f518
fix(assistant): validate base64 before decode on /assistant/transcrib…
DiyanaDimitrova Jul 8, 2026
b5b015c
perf(web): lazy-load assistant dock + error boundary, stream timeout,…
DiyanaDimitrova Jul 8, 2026
3a0d2e0
fix(assistant): recover a dropped summary when post-tool text is empty
ydimitrof Jul 8, 2026
6963c04
fix(assistant): skip the header-only recap and cap the report gist
ydimitrof Jul 8, 2026
c3ce6a3
fix(assistant): harden report export output
ydimitrof Jul 8, 2026
d396e27
fix(assistant): distinguish BLOB values in the dedup fingerprint
ydimitrof Jul 8, 2026
3621a4d
fix(assistant): drop transcript messages whose reports field is not a…
ydimitrof Jul 8, 2026
b71e36d
fix(assistant): cap totals evidence and sweep facts for non-finite va…
ydimitrof Jul 8, 2026
da7f498
fix(web): keep 'self' in the frame-src CSP directive
ydimitrof Jul 8, 2026
cfcb95d
fix(web): render stored reports at a stable schemaVersion floor
ydimitrof Jul 8, 2026
a4c789e
fix(etl): label the prompt window from its first included day
ydimitrof Jul 8, 2026
f2d7d98
perf(etl): scope the is_synthetic refresh update to touched contracts
ydimitrof Jul 8, 2026
82c68a9
test(web): share the vitest alias map with the golden config
ydimitrof Jul 8, 2026
4ec4564
test(assistant-dock): fully drain async work before asserting prompt …
ydimitrof Jul 8, 2026
b1af3da
refactor(assistant): review-follow-up cleanups
ydimitrof Jul 8, 2026
49cc64f
docs(assistant): reconcile spec and ADR drift
ydimitrof Jul 8, 2026
18f6cef
fix(web): register vitest.alias.ts in tsconfig.node.json include
ydimitrof Jul 8, 2026
a8c66bd
style(web): apply prettier to files from the review-fix commits
ydimitrof Jul 8, 2026
32f19b0
docs(assistant): add go-live provisioning runbook
lyubomir-bozhinov Jul 8, 2026
46ca5c0
fix(assistant): reject double-quoted identifiers in the run_sql guard…
ydimitrof Jul 8, 2026
5d7e1ee
docs(etl): document the is_synthetic touched-scope invariants in refr…
ydimitrof Jul 8, 2026
541a65d
fix(assistant): scope PRIV-1 star rule to the table it expands + add …
DiyanaDimitrova Jul 8, 2026
09e5849
fix(web): fall back for AbortSignal.any so older browsers keep the st…
DiyanaDimitrova Jul 8, 2026
d2698dc
fix(web): log suppressed assistant-dock crashes (componentDidCatch)
DiyanaDimitrova Jul 8, 2026
28f1f4a
style(assistant): prettier-format the PRIV-1 star-rule tests (fix CI …
DiyanaDimitrova Jul 8, 2026
1c453c2
fix(assistant): cap model-facing result preview to prevent context ov…
cefothe Jul 8, 2026
06fe0d9
fix(assistant): allow read-only bitwise/shift opcodes in the SQL opco…
cefothe Jul 8, 2026
81330e7
fix(assistant): document region as a name + inject canonical CPV map …
cefothe Jul 8, 2026
a028944
fix(assistant): don't headline the grand total for top-N ranking reports
cefothe Jul 8, 2026
c174dab
fix(etl): regenerate starter prompts on each data refresh
cefothe Jul 8, 2026
cb006c5
Merge pull request #77 from lyubomir-bozhinov/fix/assistant-report-qu…
cefothe Jul 9, 2026
46c4b61
Merge pull request #75: assistant strict-review + report-quality fixes
lyubomir-bozhinov Jul 9, 2026
f5af092
docs(assistant): sync implementation README with the shipped feature
lyubomir-bozhinov Jul 9, 2026
546d973
style(assistant): prettier-format README (parity with feat)
lyubomir-bozhinov Jul 9, 2026
c4907ab
fix(assistant): address correctness + secret-hygiene review findings
lyubomir-bozhinov Jul 10, 2026
b6c0696
fix(assistant): hide internal entity-id scheme in cells and bound rec…
lyubomir-bozhinov Jul 10, 2026
1c065d5
fix(assistant): collapse composite contract ids so the embedded bidde…
lyubomir-bozhinov Jul 10, 2026
f381cb1
fix(assistant): exclude user-message report chips from the signed sum…
lyubomir-bozhinov Jul 11, 2026
5dfcecf
chore: sync fork main with upstream (entity-list search #204, amendme…
lyubomir-bozhinov Jul 11, 2026
af0b881
docs(weekly-digest): add implementation plan and producer ticket (#167)
DiyanaDimitrova Jul 16, 2026
9a0dfb2
feat(db): weekly digest queries + 0004 migration (#167)
ydimitrof Jul 16, 2026
458b12c
docs(weekly-digest): add consumer ticket (#167B)
DiyanaDimitrova Jul 16, 2026
4e173f1
feat(weekly-digest): consumer render layer + /weeks routes (#167B)
DiyanaDimitrova Jul 16, 2026
299d403
refactor(report): extract @sigma/report package (#167)
ydimitrof Jul 16, 2026
49ebc88
feat(report): decoupled persist + iso-week util (#167)
ydimitrof Jul 16, 2026
7035523
chore(assistant): remove orphaned r2-report-object fixture (#167)
ydimitrof Jul 16, 2026
b852971
Merge remote-tracking branch 'upstream/main' into wt-main-sync
lyubomir-bozhinov Jul 16, 2026
84e27aa
chore: sync fork main with upstream (osv-scanner CI, eu-benchmark pan…
lyubomir-bozhinov Jul 16, 2026
4edc11f
build(etl): AI + REPORTS bindings + DIGEST_CRON (#167)
ydimitrof Jul 16, 2026
5abdc1c
feat(etl): weekly digest cron + generator (#167)
ydimitrof Jul 16, 2026
3a368e3
Merge fork main into feat/ai-assistant-contracts (sync 2026-07-16)
lyubomir-bozhinov Jul 16, 2026
e277ae3
fix(etl): log empty-narrative fallback path in weekly digest
ydimitrof Jul 16, 2026
2ad6d4d
Merge fork main (b852971) into feat/ai-assistant-contracts — collapse…
lyubomir-bozhinov Jul 16, 2026
5f788da
fix(ci): pass REPORTS + VECTORIZE names to preview so bindings resolv…
cefothe Jul 16, 2026
a9da01c
Merge fork main into feat/ai-assistant-contracts — preview REPORTS/VE…
lyubomir-bozhinov Jul 16, 2026
8c1828b
Merge remote-tracking branch 'origin/feat/weekly-digest' into feat/we…
ydimitrof Jul 16, 2026
82feda9
Merge remote-tracking branch 'origin/feat/ai-assistant-contracts' int…
ydimitrof Jul 16, 2026
ab321c2
style(db): prettier-format weekly digest queries
ydimitrof Jul 16, 2026
35d2b26
feat(ci): swap AI-Gateway account id + Turnstile site key per account…
cefothe Jul 16, 2026
697907d
fix(ci): drop docs/ path from wrangler-render comment to satisfy docs…
lyubomir-bozhinov Jul 16, 2026
0416d70
fix(db): count clean-amount rows separately in the weekly counts
ydimitrof Jul 16, 2026
715ce61
fix(etl): pair the digest's contract count with the money sum
ydimitrof Jul 16, 2026
e22c3bf
fix(etl): label a stripped-narrative digest as AI-free
ydimitrof Jul 16, 2026
f800778
Merge origin/feat/weekly-digest into feat/weekly-digest-consumer
DiyanaDimitrova Jul 16, 2026
eb658aa
feat(weekly-digest): daily ghost-bar chart, competition bar, explore …
DiyanaDimitrova Jul 16, 2026
886b5bb
docs(weekly-digest): index the plan + tickets in docs/README so check…
DiyanaDimitrova Jul 16, 2026
e808068
refactor(weekly-digest): address #81 review notes
DiyanaDimitrova Jul 17, 2026
d8b60f6
fix(weeks): bounded cache instead of immutable so corrected digests p…
DiyanaDimitrova Jul 17, 2026
df1126b
Merge remote-tracking branch 'upstream/main' into wt-main2
lyubomir-bozhinov Jul 17, 2026
931b2b2
Merge fork main into feat/ai-assistant-contracts (sync 2026-07-17)
lyubomir-bozhinov Jul 17, 2026
00e8039
Merge remote-tracking branch 'origin/feat/ai-assistant-contracts' int…
ydimitrof Jul 17, 2026
31fb4d4
feat(weeks): report-page layout + export toolbar (Markdown/Word/PDF) …
DiyanaDimitrova Jul 20, 2026
8cf0e52
fix(weeks): short digest cache TTL (5m) so corrections + deploys prop…
DiyanaDimitrova Jul 20, 2026
66ffec3
feat(nav): add „Седмични обзори" (/weeks) to the site menu
DiyanaDimitrova Jul 20, 2026
196c739
fix(weeks): use the standard wide page column (like /contracts), not …
DiyanaDimitrova Jul 20, 2026
7ea3d0f
fix(export): handle the weekbars block in Markdown + Word export (#81…
DiyanaDimitrova Jul 20, 2026
b25f50c
feat(etl): expand the weekly digest into a 3–4 paragraph „Какво се сл…
DiyanaDimitrova Jul 20, 2026
6fab27b
test(etl): add a weekly-digest R2 seeding script for the /weeks routes
DiyanaDimitrova Jul 20, 2026
817a1d7
refactor(etl): rename the weekly report „дайджест" → „обзор"
DiyanaDimitrova Jul 20, 2026
8c8d4f3
feat(etl): deepen the weekly „Какво се случи" into a ≥5 paragraph dat…
DiyanaDimitrova Jul 20, 2026
8c94799
test(etl): seed a ≥5 paragraph analytical narrative to match v3
DiyanaDimitrova Jul 20, 2026
d302a87
fix(web): shorten the digest route SWR so re-seeds/corrections propag…
DiyanaDimitrova Jul 20, 2026
88bb6bf
fix(web): stop edge/shared-caching the weekly digest detail page so r…
DiyanaDimitrova Jul 20, 2026
d07e8b0
fix(export): align weekbars rows off the longer series so no prior-we…
DiyanaDimitrova Jul 20, 2026
1103e80
feat(weeks): add a legend for the digest graphics + tables
DiyanaDimitrova Jul 20, 2026
075a98c
feat(weeks): label each digest section with an inline heading instead…
DiyanaDimitrova Jul 20, 2026
88e3091
feat(weeks): make the whole archive row clickable, not just the week …
DiyanaDimitrova Jul 20, 2026
4509dda
fix(weeks): draw the row-link keyboard focus ring on the row-sized ov…
DiyanaDimitrova Jul 20, 2026
db1823f
chore(weekly-digest): address PR #81 review (dead code, weekbars robu…
DiyanaDimitrova Jul 20, 2026
ccf042a
fix(weeks): tighten ISO-week regex + sr-only completeness (PR #81 rev…
DiyanaDimitrova Jul 21, 2026
cf20462
feat(report): add isoWeekFromId, the inverse of priorIsoWeek
ydimitrof Jul 21, 2026
73262ef
feat(etl): configurable digest schedule + on-demand test trigger
ydimitrof Jul 21, 2026
e6bd83e
refactor(etl): rename digest LLM key BGGPT_API_KEY → ASSISTANT_API_KEY
ydimitrof Jul 21, 2026
d130628
chore(etl): enable on-demand digest trigger for dev testing
ydimitrof Jul 21, 2026
5e760b0
chore(etl): enable workers.dev route for dev digest-trigger testing
ydimitrof Jul 21, 2026
89f1162
fix(weeks): stop edge-caching the /weeks archive too, so add/remove s…
DiyanaDimitrova Jul 21, 2026
c8c6b75
chore(deps): pin sharp ^0.35.3 in workspace overrides (GHSA-f88m-g3jw…
lyubomir-bozhinov Jul 22, 2026
9298b90
feat(etl): show human-readable date range in weekly digest title
ydimitrof Jul 22, 2026
b9c761d
fix(deploy): rename etl REPORTS R2 bucket per environment
ydimitrof Jul 22, 2026
504a508
fix(deploy): rewrite etl AI_GATEWAY_BASE_URL account per environment
ydimitrof Jul 22, 2026
565f976
fix(deploy): pass SIGMA_AI_GATEWAY_ACCOUNT to the deploy render step
ydimitrof Jul 22, 2026
d4b99d0
style(weeks): enlarge the digest section headings so they're noticed
DiyanaDimitrova Jul 22, 2026
ca676fc
chore: retrigger CI/preview (dropped synchronize event for d4b99d0)
DiyanaDimitrova Jul 22, 2026
ba07b83
Merge remote-tracking branch 'origin/feat/weekly-digest' into feat/we…
DiyanaDimitrova Jul 22, 2026
bcfcfb9
build(deps): override transitive sharp to >=0.35.0 (GHSA-f88m-g3jw-g9cj)
DiyanaDimitrova Jul 22, 2026
97cdb27
build(deps): pin esbuild/undici/ws safe floors alongside sharp, fix a…
DiyanaDimitrova Jul 22, 2026
072ad8f
style: prettier-format wrangler-render.test.mjs (merged unformatted f…
DiyanaDimitrova Jul 22, 2026
bf8096e
fix(etl): run the digest verifier at temperature 0 for reliable JSON …
ydimitrof Jul 23, 2026
016e877
fix(etl): disable BgGPT thinking so the digest summary is a clean sen…
ydimitrof Jul 23, 2026
de809ca
fix(etl): keep the digest verifier reasoning, suppress thinking on na…
ydimitrof Jul 23, 2026
d7eaba2
fix(etl): keep the verifier on no-think — reasoning breaks its JSON
ydimitrof Jul 23, 2026
911a07e
feat(etl): enrich weekly-digest narrative with named sector, authorit…
ydimitrof Jul 23, 2026
f851de1
chore(etl): dev-only DIGEST_DEBUG logging for narrative + verifier ve…
ydimitrof Jul 23, 2026
8bf58de
fix(etl): stop the digest verifier false-positive-stripping grounded …
ydimitrof Jul 23, 2026
412e6fc
feat(etl): rename digest to „Седмичен обзор" and label sector bar wit…
ydimitrof Jul 23, 2026
7b76d1c
feat(etl): stamp totalEur + week dates into digest R2 customMetadata
ydimitrof Jul 23, 2026
76d8a75
feat(web): label the /weeks archive by Mon–Sun date range, not the IS…
ydimitrof Jul 23, 2026
af60f77
Merge feat/weekly-digest into feat/weekly-digest-consumer
ydimitrof Jul 23, 2026
842fe1f
style: prettier-format reconciled digest + weeks tests
ydimitrof Jul 23, 2026
d246339
fix(etl): bound the narrative LLM call + disable committed DIGEST_DEB…
DiyanaDimitrova Jul 23, 2026
369808e
feat(weeks): remove the archive sparkline
DiyanaDimitrova Jul 23, 2026
78fef0a
build(deps): bump react-router 7.15.1 → 7.18.0 (GHSA-337j / GHSA-h8fp…
DiyanaDimitrova Jul 23, 2026
2c6ef07
docs: index the #167 weekly-digest plan + producer ticket (docs-integ…
ydimitrof Jul 24, 2026
0e777b9
build(deps): clear osv-scanner findings — react-router 7.18.0 + sharp…
ydimitrof Jul 24, 2026
6eb4539
style: prettier-format two files the now-blocking lint gate flagged
ydimitrof Jul 24, 2026
b52411e
Merge remote-tracking branch 'origin/feat/weekly-digest' into feat/we…
DiyanaDimitrova Jul 24, 2026
683bf05
feat(weekly-digest): surface the §10.4 „коригирано" note; address PR …
DiyanaDimitrova Jul 24, 2026
955c0d5
fix(weekbars): pair the two series by label everywhere, not array ind…
DiyanaDimitrova Jul 24, 2026
9cd3d57
fix(weekly-digest): error handling, a11y, doc drift (PR #81 multi-age…
DiyanaDimitrova Jul 24, 2026
db2085c
Merge remote-tracking branch 'upstream/main' into HEAD
lyubomir-bozhinov Jul 26, 2026
9600147
fix(weekly-digest): footer order, CSS scoping, export column, comment…
DiyanaDimitrova Jul 27, 2026
70bd440
feat(weeks): noindex /weeks/:iso — it names winning bidders publicly …
DiyanaDimitrova Jul 27, 2026
28910bb
build(deps): patch postcss + valibot; ignore inapplicable react-route…
DiyanaDimitrova Jul 27, 2026
d9418cd
fix(etl): reject a truncated narrative (finishReason=length) instead …
DiyanaDimitrova Jul 27, 2026
b8c4618
fix(weekly-digest): safe seed defaults, .data-twin noindex, malformed…
DiyanaDimitrova Jul 27, 2026
4a5198b
Merge pull request #81 from lyubomir-bozhinov/feat/weekly-digest-cons…
DiyanaDimitrova Jul 27, 2026
64fe785
Merge upstream/main into fork main (similar-contracts #210, leaf-inde…
lyubomir-bozhinov Jul 29, 2026
aebcca2
Merge fork main into feat/ai-assistant-contracts (sync 2026-07-29)
lyubomir-bozhinov Jul 29, 2026
6e505ee
Merge remote-tracking branch 'origin/feat/ai-assistant-contracts' int…
DiyanaDimitrova Jul 30, 2026
8ecf66b
test(weekly-digest): discriminating value_suspect+amount row; declare…
DiyanaDimitrova Jul 30, 2026
626c27f
refactor(web): narrow the @sigma/report shims to explicit re-exports
DiyanaDimitrova Aug 7, 2026
199b4ff
build(deps): bump undici override to 7.29.0 to clear the Dependency a…
DiyanaDimitrova Aug 7, 2026
e8eef56
Merge upstream/main into feat/ai-assistant-contracts (sync 2026-08-18)
lyubomir-bozhinov Aug 18, 2026
6151acb
test(web): exclude JSON fixtures from coverage so the upstream ratche…
lyubomir-bozhinov Aug 18, 2026
a317e6d
Merge origin/feat/ai-assistant-contracts into feat/weekly-digest
cefothe Aug 18, 2026
e042644
fix(weekly-digest): exclude synthetic contracts; renumber migration t…
DiyanaDimitrova Aug 19, 2026
88767e5
Merge remote-tracking branch 'origin/feat/weekly-digest' into feat/we…
DiyanaDimitrova Aug 19, 2026
0aa75ef
fix(weekly-digest): renumber weekly_digests migration to 0013 after b…
DiyanaDimitrova Aug 19, 2026
726237e
test(report): add coverage config + baseline entry to clear the ratchet
DiyanaDimitrova Aug 19, 2026
82ad5c3
docs: remove the 167a/167b weekly-digest ticket files
DiyanaDimitrova Aug 19, 2026
fbdf01a
docs: drop the removed ticket links from the docs index
DiyanaDimitrova Aug 19, 2026
335eb84
docs: rename 167-weekly-digest.md to weekly-digest.md
DiyanaDimitrova Aug 19, 2026
d16dfa3
ci: drop the fork-only ephemeral PR preview workflows
DiyanaDimitrova Aug 19, 2026
1aa6cbd
fix(etl): restore fail-dark committed posture for the digest trigger
DiyanaDimitrova Aug 20, 2026
68a18a1
fix(deploy,etl): forward Turnstile site key; move PROMPTS_CRON off th…
DiyanaDimitrova Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
27 changes: 27 additions & 0 deletions .dev.vars.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,30 @@

# Web worker privacy-preserving request log IP HMAC key.
LOG_IP_KEY=dev-only-log-ip-key-change-me

# ── AI assistant (docs/spec/ai-assistant.md) ───────────────────────────────────────────────────────
# Provider API key (OpenRouter today) — SECRET. Without it /assistant/chat returns a controlled 503
# (endpoint unprovisioned). In the cloud set via `wrangler secret put ASSISTANT_API_KEY --name <worker>`,
# never committed.
# ASSISTANT_API_KEY=
#
# All model traffic MUST transit the Cloudflare AI Gateway (fail closed): without AI_GATEWAY_BASE_URL the
# assistant returns 503 rather than calling the provider directly. Set both to your gateway (account
# f6308e22233e69cba80ed57bdb6d5f44); AI_GATEWAY_ID is the gateway slug used to route Workers AI embeddings.
# ASSISTANT_MODEL is the provider-scoped model id — swap models by editing it alone.
# AI_GATEWAY_BASE_URL=https://gateway.ai.cloudflare.com/v1/f6308e22233e69cba80ed57bdb6d5f44/<gateway>/openrouter/v1
# AI_GATEWAY_ID=<gateway>
# ASSISTANT_MODEL=google/gemma-4-31b-it # append ':free' for the rate-limited free tier
#
# NOTE: Workers AI (`AI`) and Vectorize (`VECTORIZE`) have NO local miniflare emulation, and
# vite.config.ts configures no remote bindings — so under a plain `pnpm dev` the assistant route
# degrades (RAG grounding/semantic_search no-op, then 503 without a key). To exercise the assistant
# locally, run with remote bindings (`wrangler dev --remote`) against the dev account's resources.
#
# Optional: enables the one-shot schema-corpus seed route POST /assistant/reindex (off when unset).
# High-entropy value; sent as `Authorization: Bearer <token>`. See app/lib/assistant/README.md.
# ASSISTANT_SEED_TOKEN=
#
# Transcript signing key (HMAC-SHA-256 over server-emitted messages).
# Prod: `wrangler secret put ASSISTANT_HMAC_KEY`. Never commit the real value.
ASSISTANT_HMAC_KEY=dev-only-assistant-hmac-key-change-me
22 changes: 21 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ jobs:
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22
# Pinned to an exact minor: node:sqlite bundles SQLite, whose version (and thus the EXPLAIN
# opcode universe the assistant opcode-guard test pins) changes with the Node minor. 22.23.0
# ships SQLite 3.51.3, which READ_ONLY_OPCODES/KNOWN_SQLITE_VERSIONS cover. Bump deliberately
# and re-harvest the allowlist (see sql-opcode-guard.test.ts) rather than letting 22.x float.
node-version: 22.23.0
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Dependency audit
Expand Down Expand Up @@ -87,9 +91,19 @@ jobs:
- name: Typecheck
if: ${{ !cancelled() }}
run: pnpm typecheck
# `pnpm test` also runs the assistant adversarial suites — the two-layer SQL guard
# (sql-guard-adversarial), the EXPLAIN-opcode allowlist (sql-opcode-guard), and the
# prompt-injection boundary tests (system-prompt / tool-results / chat-input) — so a regression in
# any read-only / injection defence blocks merge (launch gate §9.9, issue #83).
- name: Test
if: ${{ !cancelled() }}
run: pnpm test -- --coverage
# Launch gate §9.9 (issue #83): replays the golden report fixtures through the real bind/reconcile
# pipeline and asserts A–E2 (canonical amount_eur, default filters, rollup reconciliation, no prose
# figures). Blocking, so a model / schema / prompt change can't silently regress a published report.
- name: Golden reports
if: ${{ !cancelled() }}
run: pnpm test:golden
# Coverage ratchet (#93): per-workspace lines/branches may not drop below
# coverage-baseline.json (0.5pp tolerance). The checker self-tests first,
# so the gate is itself gated — same pattern as the docs check below.
Expand All @@ -115,6 +129,12 @@ jobs:
run: |
pnpm check:docs:test
pnpm check:docs
# Root-level tooling (scripts/*.mjs) lives outside the turbo/vitest workspaces, so `pnpm test` never
# ran its node:test suites. Gate them here — the CI-provisioning + teardown tools (ensure-kv-namespace,
# ensure-voice-provider, teardown-remote, reap-previews, load-eop) are deploy-critical and must stay covered.
- name: Script tests
if: ${{ !cancelled() }}
run: node --test scripts/*.test.mjs

# Sticky PR comment with the coverage table (#93). Same-repo PRs only: fork
# PRs get a read-only token no matter what `permissions:` says, so for them
Expand Down
97 changes: 87 additions & 10 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ name: Deploy (web + etl → Cloudflare)
# Event → target mapping (see the `detect` job):
# - push to `main` → staging (continuous staging on every merge to main, so staging leads prod)
# - version TAG (v*) → production (cut a release: git tag v1.0.1 && git push origin v1.0.1)
# - workflow_dispatch → the chosen environment
# midt-bg/sigma is the single repo carrying both the `staging` and `production` Environments (the former
# - workflow_dispatch → the chosen environment (incl. `dev`)
# midt-bg/sigma is the single repo carrying the `dev`, `staging` and `production` Environments (the former
# midt-bg/sigma-stage mirror was consolidated into it). Production is cut deliberately as a tag; staging
# tracks `main`. (To deploy on GitHub *Releases* instead of tags, swap the tag trigger for
# `release: { types: [published] }`.)
#
# `dev` is a long-lived, manual-only target: deploy ANY branch to it on demand from the Actions tab or
# gh workflow run deploy.yml --ref <branch> -f environment=dev
# It reuses the same render/guard machinery — it just needs a `dev` GitHub Environment provisioned with
# its own SIGMA_* names + a dev D1 id. Ephemeral per-PR previews are a separate workflow (preview.yml).
# Setup runbook: docs/dev-environments.md.

on:
push:
Expand All @@ -24,7 +30,14 @@ on:
description: Deploy target
type: choice
options:
- dev
- staging
# Once provision-environments.sh has been applied, selecting `production` here only
# works when the workflow is dispatched from a `v*` tag ref, e.g.:
# gh workflow run deploy.yml --ref v1.0.1 -f environment=production
# Dispatching from a branch ref (e.g. main) is refused by the environment tag policy.
# Admins may bypass this via the `admins_can_bypass` setting (Settings → Environments
# → production) for emergency situations.
- production
default: staging

Expand All @@ -44,12 +57,19 @@ jobs:
# workflow_dispatch -> the chosen environment
# tag v* -> production
# push to main -> staging (continuous staging — main always leads prod)
# Pass the GitHub expressions through `env:` and reference the quoted shell vars, rather than
# interpolating `${{ github.ref }}` (attacker-influenceable via a crafted tag/branch name)
# directly into the script body — that would let a ref like `v"; <cmd>; "` break out of the test.
env:
GITHUB_REF: ${{ github.ref }}
EVENT_NAME: ${{ github.event_name }}
INPUT_ENV: ${{ inputs.environment }}
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "env=${{ inputs.environment }}" >> "$GITHUB_OUTPUT"
elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
echo "env=$INPUT_ENV" >> "$GITHUB_OUTPUT"
elif [[ "$GITHUB_REF" == refs/tags/v* ]]; then
echo "env=production" >> "$GITHUB_OUTPUT"
elif [ "${{ github.ref }}" = "refs/heads/main" ]; then
elif [ "$GITHUB_REF" = "refs/heads/main" ]; then
echo "env=staging" >> "$GITHUB_OUTPUT"
else
echo "env=" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -81,6 +101,27 @@ jobs:
SIGMA_CSV_CACHE_NAME: ${{ vars.SIGMA_CSV_CACHE_NAME }}
SIGMA_REPORTS_NAME: ${{ vars.SIGMA_REPORTS_NAME }}
SIGMA_VECTORIZE_NAME: ${{ vars.SIGMA_VECTORIZE_NAME }}
# Per-build dedup freshness `c` — wrangler-render stamps this over the committed "dev" so a code-only
# deploy (report shape / FX / CPV logic) busts the report cache, and each env's build keys stay distinct.
SIGMA_BUILD_ID: ${{ github.sha }}
# Master kill-switch override (#83), per-environment: set the SIGMA_ASSISTANT_ENABLED *variable* to
# "true" on the environments that should serve the assistant (dev now; staging at go-live). Unset →
# wrangler-render leaves the committed fail-dark "false", so production stays dark until deliberately
# flipped. Killing a live assistant is then just clearing this var + redeploy (or the runtime flag).
SIGMA_ASSISTANT_ENABLED: ${{ vars.SIGMA_ASSISTANT_ENABLED }}
# Runtime deploy-env for the §9.3 HMAC gate (ADR-0012). Set the SIGMA_ENVIRONMENT *variable* per
# GitHub Environment: "production" and "staging" are public/unauthenticated → the gate REQUIRES the
# signing key (fail-closed 503 without it); any other value (or unset → committed "development")
# fails open. wrangler-render stamps this into ENVIRONMENT. Not derived from import.meta.env.PROD,
# which Vite inlines true for the staging build too (it would misclassify staging as production).
SIGMA_ENVIRONMENT: ${{ vars.SIGMA_ENVIRONMENT }}
# AI-Gateway account id in AI_GATEWAY_BASE_URL (and web's BGGPT_STT_BASE_URL). wrangler-render swaps
# the committed prod account for this env's own — dev/staging run on a different Cloudflare account
# whose `sigma-assistant` gateway + `custom-bggpt` provider live there. Unset (prod) → committed
# account stays. Without this, a non-prod deploy calls the PROD gateway, which this env's key can't
# use, so every BgGPT call (web assistant + etl digest narrative) fails. preview.yml already passes
# this; deploy.yml was missing it, so the dev etl/web workers kept the prod URL.
SIGMA_AI_GATEWAY_ACCOUNT: ${{ vars.SIGMA_AI_GATEWAY_ACCOUNT }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
Expand Down Expand Up @@ -126,8 +167,34 @@ jobs:
check "$SIGMA_VECTORIZE_NAME" sigma-assistant SIGMA_VECTORIZE_NAME
[ "$fail" = 0 ] || { echo "Refusing to deploy '${{ needs.detect.outputs.env }}' with production resource names."; exit 1; }

# GitOps: enforce this environment's report-dedup KV namespace from git (idempotent create-if-absent)
# and hand its id to wrangler-render via SIGMA_DEDUP_KV_ID. One namespace per real env — dev (shared
# with the ephemeral previews), staging, prod — kept distinct by title so no env can touch another's
# cache. `production` maps to the short `prod` suffix; dev/staging use the env name verbatim.
- name: Ensure DEDUP_KV namespace
if: steps.guard.outputs.ok == 'true' && success()
run: |
case "${{ needs.detect.outputs.env }}" in
production) title=sigma-dedup-prod ;;
*) title="sigma-dedup-${{ needs.detect.outputs.env }}" ;;
esac
id="$(node scripts/ensure-kv-namespace.mjs "$title")"
echo "::add-mask::$id"
echo "SIGMA_DEDUP_KV_ID=$id" >> "$GITHUB_ENV"

# GitOps: ensure the account-scoped `bggpt-voice` custom provider (+ the shared `sigma-assistant`
# gateway) the VOICE lane calls directly. Provider-only — the assistant hits the gateway's provider
# endpoints for transcription, NOT a dynamic route (dynamic routing can't carry audio; see ADR-0011).
# Idempotent: a no-op when the provider already exists. VOICE_ASSISTANT_API_KEY is used only to
# first-create the provider; absent ⇒ the existing provider is reused as-is.
- name: Ensure voice AI-Gateway provider
if: steps.guard.outputs.ok == 'true' && success()
env:
VOICE_ASSISTANT_API_KEY: ${{ secrets.VOICE_ASSISTANT_API_KEY }}
run: node scripts/ensure-voice-provider.mjs --apply

- name: Typecheck
if: steps.guard.outputs.ok == 'true'
if: steps.guard.outputs.ok == 'true' && success()
run: pnpm typecheck

# Schema before serving: apply the свързани-лица schema to the target env BEFORE the Worker deploys,
Expand Down Expand Up @@ -308,11 +375,11 @@ jobs:

# `run deploy`, not `deploy` — bare `pnpm deploy` is a pnpm built-in, not our package script.
- name: Deploy explorer (sigma)
if: steps.guard.outputs.ok == 'true'
if: steps.guard.outputs.ok == 'true' && success()
run: pnpm --filter @sigma/web run deploy

- name: Initialize LOG_IP_KEY secret if absent
if: steps.guard.outputs.ok == 'true'
if: steps.guard.outputs.ok == 'true' && success()
run: |
# LOG_IP_KEY is long-lived so IP HMAC tokens remain stable for same-client correlation.
# Never overwrite it; a new blue-green worker name starts without a key and correlation
Expand Down Expand Up @@ -356,6 +423,16 @@ jobs:
echo "::add-mask::$key"
printf '%s' "$key" | pnpm --filter @sigma/web exec wrangler secret put LOG_IP_KEY --name "$SIGMA_WEB_NAME"

# The §9.3 transcript-signing key (ADR-0011/0012). Same generate-if-absent story as LOG_IP_KEY above:
# a purely internal HMAC key with no human value, generated once per worker and left stable across
# redeploys (rotating it would invalidate every in-flight client transcript at once). On production +
# staging the runtime gate fails CLOSED without it, so this step must run on every deploy to keep the
# required key present. The key is generated in-process and streamed to wrangler over stdin — it never
# appears in a log line, so no ::add-mask:: is needed. Runs after the deploy: the worker must exist first.
- name: Initialize ASSISTANT_HMAC_KEY secret if absent
if: steps.guard.outputs.ok == 'true' && success()
run: node scripts/ensure-worker-secret.mjs ASSISTANT_HMAC_KEY

- name: Deploy refresh Workflow (sigma-etl)
if: steps.guard.outputs.ok == 'true'
if: steps.guard.outputs.ok == 'true' && success()
run: pnpm --filter @sigma/etl run deploy
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,7 @@ worker-configuration.d.ts
# Local-only policy/regulatory source materials copied in for review (never commit)
.policy-source/

# Playwright MCP session artifacts (screenshots / a11y snapshots)
.playwright-mcp/
# Phase-0 „свързани лица" spike scratch — scraped declaration PII, local DB, derived JSON (never commit)
scratch/
8 changes: 7 additions & 1 deletion apps/etl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@sigma/ingest": "workspace:*"
"@ai-sdk/openai": "^3.0.73",
"@sigma/config": "workspace:*",
"@sigma/db": "workspace:*",
"@sigma/ingest": "workspace:*",
"@sigma/report": "workspace:*",
"@sigma/shared": "workspace:*",
"ai": "6.0.208"
}
}
27 changes: 27 additions & 0 deletions apps/etl/src/cron-guard.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/// <reference types="node" />
import { readFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { DIGEST_CRON, PROMPTS_CRON, REFRESH_CRON } from './crons';

// Routing safety: scheduled() branches on controller.cron against the named constants. A typo in
// wrangler.toml's `crons` (or in the constants) would silently misroute a trigger, so this parses the
// committed `crons` array and asserts it equals exactly [REFRESH_CRON, PROMPTS_CRON, DIGEST_CRON] — a
// mismatch fails CI instead of misfiring in production.

const wranglerPath = resolve(dirname(fileURLToPath(import.meta.url)), '../wrangler.toml');

function parseCrons(toml: string): string[] {
const match = toml.match(/crons\s*=\s*\[([^\]]*)\]/);
const inner = match?.[1];
if (inner === undefined) throw new Error('no `crons = [...]` array found in wrangler.toml');
return [...inner.matchAll(/"([^"]*)"/g)].map((m) => m[1] ?? '');
}

describe('cron routing guard', () => {
it('wrangler crons equal [REFRESH_CRON, PROMPTS_CRON, DIGEST_CRON] in order', () => {
const crons = parseCrons(readFileSync(wranglerPath, 'utf8'));
expect(crons).toStrictEqual([REFRESH_CRON, PROMPTS_CRON, DIGEST_CRON]);
});
});
8 changes: 8 additions & 0 deletions apps/etl/src/crons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Cron strings shared by wrangler.toml's `crons`, scheduled()'s routing branch (index.ts), and the
// cron-guard test. Kept in a dependency-free module (no `cloudflare:workers` / `.sql` text imports) so
// the guard test can import them under plain vitest without pulling in the Workflow runtime.
export const REFRESH_CRON = '0 */6 * * *';
export const PROMPTS_CRON = '0 6 * * 1';
// Weekly Digest producer (#167A T3) — Monday 07:00 UTC, an hour after PROMPTS_CRON, so the digest's
// weekly queries run against the same freshly-refreshed slice the starter prompts just rebuilt from.
export const DIGEST_CRON = '0 7 * * 1';
Loading