Spaces:
Running
Running
| # Decisions Log | |
| > One entry per architecture/infrastructure/vendor/model decision. | |
| > Format: date - decision - why - rejected alternative - revisit trigger. | |
| > CLAUDE.md hard rule: no new infrastructure without an entry here. | |
| ## 2026-07-09 - Repo seeded from demo; demo stays live | |
| Mainline moves to this repo (future home: Culture-and-Morality-Lab org). The public demo repo | |
| and HF Space remain deployed and untouched - the PI links people to it. Rejected: evolving the | |
| demo repo in place (would couple experiments to a live artifact). Revisit: after Phase 1, repo | |
| transfers to the lab org (design §7 step 7). | |
| ## 2026-07-09 - Postgres jobs table over Redis/RQ (design §6) | |
| Durable queue via SELECT ... FOR UPDATE SKIP LOCKED + leases + sweeper; job state already in | |
| the DB. Rejected: Redis/RQ (a third stateful service before pressure exists). Trigger to | |
| revisit: multiple workers, scheduling needs, sustained queue depth. | |
| ## 2026-07-09 - Managed auth (Supabase Auth recommended), staged Google-first (design §8, §4.1) | |
| Rejected: custom password auth (hashing, reset flows, verification email, lockouts = hidden | |
| security work). Staged: Google sign-in first (zero email infra), email/password second within | |
| the same provider. Trigger: PI sign-off starts Phase 2. | |
| ## 2026-07-09 - MiniLM default; e5-large-v2 strong option; multilingual-E5 (design §13) | |
| MiniLM is the CCR reference model → comparability with the published method; E5 models require | |
| "query: " prefixes (encoded in registry usage_config). Rejected: e5-large-v2 as default | |
| (slow on CPU, breaks anonymous inline tier; cross-family scores not comparable). Trigger: | |
| lab validation study could change defaults. | |
| ## 2026-07-09 - Reverse scoring: raw + flags only in v1 (design §11) | |
| Aggregates exclude reversed items only when explicitly required; all-reversed blocks aggregate. | |
| Rejected: sign-negation by default (silently invented psychometrics). Trigger: lab methods | |
| decision after discussion with PI. | |
| ## 2026-07-09 - Placeholder demo sign-in for tier testing (not security) | |
| Anonymous caps (2 MB / 500 rows, env-tunable) enforce the design §5.1 tiers now; a | |
| name-only signed-cookie session lifts them so the tiered UX is testable before Supabase | |
| auth lands. get_current_user() is the single Phase 2 integration point. Rejected: waiting | |
| for real auth (tiers untestable) and client-only gating (trivially bypassed). | |
| ## 2026-07-09 - Open archive/delete until accounts exist | |
| Any visitor can archive/delete any project on the shared instance: there are no owners yet, | |
| and mistake-cleanup matters more than protection on a demo. Delete requires typing the | |
| project name, cascades to all files/rows, and is logged without text (design §9). Phase 2 | |
| adds ownership checks to the same endpoints. | |
| ## 2026-07-09 - No em dashes in project text (Deva) | |
| Style rule across UI, docs, comments, commits. Enforced by a PostToolUse hook; swept 50 | |
| existing files. | |
| ## 2026-07-10 - Questionnaire import policy (Mohammad's 40-scale collection) | |
| Multi-dimensional questionnaires split into one construct per dimension (a CCR run scores | |
| one construct; a blended Big Five score would be meaningless): 38 questionnaires -> 94 | |
| entries. "(R)" markers in subconstruct labels imported as reverse-scored flags (35 items); | |
| scales without markers default to false with reverse_flags_source recorded as pending. | |
| Filler items excluded (8, LOT-R and Hope Scale). SWLS skipped as duplicate of the existing | |
| seed. All imported entries are needs_verification until wording is checked verbatim. | |
| Importer kept as a permanent tool: packages/construct_library/import_from_xlsx.py. | |
| ## 2026-07-11 - Local email+password accounts as the interim auth provider (Deva) | |
| Mohammad delegated technical decisions and asked for the best free option available now. | |
| Supabase remains the Phase 2 target but needs dashboard/OAuth setup on lab accounts; | |
| interim: local accounts with stdlib scrypt password hashing, HMAC-signed session cookie, | |
| register/login/logout endpoints. No email verification and no self-service password reset | |
| (admin action at lab scale) - documented in the UI. get_current_user() stays the single | |
| swap point, so the Supabase move replaces token issuance only. Rejected: waiting on | |
| Supabase (blocks run limits, retention tiers, and real-user testing), shipping the | |
| name-only placeholder to real users (no actual account boundary). | |
| ## 2026-07-11 - Anonymous tier: 3 runs/day + delete-after-analysis; signed-in: 15 saved runs (PI 2026-07-10) | |
| Run limit as a signed cookie counter, reset daily (UTC): a nudge toward accounts, not a | |
| security boundary - clearing cookies evades it, acceptable at academic scale (recorded | |
| trade-off). Anonymous corpus files (and embedding caches) are deleted the moment a run | |
| completes; results stay downloadable until the project's TTL purge (CCR_ANON_TTL_HOURS, | |
| 24 on deployments, 0 = off in local dev). Signed-in users are never auto-deleted; a | |
| 15-saved-run cap (middle of Mohammad's 10-20) refuses new runs until they delete old ones. | |
| All numbers env-tunable. This retention shape is also what fits the infra into free tiers | |
| ($0-60/yr vs the earlier ~$600 estimate). | |
| ## 2026-07-11 - Ownership model: private owned projects, shared anonymous space | |
| Projects created signed-in are visible/editable only by their owner (403 otherwise); | |
| anonymous projects remain a shared open space subject to TTL purge. Replaces the 07-09 | |
| "open archive/delete" decision now that accounts exist. | |
| ## 2026-07-11 - Corpus-embedding cache keyed by (corpus, column, model, revision, prefix) | |
| ~97% of a run is document embedding and the core CCR workflow is many constructs against | |
| one corpus. Corpora are immutable after upload, so cached .npy embeddings are bit-identical | |
| on reuse; cache skipped for anonymous runs (files deleted anyway) and invalidated by | |
| corpus/project deletion. Rejected: quantization/fp16/GPU speedups - they change embedding | |
| values and would break reproduction-script parity and cross-run comparability. Any faster | |
| model variant must be a separate registry entry, never a silent swap. | |
| ## 2026-07-11 - Construct upload from CSV/XLSX: parse -> preview -> confirm | |
| Items are parsed server-side (tolerant ingest, "item"/single/longest-string column, | |
| reverse via column or trailing "(R)" marker - same convention as the lab spreadsheet | |
| importer) but NEVER saved directly: the researcher reviews and edits in the form first, | |
| because item wording IS the instrument. Item files are deleted immediately after parsing. | |
| ## 2026-07-13 - Storage interface ships now; R2 enabled at deploy by config (Deva) | |
| Local disk stays the dev default, but the S3-compatible path (Cloudflare R2) is | |
| implemented and tested behind one storage module: uploads, results, exports (streamed | |
| through the API - bucket stays private), and retention deletes all go through locators | |
| stored in the existing path columns (no migration; old local paths keep working). | |
| Flipping production to R2 = CCR_STORAGE=s3 + four env values. Rejected: deferring the | |
| implementation to Phase 2 (turns a deploy-day config flip into deploy-day development), | |
| and presigned public URLs (private bucket + API streaming is simpler and safer at lab | |
| scale). Embedding caches deliberately stay on local disk: derived data, no durability need. | |
| ## 2026-07-13 - Google sign-in via Supabase PKCE, server-side, feature-flagged (Deva) | |
| Implemented as a plain redirect flow: /api/auth/google/login sends the browser to | |
| Supabase's Google authorize URL with a PKCE challenge (verifier in a short-lived signed | |
| cookie); the callback exchanges the code server-side (stdlib urllib, no new deps), then | |
| finds-or-creates a local user by email and issues the SAME session cookie as password | |
| accounts. Inert until SUPABASE_URL + SUPABASE_ANON_KEY are set, so dev/tests need no | |
| Supabase project. Google-only accounts have no password hash and password login points | |
| them to the Google button. Rejected: supabase-js in the frontend (breaks the | |
| react+react-dom-only dependency rule for one button) and provider-JWT sessions (would | |
| fork the tier/ownership logic into two session formats for no benefit). | |
| ## 2026-07-14 - PI answers applied (library rights, caps, library pruning) | |
| 1. Questionnaire items INCLUDED in the public repos: PI's call ("I think all of them | |
| are public. and we can remove some in the future."). Per-construct removal stays a | |
| one-file delete; runs keep immutable snapshots regardless. This unblocks pushing to | |
| the lab org and shipping the full library on the dev instance. | |
| 2. Anonymous row cap tightened 500 -> 200 per PI. Applied to the ANONYMOUS tier only | |
| (Deva's scoping decision): signed-in research use routinely needs thousands of rows, | |
| and the PI's confirmed numbers were all anonymous-tier values. Env-tunable either way. | |
| 3. CRS (Centrality of Religiosity Scale, 5 dimension files) removed from the library | |
| per PI ("Dropp CRS too"; it was missing its citation). MFQ-30 and CRT were never | |
| imported, so "drop" for those is a no-op. MFQ-2 retained. Existing dev DBs keep | |
| already-synced CRS rows until their DB resets (append-only sync never deletes); | |
| fresh deploys never see it. | |
| 4. Wording verification assigned to the PI's RA: docs/verification_checklist.csv | |
| (525 items across 94 constructs, one row per item with correction columns) generated | |
| for that workflow; constructs stay needs_verification until she checks them. | |
| ## 2026-07-17 - Minimal admin surface (/admin), driven by four concrete pains (Deva) | |
| Password resets finally have an admin action; the lab tier becomes a per-user role | |
| (replaces the global saved-run env hack - lab accounts uncapped, public accounts keep 15); | |
| the RA gets a construct-verification queue (DB status is the operational overlay, the YAML | |
| library remains the durable source of truth and statuses are written back before | |
| production); usage counters + failed-run requeue answer "how is testing going" without | |
| SQL. Access = signed-in AND email in ADMIN_EMAILS (env allowlist, not a DB role, so a | |
| compromised DB cannot mint admins). Rejected: a fuller dashboard product (charts, audit | |
| UIs) - no current pain demands it. | |
| ## 2026-07-17 - DATABASE_URL: Postgres (Supabase) for persistent storage (Deva) | |
| Ephemeral-disk hosts (HF Spaces free) wipe SQLite on every restart, so accounts and all | |
| data disappeared. Fix: db.py uses Postgres when DATABASE_URL is set (SQLite otherwise, so | |
| local dev is unchanged). Supabase Postgres free tier chosen - already the auth provider, | |
| so it is the design doc's "one vendor for auth + DB + backups" with zero new cost. Schema | |
| was Postgres-portable by design; auto-migrate is now dialect-aware; psycopg v3 driver. | |
| Note: uploaded FILES still live on the local (ephemeral) disk - full file durability is | |
| the separate CCR_STORAGE=s3 (R2) switch, already implemented. | |
| ## 2026-08-12 - Anchor vectors: cosine default (+ dot toggle); per-pole score = mean-of-item-cosine (spec 0006) | |
| Bipolar constructs score along AV = target_centroid - opposite_centroid. Similarity metric | |
| defaults to cosine (the PI's formula) with a per-run dot-product toggle, because the paper | |
| (Teitelbaum & Simchon 2025, Appendix B) found dot sometimes better - a config flag lets us | |
| compare rather than guess. The per-pole "plain" score reported/exported is the standard CCR | |
| score (mean of per-item cosines), which equals text · pole_centroid; this makes the dot | |
| anchor score exactly target_score - opposite_score, so the export is self-checking. Rejected: | |
| cos(text, centroid) as the per-pole number (would not equal a normal single-construct CCR | |
| score, breaking cross-checks) and auto-negating reverse-scored items (kept as v1: (R) flags | |
| recorded, not applied - paper footnote 27 is a later option). Revisit: lab validation may fix | |
| one metric, or promote reverse-item negation. | |
| ## 2026-08-26 - Construct library: corrections ship as new versions; non-hash metadata re-syncs | |
| The 2026-08-25 library review (spec 0007) changed items in 23 constructs and status/citations | |
| in the rest. Split by the item hash: anything the hash covers (item text, order, reverse flags) | |
| ships as a NEW version file and the old one is kept and marked archived, so runs that pinned it | |
| still resolve; everything the hash does not cover (verification_status, citation, source_url, | |
| name, description, category) is edited in place and now re-syncs onto existing DB rows. | |
| The picker collapses seed constructs to the newest version per slug. Rejected: bumping the | |
| version for a status-only change (version would stop meaning "these items", and every | |
| verification pass would fork the library); and leaving sync insert-only (a review would never | |
| reach an already-seeded database, which is every deployed instance). The append-only guard on | |
| ITEMS is unchanged and still a hard error. Revisit: if a construct ever needs a third version, | |
| confirm the picker's "newest wins" is still what researchers expect. | |
| ## 2026-08-26 - License: MIT only, not dual MIT/Apache-2.0 | |
| The repo ships a single MIT LICENSE, matching what was already committed and the landing | |
| page's "free, open-source" claim. Two untracked drafts (an exact duplicate LICENSE-MIT and an | |
| Apache-2.0 alternative) are removed. Rejected: dual MIT OR Apache-2.0 - its main benefit is | |
| Apache's explicit patent grant, which is not a live concern for an academic text-analysis | |
| tool, and widening the license terms is the PI's and the university's call to make, not a | |
| cleanup task. README now states the license and separates it from the questionnaire items, | |
| which belong to their original authors. Revisit: if the lab distributes a packaged library | |
| (e.g. a PyPI ccr_engine) or a funder/university requires a specific license. | |
| ## 2026-08-26 - Model timing recorded as a curve with its measurement host, not one number | |
| All seven registry models said `expected_time_per_1k_texts_cpu: benchmark_required`. Measured | |
| with scripts/bench_models.py. Throughput varies ~50x between a 15-word social post and a | |
| 250-word document, so the field is now three numbers (short/medium/long) rather than one, with | |
| a comment naming the host and date. Rejected: a single representative figure (40x wrong at one | |
| end of the range) and recording laptop numbers unlabelled (the script's own docstring warns a | |
| shared cloud vCPU runs 2-4x slower). Revisit: re-run on the Space before these are shown to | |
| users anywhere in the UI. | |