Spaces:
Running
Running
Commit History
-
feat(cite): one canonical citation for the platform and the CCR papers
Ali Hajian asked for a Google Scholar style Cite button (2026-09-09). The strings behind it live in app/citation.py and nowhere else: the dialog, the BibTeX and RIS exports, and the repo's new CITATION.cff all read from there, and a test fails if the .cff drifts. A citation that says two different things in two places is worse than no button. GET /api/citation is public, since the dialog sits on the landing page that signed-out visitors see. It returns the platform reference in APA, MLA, Chicago, Harvard and Vancouver, the two CCR papers a methods section needs beside it, and BibTeX and RIS containing all three. PLATFORM_VERSION moves to app/version.py so citation.py and jobs.py read the same constant: the version a paper cites is then the version stamped into the run metadata and the reproduction script that produced its numbers. Both papers were taken from Crossref, not from memory: 10.31234/osf.io/m93pd (Atari, Omrani & Dehghani 2023) and 10.18653/v1/2024.emnlp-main.151 (Chen, Li…b64a521
-
docs(corpus): record the CAMEL licence; the dataset link is fine
Correcting an earlier claim of mine: the dataset link is NOT broken. The HF repo is public and GATED, not private - the page returns 200 for anyone, and only file downloads require signing in and accepting the terms, which is ordinary for a research dataset. I had tested the file URL and generalised it to the page. Checking that surfaced something that does matter. CAMEL is licensed CC BY-NC 4.0, and nothing in the repo said so, while README states the project is MIT and sample_data/camel_sample.csv - 999 rows of that corpus - sits in the tree. Nothing here relicenses it, but a reader had no way to know the sample carries different terms from the code around it. Examples now record their own licence. It shows in the info dialog and rides in run metadata next to the citation, so a researcher who exports results has the terms in the same file as the scores. README and sample_data/README name CC BY-NC 4.0 for the bundled sample explicitly and separate it from MIT, and the guide states i…5411f5f -
fix(storage): do not require server-side copy for example corpora
Loading the full CAMEL corpus 500'd on the lab's deployment. The cause is not ours: R2 CopyObject is unavailable on that bucket, answering NoSuchKey for an object head_object resolves in the same session and with both CopySource forms. The dev account copies the identical 38 MB file without complaint, so this varies by account and cannot be relied on. The server-side copy was only ever an optimisation - it avoids reading 38 MB back out through the app per visitor. The caller has already downloaded the file to parse it, so the fallback is to upload that local copy: one upload, no extra fetch, and it works on any S3-compatible backend. Verified against the lab's bucket, where it now succeeds and leaves the master object intact. Note for whoever adds the next deployment: dev and prod are separate Cloudflare accounts, so the master file needs uploading once per account with scripts/upload_example_corpus.py. The catalogue only stores the key. Co-Authored-By: Claude Opus 5 (1M context) <n…82c5ea2 -
feat(library): settle the last two wording questions; CAMEL logo
Both decisions confirmed with the PI on 2026-09-07, so no construct in the library is left without a verification decision. IPIP keeps its leading "I". Worth recording why the question dissolved: the platform never added it. import_from_xlsx.py only strips whitespace, and the items arrived with the "I" already present in the commit that first created the library, from lib.xlsx. The IPIP source really does print bare stems ("Feel little concern for others") with the subject supplied by its instruction header, so the reviewer's observation was right about the state and wrong about the cause: this is a transcription convention in the lab's own spreadsheet. The five constructs are verified with that provenance recorded on each. K10 is retired rather than rewritten. Its items are fragments under a shared header, and CCR embeds item text directly, so "worthless?" is a poor stand-in for the construct. Archived, not deleted: constructs are append-only and any run that used it must still reso…726d852 -
fix(ui): sample data as a small entry point under the upload row
The PI's framing for CAMEL is a small "use sample data" call to action: from it a visitor sees the two available sizes, and can read about the data if they want to. The dropdown row did the opposite - it sat above the upload control and read as the first thing to do. Step 1 is now upload first. Under the row, one muted line ("No data of your own? Use sample data") expands in place into a two-row list: name, size, language, a one-line fit note, About, Use. About opens the provenance dialog (description, text column, preprocessing, citation, dataset link), which carries its own Use button so a reader can decide from there; Escape and the backdrop close it. The panel closes once a corpus is loaded, and the Corpus select echoes the sample's name rather than its filename. Sizes an instance cannot run stay listed with the reason and a disabled Use. Picking a size already in the project selects that copy instead of copying the corpus again, so a second click cannot duplicate 38 MB in the bu…0b5c981 -
fix(ui): one compact corpus row instead of two large cards
Three problems with the first cut, all fair. CAMEL appeared twice, as two "corpora". It is one dataset in two sizes, so the sizes are now options in a single dropdown and it reads as one dataset. It was badged DEFAULT, which overstates it. The PI asked for CAMEL to be "available and ready-to-use"; that is a starting point, not a claim that this is THE corpus. The field is renamed default -> preselected, it decides ordering and what loads first, and nothing is badged. The two expanded cards took most of Step 1 and pushed the upload control below the fold, which is the wrong hierarchy: most researchers bring their own data. Step 1 is now one labelled row - dropdown, info button, action - and the provenance (description, text column, preprocessing, citation, dataset link) moved into a modal behind the info button, where it is one click away without occupying the step. Also: the listing now knows this instance's row ceiling. The full corpus is 57,174 rows against a deployed CCR_MAX_ROW…bb4c28d -
fix(anon): owner key overflowed owner_user_id, 500ing anonymous writes
Live outage on both deployments since spec 0009 shipped on 2026-09-04: POST /api/projects and POST /api/constructs returned 500 for anyone not signed in, which is the entire anonymous try-it path. ANON_PREFIX + secrets.token_hex(16) is 37 characters and both owner_user_id columns are String(32). SQLite ignores VARCHAR limits so all 162 tests passed locally; PostgreSQL enforces them, so every anonymous insert was rejected. Signed-in users were unaffected because a user id is exactly 32 characters, which is why nobody reported it - lab members are signed in. Shortened the session id to 13 bytes (104 bits of entropy, key length 31) rather than widening the column. A type change is not additive, so auto_migrate_sqlite would not apply it and both deployed databases would have needed hand-run DDL; this deploys as-is. No data is lost because no anon: row was ever successfully written. The regression test reads the column width from the model rather than hardcoding it, and asserts against t…a7f2409 -
feat(corpus): offer the full CAMEL corpus from R2, raise the row ceiling
Follows spec 0010, which shipped only a 999-text sample because the full corpus could not be ingested. CCR_MAX_ROWS goes to 100,000, which was the code default all along; the three docs that variously claimed 20,000 and 50,000 are corrected to agree with it and with each other. That removes the ceiling that made the 57,174-row corpus un-ingestible. The full corpus is served from R2 under examples/, not the repo: 38 MB would ride in the Docker image and the Space repo on every deploy. It is deliberately NOT fetched from Hugging Face at runtime - that dataset returns 401, so fetching it would mean shipping lab credentials in a public app. scripts/upload_example_corpus.py puts it there and prints the row count the catalogue entry has to declare. Selection downloads the master once to parse, then makes the user's copy with a server-side copy_object, so 38 MB does not travel back out through the application for every visitor. Verified against the live bucket: 7.0s fetch, 0.7s parse of 57…88b1ea9 -
feat(corpus): CAMEL as a built-in example corpus [spec 0010]
PI request from 2026-08-19: make the lab's CAMEL corpus the example data on the site so visitors can try CCR without uploading anything. Corpus details from Aliah 2026-09-06. Step 1 now offers an example corpus above the upload control. One click loads it, selects the declared text column, and the run proceeds through the normal ingest path, so nothing downstream changes. The citation and a link to the full dataset sit on the card, and a run on an example writes a corpus_source block into its metadata carrying the citation, source URL and preprocessing note: results produced on someone else's corpus have to stay attributable in the same file as the scores. Ships a 999-text SAMPLE rather than the full corpus, because the full one cannot be the bundled default: 57,174 rows exceeds CCR_MAX_ROWS (50,000 on both deployments) so ingest would refuse it outright, 38 MB against a 156 KB sample_data would ride in the Docker image and the Space repo, and a demo has to finish in seconds on 2 vCP…3a1d713
-
feat(ui): show language names instead of ISO codes
PI request: "change the name of languages from acronyms to actual names (e.g., ar -> Arabic)". Display only. Codes stay exactly as they were everywhere they are recorded - run metadata, the results CSV, construct YAML, and the structured fields on warnings - because those are the reproducibility record and the output contract, so exports and reproduction scripts are byte-identical to before. app/languages.py is the single source of the mapping so the UI and the warning text cannot drift apart. Renamed at every point a code reached a human: the text-language selector, the language column in the runs table, the model list's language coverage, and the three language warnings. The registry's named sets were leaking a machine identifier too, so xlm_roberta_100 now reads "100+ languages (XLM-RoBERTa coverage)" while models.yaml keeps the checkable name. /api/languages now returns {code, name} rather than a bare code list; its one existing assertion is updated. A coverage test asserts ever…41ee593 -
feat(privacy): anonymous session scope, construct TTL, creation cap [spec 0009]
Follows spec 0008, which scoped custom constructs to signed-in owners and left the anonymous tier as one shared bucket. Digging into that surfaced that three separate defects were being read as one "constructs are public" bug. No anonymous identity: owner_user_id == "" meant anonymous, so every anonymous visitor shared a bucket and saw everyone else's projects and constructs. Anonymous visitors now carry a signed session-id cookie and own their rows as "anon:<id>", so one column carries both ownership kinds and the shared bucket is retired for projects and constructs at once. Signing in ADOPTS that session's rows into the account: without it, scoping anonymous work to a session would strand it at sign-in, and it also replaces the reason 049074a left anonymous projects open to everybody. No lifecycle: retention.py imported Corpus, Job, Project - Construct was not in it, and constructs hang off no project, so nothing ever cascaded to them. Corpora go when a run finishes and projects ex…b35131c -
fix(privacy): scope custom constructs to their creator, add delete [spec 0008]
A PhD student reported seeing custom constructs on the public site that they had not created, with no way to remove them. Construct had no owner column at all, so every custom construct anyone typed was returned to every visitor by GET /api/constructs - listed, with some irony, under a picker heading reading "My custom constructs". This is the projects listing leak (049074a) one table over: that fix taught _visible_owners the right rule, but constructs had no ownership concept to correct. Construct gains owner_user_id, mirroring Project, and the listing applies the same rule: seeds are public, a signed-in user sees only their own custom constructs, anonymous ones stay in the shared anonymous bucket. Both columns are additive with scalar defaults, so auto_migrate_sqlite adds them to the deployed databases and existing constructs become anonymous - which removes them from every signed-in user's picker on upgrade, without a manual cleanup. Deletion is a confirmation dialog in the picker…3594eb6
-
fix(retention): delete project rows in FK-safe order, enforce FKs in SQLite
delete_project_cascade removed jobs, then corpora, then the project, which is the right order to write but not the order that reached the database. Within a single flush SQLAlchemy sorts DELETEs by mapper dependency, and Job has no ORM relationship to Corpus (only a raw ForeignKey column), so it was free to emit DELETE FROM corpora first and violate jobs_corpus_id_fkey. Postgres enforces that constraint, SQLite does not unless foreign_keys is ON, so this only ever failed on a deployed instance: - DELETE /api/projects/{id} returned 500 (also the /admin delete path). - purge_expired_anonymous shares the cascade, so the hourly retention sweep raised on the first expired project that had ever been run and purged nothing after it. Anonymous projects and runs outlived CCR_ANON_TTL_HOURS; the dev database still holds one from 2026-08-06. Uploaded files were never affected, since an anonymous run's corpus is deleted when the run finishes. Fix is a flush between levels, so each level…248d767 -
fix(constructs): resolve the review's open questions from Noor's answers
She replied with the source she used for each disputed item, and every one checks out. Three of the four disagreements were about which PRINTING of a scale is authoritative, not about who read it correctly. MFQ Fairness: I was wrong to revert her. She read the questionnaire printed at the end of Graham et al. (2011), which is the paper this construct cites, and it does say "treated differently from others" (twice). The moralfoundations.org MFQ30 handout says "than others". Her correction is applied and source_url now points at the paper, so items, citation, and source finally agree. RSES and CBI work-related burnout: she used the source_url recorded on each construct, and both mappings are exactly right against it. The Rosenberg questionnaire at socy.umd.edu starts with "I feel that I'm a person of worth" and its wording and reverse keys match ours item for item, so only our ORDER was wrong. Both are reordered to the printed order with item ids renumbered, so item_id means "source it…0af19f8 -
fix(constructs): verify MFQ against the source the review sheet omitted
Checking whether the follow-up questions for the reviewer were fair turned up that two of them were not. The review sheet shipped with an EMPTY source_url for mfq_care and mfq_fairness, so the reviewer had nothing to check those against and correctly flagged it. That was our omission, not a missing document: the MFQ30 is public, all four items in each construct match it verbatim, and both are now verified with the source recorded. This also settles the "differently than/from others" disagreement in favour of the wording already stored. Also confirmed independently, so the reviewer does not need to be asked: the Subjective Happiness Scale PDF at the source_url we gave her does read "Compared to most of my peers", exactly as she corrected it. And for context on the two item-order findings: the APA measures-package printing of the RSES numbers its items exactly as we store them, while the reviewer's renumbering matches another widely-used printing. Same version-mismatch pattern as Edmo…10425ec -
fix(constructs): correct the review pass after a validity review
A research-validity review of the previous commit found four real problems. All four are fixed here. Two of the reviewer's wording corrections moved an item AWAY from the publication its construct cites, and I had applied them under a `verified` badge. Checked against the sources: the published MFQ30 reads "treated differently than others" (not "from"), and Edmondson (1999) reads "It is safe to take a risk on this team" (not "in") - the proposed "in this team" comes from a third-party questionnaire, whose URL I had also promoted over the working Edmondson PDF. Both items keep their published wording, both constructs are held back as unverified with the disagreement recorded, and the source_url override is dropped. The reviewer's item-ORDER findings for rses and cbi_work_related_burnout were silently dropped: my classifier only read notes on rows that failed one of the four checks, and these sat on rows marked OK. Order feeds item_hash and decides what sim_item_N means, so both are no…461b859 -
feat(constructs): apply the 2026-08-25 library review [spec 0007]
Noor Skhiri reviewed all 525 items across the 94 constructs (wording, reverse keying, subscale grouping, citations). This applies it. Split by what item_hash covers. Item changes ship as a NEW version: 61 reverse-scoring flags corrected across 14 constructs (35 -> 96 flagged items), 9 verbatim wording fixes, and one SCS-SF item moved to Over-identification per the source coding key. 23 constructs gain a version 2; their version 1 files keep their original items and are marked archived so runs that pinned them still resolve. Everything outside the hash is edited in place: 88 constructs promoted to verified with reviewer and date recorded, plus citation and source_url repairs. Six constructs stay needs_verification pending two PI decisions (the IPIP "I" prefix and the K10 stem). Two loader gaps had to close for this to ship: sync_library was insert-only, so a status change could never reach an already-seeded database, and the picker listed every version, so a v2 would show the same co…4afd259
-
fix(models): drop the PsyEmbedding pooling workaround, re-pin to fixed repos
The four lab PsyEmbedding repos shipped a modules.json referencing a 1_Pooling config the repos did not contain, so SentenceTransformer(id) could not load them and the registry carried a pooling_fallback flag telling the backend to assemble Transformer+mean-Pooling by hand. The missing config was pushed upstream on 2026-08-22, so the workaround is no longer needed. - models.yaml: re-pin all four models to the fixed revision (one commit above the old pin) and remove pooling_fallback. - registry test inverted: psyembedding models must now report pooling_fallback is None, so the workaround cannot creep back in. No code change is required. Both branch points (ccr.py model loading and reproducibility.py script generation) key off pooling_fallback being truthy, so new runs take the plain one-line loader automatically, while older runs keep the explicit module assembly recorded in their own metadata. Verified against the Hub: all four pinned revisions exist and carry 1_Pooling/config.j…597c9b7
-
feat(scoring): anchor vectors (bipolar constructs) [spec 0006]
Score texts along the axis between a target construct and a contrasting "opposite" construct: AV = target_centroid - opposite_centroid, anchor score = cosine(text, AV) (default) or dot(text, AV). Because embeddings are L2-normalized, a pole's CCR score (mean of per-item cosines) equals text . centroid, so the dot score is exactly target_score - opposite_score and the export is self-checking. - engine: run_ccr_anchored() (per-pole sims/scores + anchor score + metadata), reuses the doc-embedding cache; degenerate poles flagged. - data model: Job.opposite_construct_id + similarity_metric (additive columns). - runner: anchored export columns under output_schema_version 1.2, bipolar summary, both construct snapshots + scoring block in metadata, ANCHOR_DEGENERATE_POLES warning. - API: POST /api/jobs opposite_construct_id + similarity_metric, validated (single target, distinct poles, cosine|dot); JobOut echoes anchored fields. - reproduction script: anchored variant embeds both item …f341328
-
fix(privacy): stop showing shared anonymous projects to signed-in users
_visible_owners() returned ("", user_id) for a signed-in user, so every signed-in account saw the shared anonymous bucket (owner_user_id == ""): projects anyone created without signing in. Signed-in users now see only their own projects. Anonymous projects stay open by direct access so the anonymous -> sign-in continuity flow keeps working; they just no longer appear in a signed-in user's project list. Adds a regression test.049074a
-
fix(auth): trim whitespace from CCR_APP_URL to prevent sign-in issues
docs: update deployment instructions and clarify CCR_APP_URL usage test: add test for app_url to handle pasted whitespace in CCR_APP_URL chore: update reverse proxy worker with correct upstream host3fde616
-
feat(guide): public how-to guide + live AI model details
Split the guides so the public gets a plain how-to page and the lab keeps its click-through testing guide: - /guide now serves a new public how-to guide (public_guide.html): the upload -> construct -> run -> export flow, the four ways to add a construct (library, typed, uploaded, AI drafting), a models table, and a reproducibility section covering the results CSV, run metadata, and the offline reproduction script. - /testing serves the original testing guide, gated to lab and above (same gate as /product); the 403 explainer points to the public guide. Header shows a lab-only Testing link. - Landing page links the PI's name (Mohammad Atari) to mohammadatari.com. - AI construct path shows "Drafted by <model>" with a hover/focus info bubble (model, provider, prompt version, item rules, daily cap). Model identity is read live from the API: new item_generation.public_info() surfaced as a `generation` block on /api/auth/me, so the tooltip and guide never hardcode a model that…8586c1b
-
feat: audit trail for AI drafts + disabled-button reasons everywhere
- ConstructGeneration.items stores the ORIGINAL drafted items (PI request 2026-08-07): run metadata now shows the as-drafted text alongside the researcher's saved/edited items - Draft-items button disabled until Name + Description exist, reason inline (same pattern as Save; errors in the top banner go unseen) - Run button gets a 'To run, still needed: ...' line when disabled - AI generation quota shown from tab-open via auth/me, not only after the first draftc49bb09
-
feat: LLM-generated construct items (design note ITEM_GENERATION.md)
Draft questionnaire items for a construct from its name + explanation, for use as CCR item sets when no validated scale exists (PI idea 2026-07-31; decisions 2026-08-05: positively-keyed only, signed-in only, PI-approved prompt v1 and cautionary wording). - POST /api/constructs/generate-items: preview-only into the existing review-and-save flow; 20/day per-user cap (generation_events table) - Providers: Groq free tier (llama-3.3-70b, interim) / Anthropic Haiku (target once billing lands); auto-selected from which key is set - Provenance: Construct.generation_json -> ai_generated in the API, source_type llm_generated + cautionary note in run-metadata snapshots - UI: draft-with-AI section in the custom-construct form (library name-match warning), AI-generated badges in picker/selection/results - Live smoke test: scripts/smoke_test_generation.py (Groq verified)1a28176
-
feat: put invite links on hold; single brand header everywhere
Invite links are dormant behind CCR_INVITES_ENABLED (default off): creation 409s, existing tokens stop redeeming with a clear message, /admin hides the section. Pre-assigned roles remain the onboarding path; machinery and audit history stay intact for re-enabling. Header: the CCR logo (which already reads 'CCR') stands alone with the tagline 'Psychological Text Analysis with Contextualized Construct Representation' - app, guide, and product topbars now identical.6a6c7cf -
feat: make /product internal - lab members, maintainers, and PI only
Anonymous and external visitors get a friendly 403 explainer instead of the architecture/access-model docs (PI request 2026-07-31). The app header and public welcome page stop advertising the link to everyone else; the server enforces the gate via the new auth.role_lab_or_above helper.78fa678
-
feat: Implement multi-construct functionality in CCR analysis
- Updated frontend components to support selection of multiple constructs in the ConstructPicker. - Modified Workspace to handle an array of construct IDs instead of a single ID. - Enhanced ResultsView to display results for multiple constructs, including a correlation matrix. - Added tests for multi-construct runs to ensure correct behavior and output. - Updated styles to accommodate new UI elements for multi-construct selection and display. - Adjusted metadata handling to reflect changes in construct selection and results.a685524
-
feat: add welcome page for first-time users and update routing
- Introduced a new WelcomePage component to guide first-time visitors through the platform's features and usage. - Updated App component to conditionally render the WelcomePage based on user navigation and localStorage state. - Modified test cases to include the new /welcome route. - Updated styles to accommodate the new welcome page layout and design. - Changed asset references in index.html for updated frontend build.8ed8f32 -
Refactor admin page to include collapsible sections and enhance invite link management
- Added a collapsible Section component to improve layout and readability of the AdminPage. - Integrated invite link management, allowing admins to view, copy, and revoke invite links. - Updated tests to verify invite link functionality, including status checks and revocation behavior. - Enhanced UI elements for better user experience, including status indicators for invites and improved error handling.5ad96fc -
Four-tier access model: self-governing admin, pre-login access, audit trail
PI decisions 2026-07-22: - Roles: pi | maintainer | lab | external (legacy "member" reads as external). Lab members and above: unlimited saved runs. - PI role holds escalation rights in-app (grant/revoke staff, act on staff accounts, audit view); ADMIN_EMAILS is bootstrap + break-glass only. Maintainers get the operational surface. Nobody can change their own role. - Construct verification is maintainer-only (the RA's job); PI/admin keep a read-only queue. - Access before sign-in: email-bound pre-assigned roles (staff-capable, claimed on first password/Google sign-in) and signed 7-day invite links (lab/external only, ?invite= signup flow). - Append-only admin_audit table records every admin action; visible to PI/env admins. - Docs split: /guide = navigate-and-test; new /product page = access model + capability matrix + architecture (moved from guide).bfb6467 -
Repro script: valid Python booleans + per-run filenames in instructions
- ITEMS was rendered with json.dumps, emitting true/false: valid Python syntax (bare identifiers) but a NameError at runtime. Now a real Python literal (True/False); test literal_evals the block so JSON booleans can't slip through compile() again. - Script/requirements usage instructions now name the files exactly as they download (run-id suffix) and use the uploaded CSV as the example arg; download endpoints share the same filename helpers so names can't drift. Guide + MANUAL_TESTING updated to match.ffe862f
-
new upload restrictions
a9d817a -
Upload limits: make the byte ceiling configurable and stream-checked
The 25 MB cap was hardcoded, unlike every other limit, and was the wrong axis: embedding cost scales with rows and tokens, not file bytes. On the deployed Space (CCR_MAX_ROWS=20000) it only bound for rows averaging over ~1.3 KB, i.e. long-document corpora - the exact shape the planned chunking feature makes more common. - MAX_UPLOAD_BYTES -> max_upload_bytes(), env-configurable via CCR_MAX_UPLOAD_BYTES, default 50 MB. It is now an OOM/abuse backstop; CCR_MAX_ROWS remains the limit that bounds job cost. - Stream uploads in 1 MB chunks and reject mid-transfer. Previously the whole payload was read into memory *before* the size check, so an oversized upload was fully resident before being refused. Partial temp files are cleaned up on any failure, including client disconnect. - Anonymous byte cap 2 MB -> 5 MB: 200 rows of long documents (200 x 8 KB transcripts ~ 1.6 MB) sat uncomfortably close to the old ceiling. The 200-row cap is unchanged and remains what actually binds f…776393c
-
Graceful handling when an uploaded file is gone (restart-safe UX)
3f7c413 -
Admin: no password reset for Google accounts (UI + backend guard)
5a24540 -
Admin page v1: user roles (lab tier), password resets, failed-run requeue, verification queue
Synced from lab mainline. /admin gated by ADMIN_EMAILS env allowlist. Lab-role accounts get unlimited saved runs (replaces the global env hack). 72 tests passing.b633038
-
Refine documentation and fix upload handling
- Update MANUAL_TESTING.md to clarify parsing behavior for multi-column files. - Correct title and formatting in guide.html and index.html for consistency. - Enhance README.md to specify behavior for skipped duplicates and blank lines. - Implement unique ID assignment for corpus uploads in main.py to prevent file clobbering. - Add regression test in test_api.py to ensure distinct uploads do not overwrite each other.9fce59c -
Add /guide tester page and /samples demo-data downloads
ed98758
-
Sync platform from lab mainline: dev instance ready to deploy
Full feature set: accounts (email/password + feature-flagged Google via Supabase PKCE), anonymous tiers (3 runs/day, delete-after-analysis, 24h purge), saved-run cap, construct upload from CSV/XLSX, structured warnings + language detection, model registry (MiniLM/E5), per-run reproduction scripts, corpus-embedding cache, storage interface (local/ R2), sample-data test kit, MANUAL_TESTING.md. Rights guard: only the 5 original seed constructs ship in this public repo; the imported lab collection stays out pending the redistribution decision. HF remote URL detokenized. 64 tests passing.4b09d2d
-
CCR Platform: web platform for Contextualized Construct Representations
Upload a corpus, select or define a validated psychological construct, run CCR analysis with locally-hosted sentence embeddings, inspect per-item loadings and score distributions, export results with a reproducibility record. - FastAPI backend, SQLite, worker-queue jobs with orphan recovery - React dashboard (prebuilt; no Node needed to run) - Tolerant corpus ingestion (encodings, delimiters, caps, warnings) - Seeded construct library with citations; custom constructs - 17 tests via a deterministic embedding backend (no ML deps needed) - Docker deploy for Hugging Face Spaces / Cloud Rund69ace5