尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗India-focused biotech & pharmaceutical catalyst intelligence — a web PWA that connects verified-style facts, concise summaries, catalyst events, source links, listed companies/tickers, and transparent post-event market reaction.
Milestones 0–3 done. Companies, drugs, trials, seed events and market figures are seed fixtures — realistic placeholders, not real announcements or real market data. In addition, Milestone 3 adds fixture-ingested events from a mock CTRI adapter (
ctri-fixture): these come from five hand-curated fictional CTRI-shaped JSON records (no network), each with full provenance, and are clearly badged in the code/docs as fixture data. Every screen badges fixtures as such.
Full product/engineering source of truth: the build spec
(~/workspace/user/files/muse_build_spec.pdf in the author's workspace).
Prerequisites: Node.js ≥ 20, pnpm 10 (npm install -g pnpm@10).
cd biocatalyst-india
pnpm install # 1. install all workspace dependencies
pnpm db:setup # 2. create local Postgres role/DBs, migrate, load seed fixtures
pnpm build # 3. typecheck + build all packages
pnpm test # 4. run unit/integration tests (incl. DB-backed tests)
pnpm dev # 5. start the web app → http://localhost:3000
That's the whole local setup: no API keys, no paid services — just a local Postgres (see "Database" below).
The app itself sends no analytics or tracking. The only
telemetry.vercel.com calls come from the Next.js CLI's built-in anonymous
usage telemetry (and Turborepo's equivalent) — neither is required for the
app to function, and both are disabled project-wide via
NEXT_TELEMETRY_DISABLED=1 and TURBO_TELEMETRY_DISABLED=1 in
.env.example (copy to .env / export in your shell to apply). The local
Next.js telemetry config is also opted out (next telemetry disable).
Milestone 2 uses a local PostgreSQL for development and tests (production path: Supabase free tier — not wired up yet).
pnpm db:setup # create role + databases, run migrations, load seed fixtures
pnpm db:migrate # apply pending migrations only
pnpm db:seed # (re)load seed fixtures into the dev database
pnpm db:reset # drop, recreate, migrate, reseed — clean slate
DATABASE_URL defaults to
postgresql://biocatalyst:biocatalyst@localhost:5432/biocatalyst_dev
(see .env.example); tests use .../biocatalyst_test, and the
DB-backed suites each get an isolated database —
biocatalyst_ingestion_test, biocatalyst_extraction_test,
biocatalyst_market_test, biocatalyst_auth_test,
biocatalyst_notifications_test, biocatalyst_web_test — so parallel
test runs can't interfere (see docs/decisions.md).
On macOS, install/start PostgreSQL with Homebrew (brew install postgresql@16
and brew services start postgresql@16); pnpm db:setup then uses the local
admin user. On Linux it uses sudo -u postgres for the one-time role/database
creation.
Three compliant source adapters run end-to-end behind the
SourceAdapter interface, all fixture-based mocks with zero network
requests (spec §7 explicitly permits fixture-based mocks where automated
access is blocked or terms are unclear; rationale in
docs/decisions.md):
CtriFixtureAdapter (sourceName: "ctri-fixture") — five fictional,
hand-curated CTRI-shaped JSON records (M3).BseFixtureAdapter (sourceName: "bse-fixture") — five fictional
BSE corporate-announcement records: regulatory approval, licensing,
acquisition, a trial-result, and a regulatory submission (M7).PubmedFixtureAdapter (sourceName: "pubmed-fixture") — three
fictional PubMed abstracts, all scientific_publication (M7).pnpm ingest # all registered adapters → JSON array of per-source metrics
pnpm ingest --source bse-fixture # one adapter only
cross_source_key (first writer
wins, no duplicate alert; migration 007_cross_source_key.sql).sources row (publisher, URL, content hash),
event_sources link, raw_snapshots archive; run metrics in
ingestion_runs, resume cursor in ingestion_cursors (migrations
003_ingestion.sql, 007_cross_source_key.sql).Authorization: Bearer <ADMIN_TOKEN>
outside development — see docs/runbook.md):
GET /api/v1/admin/health (DB status + per-source health metrics) and
POST /api/v1/admin/ingestion/run (trigger a pass for all sources, or
?source=<name> for one; returns per-source metrics).Every ingested document is extracted into the spec §8.2 structured schema
via the ExtractionProvider interface (packages/extraction) — fully
offline, no model API keys, no network:
MODEL_PROVIDER=deterministic pnpm ingest # default: rule/template extractor
MODEL_PROVIDER=mock pnpm ingest # fixture "model" (canned outputs)
§8.2, Zod-validated): closed-taxonomy event_type,
company/drug/trial/indication names, ticker_candidates, phase,
outcome (primary_endpoint_met | primary_endpoint_missed | unknown),
facts[] ({claim, source_span, confidence}), published_at,
confidence. Invalid payloads are rejected.unknown + lowered confidence; every fact
carries a verbatim source_span; endpoint claims below 0.85 confidence
→ pending_review, never auto-published.extractions table keyed on
(document_hash, provider, prompt_version, schema_version) — same
document + same versions is never re-extracted (migration
004_extraction.sql).pending_review — the pipeline never crashes.factual_summary (80–150 words) and catalyst_summary
(neutral operational relevance, no investment language — spec §6) are
generated from structured facts via deterministic templates.GET /api/v1/admin/health now embeds reviewQueue
(pending count + lowest-confidence items with extraction provenance);
GET /api/v1/admin/review-queue lists it directly. No review UI yet.Why no real model is wired (cost, keys, offline-first): docs/decisions.md.
Post-event price movement is computed per spec §9 behind a replaceable
MarketDataProvider interface (packages/market-data) — fully offline, no
price API keys, no network:
pnpm market:reaction # compute/backfill §9 reactions (idempotent)
MARKET_DATA_PROVIDER=fixture, the default): hand-curated
fixture price series for the seeded NSE tickers plus a synthetic
NIFTY-Pharma-like benchmark, all clearly marked as illustrative fixtures.
Unknown provider names warn and fall back to fixture. A future licensed
provider implements the same interface — no caller changes.price_before = latest close before the event
window; price_after = selected close after; pct_change =
(after/before − 1) × 100; benchmark-relative relative_change = pct_change − benchmark_change. Missing data → NULLs, never fabricated
or interpolated; benchmark fields omitted when benchmark data is
unavailable.window_definition, window_start,
window_end) and displayed verbatim, e.g. "NSE close on 2026-09-28 → NSE
close on 2026-09-29 (announcement 2026-09-29 08:15 IST — before market
open)"; date-only events get a clearly labeled daily window.market_reactions row carries provider name +
license/usage metadata; bars measured from are persisted to
market_prices. UI copy is non-causal ("market reaction around the
event", never "caused by").Why no real price API is wired (keys, licenses, offline-first):
docs/decisions.md.
Managed-style authentication without any third-party provider: a pluggable
AuthProvider interface (packages/auth) whose default is a local,
self-hostable implementation — scrypt password hashing (Node built-in),
random session tokens stored as SHA-256 hashes in Postgres, httpOnly +
SameSite=Lax cookies, 30-day expiry. Sign up/in at /signup, /signin;
the header shows a notification bell with the unread-alert count.
GET/POST /api/v1/watchlist and DELETE /api/v1/watchlist/{entity_type}/{entity_id} — auth required; the M1
localStorage mock is gone. Follow/unfollow buttons live on company and
drug pages; /watchlist shows followed entities.(event_id, user_id, channel), so
re-ingests never duplicate. Unfollowing instantly stops new alerts for
that entity. /alerts is the notification center (per-alert and mark-all
read). Saved monitors can also match keywords, event types, countries,
indications and phases; eligible alerts are queued for push delivery./api/v1/auth/* rate-limited to 20 req/min per IP (vs 120
elsewhere), Zod-validated inputs, no password/token logging, passwords
never in API responses. Anonymous browsing (feed, entity pages, search)
keeps working — auth gates only watchlists and alerts.Why no third-party auth provider (local-first privacy): docs/decisions.md;
session/env notes: docs/runbook.md.
apps/web (Next.js 15 + TypeScript PWA),
packages/types (canonical types mirroring spec §5),
packages/data (async data-access layer over Postgres),
packages/db (Postgres client, SQL migrations, no ORM),
packages/auth (pluggable auth provider interface + local self-hostable
implementation), packages/notifications (in-app alert generation +
read state, monitor matching and durable Expo push outbox).packages/db/migrations), idempotent seed from the M1
fixtures (pnpm db:seed), Zod validation on all API inputs, and
per-IP rate limiting (120 req/min) on /api/v1/*./alerts, /signin
and /signup pages, and a header bell with the unread-alert count.GET /api/v1/events (cursor pagination +
filters), /api/v1/events/{id}, /api/v1/companies/{slug},
/api/v1/companies/{slug}/events, /api/v1/drugs/{slug},
/api/v1/trials/{registry}/{trial_id}, /api/v1/search?q=,
GET /api/healthz, plus the M6 auth/watchlist/alerts surface:
POST /api/v1/auth/signup, POST /api/v1/auth/signin,
POST /api/v1/auth/signout, GET /api/v1/auth/session,
GET/POST /api/v1/watchlist, DELETE /api/v1/watchlist/{entity_type}/{entity_id}, GET /api/v1/alerts,
POST /api/v1/alerts/{id}/read, POST /api/v1/alerts/read-all.AuthProvider interface (no third-party providers, no OAuth —
rationale in ). Passwords hashed with scrypt (Node
built-in), random session tokens stored as SHA-256 hashes in Postgres
(migration ), httpOnly + SameSite=Lax cookies, 30-day
expiry (). Stricter rate limiting on
(20 req/min per IP vs 120 elsewhere). Anonymous browsing
of the feed, entity pages and search keeps working — auth gates only
watchlists and alerts.| Path | Purpose |
|---|---|
apps/web/ | Next.js PWA |
packages/types/ | Canonical TypeScript types |
packages/data/ | Async data-access functions (DB-backed) + seed fixtures |
packages/db/ | Postgres client + SQL migrations |
packages/ingestion/ | Source adapters, ingestion pipeline, pnpm ingest CLI |
packages/auth/ | AuthProvider interface + local self-hostable auth (scrypt, DB sessions) |
packages/notifications/ | In-app alert generation (dedupe) + read state |
packages/extraction/ | AI extraction: provider interface, §8.2 schema, guardrails, caching, fallback, summaries |
packages/market-data/ | Market-data provider interface, §9 calculation, pnpm market:reaction CLI |
packages/api-client/ | Typed /api/v1/* HTTP client (zod-validated) for the mobile app |
apps/mobile/ | Expo React Native mobile app (Milestone 9) |
docs/ | decisions.md · architecture.md · data-sources.md · runbook.md · deployment.md |
scripts/ | generate-icons.mjs — dependency-free PWA icon generator |
Everything in this section was verified locally — no accounts created,
no deploys executed, no third-party services configured (see
docs/deployment.md for the options + what you'd need to provide).
/privacy, /terms, /disclaimer (footer-linked),
covering informational-only use (not medical / not investment advice),
data sources & fixture status, retention, and contact.POST /api/v1/issues (anonymous, Zod-validated, stored
in-app — migration 008_issue_reports.sql). Admins triage reports at
/admin/issues (token-gated via the existing isAdminRequest gate).display: standalone), service worker with an /offline fallback page.next.config.mjs): X-Content-Type-Options,
Referrer-Policy, X-Frame-Options, same-origin-only CSP. The app loads
zero third-party resources (no analytics — asserted by a build-scan test).bash scripts/db-backup.sh / bash scripts/db-restore.sh (see "Backup & restore" in docs/runbook.md)./api/v1/admin/health + log file; ingestion_runs is the source of
truth. Sentry/analytics deliberately not integrated.Production smoke check (after pnpm build):
DATABASE_URL=<redacted> pnpm --filter @biocatalyst/web start
then verify /, an event page, /search, /privacy, /admin/issues.
The Android/iOS/web bundles are verified locally. Store push delivery still requires the production EAS project id and platform credentials; no telemetry SDK is included.
@biocatalyst/api-client — typed fetch client over the
unchanged /api/v1/* routes (zod v4 validation with compile-time drift
guards against @biocatalyst/types); @biocatalyst/mobile
(apps/mobile) — Expo SDK 54 app (managed workflow).bc_session token
from the Set-Cookie header after sign-in and re-attaches it as a
Cookie header (SecureStore-backed token storage). No token in any
response body — web session security unchanged.expo-notifications requests permission,
registers an Expo token, and POSTs it to /api/v1/push-tokens. Published
matching alerts enter a retryable outbox; the scheduler sends pending
messages through Expo Push Service. Production still needs an EAS project
id plus FCM/APNs credentials — see docs/deployment.md.pnpm --filter @biocatalyst/web dev plus
EXPO_PUBLIC_API_URL=http://<lan-ip>:3000 pnpm --filter @biocatalyst/mobile dev, scan the QR with Expo Go. expo export
verified locally (web + iOS + Android bundles in
apps/mobile/dist/). Full docs: apps/mobile/README.md.Everything in this section was prepared locally — no store
submissions, no developer accounts, no third-party crash reporting, no
telemetry (EXPO_TELEMETRY_DISABLED=1 on all commands), no secrets
committed. The app is store-ready; submission itself is a user action
(see docs/deployment.md for the consolidated user-provided checklist).
apps/mobile/assets/, generated locally by
node scripts/generate-mobile-icons.mjs — zero-dependency pure-Node PNG
writer, no external image services): icon.png 1024×1024 (App Store
requirement), adaptive-icon.png 1024×1024 foreground (mark inside the
66% safe zone; navy background via config), splash.png 1024×1024,
favicon.png 48×48. Wired in app.json (icon, splash,
ios.icon, android.adaptiveIcon).docs/store/permissions.md): notifications are the
only runtime permission (exact in-app rationale copy documented); no
camera/location/mic/contacts/calendar. Android manifest permissions
(POST_NOTIFICATIONS, RECEIVE_BOOT_COMPLETED) come from the
expo-notifications library merge — app.json deliberately does not
pin android.permissions.docs/store/privacy-labels.md): Apple App Privacy
and Google Play Data safety answers grounded in actual behavior (account
email + push token used with Expo/platform push services; no analytics, no
tracking, no third-party SDKs, no data sold). Items needing live
verification are flagged "verify before submission".docs/store/screenshots.md): shot list for iPhone
6.7"/6.5", iPad 13", Android phone + 7"/10" tablets — executable by the
user on simulators/devices (no toolchains here).docs/store/build.md): expo prebuild flow,
version/versionCode/buildNumber strategy, Android keytool keystore
commands (throwaway examples only), iOS Xcode signing, and exactly what
the user must provide.docs/store/crash-reporting.md): decision recorded
— no Sentry/Crashlytics per the no-third-party rule; what is lost; a
deferred self-hosted error-log endpoint design.docs/store/listing.md): name, tagline, description,
keywords, categories, age-rating guidance, URL/contact placeholders,
1.0.0 release-notes template./privacy): now discloses device push-token storage
and retention. Operator contact address:
(set 2026-10-01).Informational product only: no medical advice, no investment advice, no buy/sell/hold calls or price targets. Catalyst summaries explain operational relevance; market reaction is shown as "around the event", never as caused by it.
Milestone 2 done: canonical Postgres schema + typed API over the same
contracts, seed migration, DB-backed pagination/search tests, Zod
validation, rate limiting. Milestone 3 done: first compliant source
adapter (fixtures, dedupe, provenance, health metrics, admin endpoints) —
see CHANGELOG.md. Milestone 5 done: market-data adapter + §9
market-reaction calculations (fixture provider, benchmark support,
idempotent pnpm market:reaction backfill). Milestone 6 done: local auth,
server-backed watchlists, in-app alerts with strict dedupe. Milestone 7
done: two more fixture-based source adapters (BSE India announcements +
PubMed abstracts) with cross-source trial-anchored dedupe, feeding the
same extraction/alerts/market-reaction pipelines — see CHANGELOG.md.
Milestone 8 done: production PWA readiness verified locally — legal pages,
in-app issue reporting with admin triage, security headers, backup/restore
scripts + runbook, deployment-options doc (no accounts created, nothing
deployed). Milestone 9 done: Expo mobile app verified locally — typed
API client, all 12 spec screens, cookie-session auth with zero API
changes, local-only push plumbing (no credentials configured). Milestone 10
done: store-readiness artifacts prepared locally — app icons/splash,
POST /api/v1/push-tokens, permission/privacy-label/screenshot/build/
crash-reporting/listing docs; nothing submitted, no accounts created. The
remaining launch work is all user-provided items — see
docs/deployment.md ("Store-submission checklist").
docs/decisions.md006_auth.sqlAUTH_SESSION_TTL_DAYS/api/v1/auth/*(event_id, user_id, channel) (DB unique constraint, so re-ingests
and parallel runs can't duplicate). Unfollowing instantly stops new
alerts for that entity. Email/push channels come in later milestones.manifest.webmanifest, minimal service worker, generated icons —
installable where supported, responsive down to mobile widths.BseFixtureAdapter (BSE corporate
announcements: approval, licensing, acquisition, trial results,
submission) and PubmedFixtureAdapter (scientific publications). An
adapter registry (packages/ingestion/src/registry.ts) runs all
sources in one pnpm ingest pass (--source <name> for a single
one). Trial-anchored cross-source dedupe merges the same trial's
records from different sources into one event (first writer wins, no
duplicate alert; events.cross_source_key partial unique index,
migration 007_cross_source_key.sql). Counterparties named in BSE
records (licensing partners, acquisition targets) are text-only
mentions, never entities. New-source events flow through the same
extraction → alerts → market-reaction pipelines; the Mankind event
re-proves the missing-price-data path (no row fabricated).app.json wiring, permission audit, docs completeness) + 6 new web
tests for POST /api/v1/push-tokens.