This file preserves project context for future humans and AI coding agents. Treat it as the durable replacement for chat history.
OpenSIST has many programs whose descriptions are empty or incomplete. Several public CS application projects have useful program-specific descriptions, notes, and DP-style content:
- OpenCS:
https://github.com/opencsapp/opencsapp.github.io - GlobalCS:
https://github.com/Global-CS-application/global-cs-application.github.io - CSGrad:
https://github.com/csms-apply/csgrad
The goal is to monitor those upstream sources, detect new or changed program descriptions, match them to OpenSIST programs, and make changed candidates available for a later merge/review process.
This project is intentionally independent from OpenSIST.github.io.
Reasons:
- It pulls data from multiple places: OpenSIST backend, GitHub repos, and later possibly web pages.
- It owns crawler/monitoring state and automation secrets.
- It should output events and later drafts without coupling to frontend deploys.
- It must not pollute OpenSIST backend data during MVP work.
Current local path:
/Users/caleblee/Desktop/program-intro-syncOriginal OpenSIST frontend/backend-adjacent repo used for earlier exploration:
/Users/caleblee/Desktop/OpenSIST.github.ioThe current MVP implements the data monitor only.
It does:
- Fetch OpenSIST program list and current descriptions.
- Fetch external Markdown documents from configured GitHub repos.
- Normalize Markdown and compute content hashes.
- Match source documents to OpenSIST
ProgramIDwith deterministic rules. - Store source state, OpenSIST program snapshots, match results, monitor runs, and change events in D1.
- Expose admin endpoints to run the monitor and inspect/mark events.
It does not:
- Call DeepSeek, Qwen, Workers AI, or any other LLM.
- Generate merged descriptions.
- Write to the OpenSIST backend.
- Create GitHub PRs.
- Store full normalized Markdown in D1 by default.
- Scrape arbitrary websites.
Use Cloudflare Workers + D1 for the monitor.
Use D1 as the only durable store for the monitor. Do not introduce JSON state files, R2, GitHub artifacts, or committed generated output for MVP state.
Use Workers Cron for scheduled runs and an authenticated HTTP endpoint for manual runs.
Use Containers only if future requirements need tools Workers cannot run efficiently, such as full git, browser scraping, or heavyweight parsing.
Assume future merge work can use AI Gateway + DeepSeek.
Important boundary: AI Gateway + DeepSeek belongs in a downstream merge service, not in the monitor core. The monitor should create D1 events that a later merge worker consumes.
Recommended future shape:
Monitor Worker
-> description_change_events
Merge Worker
-> read pending high-confidence events
-> fetch current source/OpenSIST context
-> call DeepSeek through AI Gateway
-> write merge_drafts
Review/Publisher
-> human review
-> backend publish APIProgram descriptions live in the OpenSIST backend database. A PR to the frontend repository is not the right primary publishing mechanism.
The intended production path is:
description_change_events
-> merge_drafts
-> human review
-> backend admin publish APIThe monitor remains read-only. Backend publishing should be added only after a draft table and review step exist. See docs/backend-publish-api.md.
A local-only summary evaluation workflow has been added.
Files:
prompts/llm-summary-system.mdscripts/evaluate-llm-summary/index.mjsdocs/llm-summary-evaluation.md
The script reads high-confidence source documents from remote D1 and writes local ignored results under outputs/llm-summary-evals/. Dry-run mode has been verified.
Real AI calls require CLOUDFLARE_API_TOKEN or CF_API_TOKEN in the local environment.
Current baseline model for evaluation:
@cf/meta/llama-3.3-70b-instruct-fp8-fastObserved result:
- AI Gateway / Workers AI path works.
- Qwen3 and GLM reasoning models returned visible reasoning and consumed output tokens before producing final JSON.
- Llama 3.3 70B fast produced strict JSON more reliably for this summary task.
- Latest Harvard CSE local script sample wrote
outputs/llm-summary-evals/2026-06-16T08-46-11-669Z.json. - That sample used
prompt_tokens=1125,completion_tokens=305,total_tokens=1430. - Validation passed with no warnings.
- A two-sample OpenCS batch hit a Cloudflare AI 408 timeout, so the script now records per-sample AI failures instead of aborting the whole run.
Important finding:
GlobalCS:docs/Program/T0.5/EPFL MSDH.mdwas matched toMSCS@EPFLwith high confidence.- This is a matcher false positive because MSDH is Digital Humanities, not MSCS.
- The LLM summary set
shouldUseForDraft=false, which is useful as a safety signal, but matcher logic must be fixed before merge drafts. - Next matcher work should add explicit program-code/name disagreement penalties and a fixture where
EPFL MSDHmust not matchMSCS@EPFL.
Prior research found:
- Workers AI has a free allocation of 10,000 Neurons/day.
- Workers Paid charges usage above the free allocation at a published Neurons rate.
- Cloudflare Workers AI pricing listed
@cf/qwen/qwen3-30b-a3b-fp8as much cheaper than Cloudflare-hosted@cf/deepseek-ai/deepseek-r1-distill-qwen-32b. - Official DeepSeek API pricing can be cheaper than Cloudflare-hosted DeepSeek-distill for output-heavy workloads.
- AI Gateway is not a model; it is the control/observability layer for provider calls.
Keep model names and cost assumptions configurable because provider pricing changes.
Core files:
wrangler.jsonc: Worker, cron, D1 binding, and config vars.migrations/0001_initial.sql: D1 schema.src/index.ts: Worker entry, scheduled handler, admin HTTP routes.src/pipeline/monitor.ts: end-to-end monitor run.src/opensist/client.ts: read-only OpenSIST API client.src/sources/github.ts: GitHub REST tree scanner and raw Markdown fetcher.src/matching/matcher.ts: deterministic source-to-program matcher.src/db/repository.ts: D1 access layer.src/config/sources.ts: configured upstream repos.
Verification already performed:
npm installnpm run typechecknpm run db:migrate:localnpm run db:migrate:remotenpm audit --omit=dev
Notes from verification:
npm installreported dev dependency audit findings, butnpm audit --omit=devreported zero production vulnerabilities.- Local
wrangler devreachedReadyafter settingcompatibility_dateto2026-05-03. - The environment used during implementation could not curl the local Wrangler port from another shell, likely due to proxy/sandbox isolation, so
/healthwas not fully verified through HTTP. - Cloudflare account is configured in
wrangler.jsoncasc1e0d935e0f8ba4685b9b6702130efe7. - Remote D1 database
program-intro-syncwas created in regionWNAM. - Remote D1 database id is
3073eab7-e1f8-4e1e-b171-33740db9ad20. - Remote migration
0001_initial.sqlwas applied successfully. - Remote
sqlite_masterquery confirmed these monitor tables exist:sources,source_documents,opensist_program_snapshots,program_matches,description_change_events, andmonitor_runs. - First remote manual monitor run proved
OPENSIST_COOKIEcould fetch OpenSIST data and wrote 327 program snapshots, but failed before source scanning due to Cloudflare Workers subrequest limits. - The monitor was then updated to write only changed/new OpenSIST program snapshots, cap program snapshot writes with
MAX_PROGRAM_UPSERTS_PER_RUN=25, and cap raw GitHub Markdown downloads withMAX_RAW_DOWNLOADS_PER_SOURCE=1, so first source ingestion can progress across multiple runs. - After that fix was deployed, manual monitor run
03e50966-b0ef-45ba-a859-6e47c26029e4succeeded withsourcesScanned=3,documentsSeen=26, andeventsCreated=2. - Remote D1 counts after the successful run:
sources=3,source_documents=26,opensist_program_snapshots=327,description_change_events=26.
Expected Cloudflare secrets:
OPENSIST_COOKIE: optional cookie for read-only OpenSIST backend access.GITHUB_TOKEN: optional GitHub token to avoid low unauthenticated rate limits.ADMIN_TOKEN: optional bearer token for admin endpoints.
Do not commit real cookies, JWTs, provider API keys, raw request headers, or full private backend responses.
Done:
- Wrangler login was completed on the local machine.
wrangler.jsoncincludes the selected Cloudflareaccount_id.- Remote D1 database
program-intro-syncexists. - Remote D1 migration
0001_initial.sqlhas been applied. - Remote monitor tables were confirmed through
sqlite_master.
Additional status:
ADMIN_TOKENwas generated locally, saved in ignored file.admin-token, and uploaded as a Worker secret.OPENSIST_COOKIEwas uploaded as a Worker secret for the MVP.- Worker was deployed and
/healthplus admin auth were verified. GITHUB_TOKENremains optional; set it only if unauthenticated GitHub API limits become a problem.- Run manual monitor passes until first source ingestion catches up.
The monitor records facts as idempotent events in description_change_events.
Event types:
source_newsource_changedsource_removedmatch_changedmatch_confidence_changedopensist_program_changedopensist_description_changedlicense_changed
Downstream services should query pending events and mark them consumed, ignored, or acknowledged.
- Matcher is deterministic and conservative; it needs more aliases and evaluation data.
- Current matcher can over-score same-university but different-program sources, e.g.
EPFL MSDH->MSCS@EPFL. - GitHub tree fetch currently fails if GitHub returns a truncated recursive tree.
- Source license labels for GlobalCS and CSGrad still need confirmation.
- The monitor does not store full source Markdown, so a future merge service may need to re-fetch source content by
source_url/source_commit. - Source-level partial failure is not implemented; one source failure currently fails the run.
- No automated test suite exists yet beyond TypeScript checking and local D1 migration validation.
- Add tests for
matcher.ts, markdown normalization, and event idempotency. - Add source-level failure tracking if partial success is important.
- Confirm GlobalCS and CSGrad license policy before using content beyond internal review.
- Add
merge_draftsschema and a separate DeepSeek/AI Gateway consumer. - Add a small evaluation fixture set for known source documents and expected
ProgramIDmatches.