Spaces:
Running
Running
Commit History
-
fix(ui): close the gap above the project name Running
Reported 2026-09-15 with a screenshot. The row that carries the expand button was rendered at all times so that toggling the sidebar would not shift the page. With the sidebar open it had nothing in it, which left an empty strip above the project name and set the heading a few pixels below the logo across the divider, where the eye reads them as a pair. It renders only while the sidebar is collapsed now, and the main pane's top padding is 7px rather than 12px, which puts the project name on exactly the same line as the logo (measured: 5px out before, 0 now). The cost is that toggling shifts the content by one row, which is the cheaper of the two.cdc4d17
-
feat(ui): account chip and menu at the foot of the sidebar
Asked for with ChatGPT's sidebar as the reference (2026-09-15). When the banner came off, its contents landed in the sidebar as a loose row of links with the account beside them. They now hang off one chip: avatar, name, and the kind of account, opening upward into About, Guide, the lab-only pages, the admin link, and sign in or out. The line under the name is ChatGPT's plan slot; ours says lab member, external user, maintainer or PI, which is what actually changes what you can do here. Signed out it reads "Not signed in / Sign in to keep your work", so the chip still makes the case the old Sign in button did. Escape closes the menu and stops there, deliberately: the drawer also closes on Escape, and one key dismissing both would leave a phone user staring at a workspace they did not ask for. Verified at 1280 and 390 px, signed in and out: the menu opens upward, stays inside the viewport, closes on Escape and on an outside click, and the drawer stays open underneath.ecf3b3a -
feat(ui): drop the dark banner; the sidebar is the chrome now
"Every modern website doesn't have an explicit header these days, ours looks old school" (2026-09-15). The banner sat on every screen, carried the product's full name, and cost vertical space on a phone to say something the landing page already says. The dashboard now has no top chrome at all, the way ChatGPT and Claude have none: the sidebar holds the logo, the guides, the admin link and the account, and the content area starts at the content. The landing page and /admin have no sidebar, so they keep a bar, but a light and quiet one with no tagline. The three static doc pages get the same treatment, and their inline logo strokes with currentColor, which is what lets one mark serve both their light and dark themes. Two things this forced, both right anyway. The dashboard became a real app shell (100vh, each pane scrolling inside it): stretched by a long page, the sidebar's account row and New project button had drifted to the bottom of the document instead of sitting at the bottom of…3967e32 -
feat(ui): the project sidebar closes, and is a drawer on phones
Asked for 2026-09-15: closable like ChatGPT or Claude. A panel button at the left of the header hides and shows it, aria-expanded and all, and the choice is remembered. It only renders where a sidebar exists, so not on the landing page and not on /admin. Wide screens: the sidebar slides out of the flex row on a negative margin rather than animating to width 0, so nothing inside it reflows on the way out and the workspace takes the space. Content sitting off-screen to the LEFT adds no page scroll, which a width animation on the inner content would have risked. Phones: it becomes a drawer over the workspace and starts closed regardless of the remembered choice. It used to be a full-width block stacked ABOVE the workspace, so every phone visit began by scrolling past the project list to reach the analysis. Three ways out, because the drawer covers the header's own toggle: tapping a project (you picked it to look at it), tapping the backdrop, or Escape. Crossing from wide to narrow close…e360d80
-
feat(ui): cite link under a run's reproducibility record
Ali Hajian, 2026-09-10, after trying the landing-page button: the results page is the other place it belongs. He is right - the reproducibility record is what someone is looking at while writing their methods section, so the citation should be within reach there rather than a trip back to the landing page. The dialog mounts inside ResultsView with its own state rather than being lifted into App and threaded through Workspace: nothing between them needs to know it exists. The landing-page footer keeps its own copy for the same reason. The site footer still does NOT follow people into the results view; that was a deliberate call about keeping the working area uncluttered, and this is one muted line, not a footer.9bde790
-
feat(ui): "Cite this platform" in the landing-page footer
Ali Hajian, 2026-09-09: somewhere inconspicuous to get a citation, the way Google Scholar's Cite button works. The footer of the landing page is that place - there for anyone writing a paper, invisible to anyone who is not. The dialog mirrors Scholar's panel in this app's language: five styles, each row a copy button, then BibTeX and RIS for reference managers. Under the styles it names the two CCR papers, because results from here rest on them and a methods section needs all three. Every string comes from GET /api/citation, so the component formats nothing. Two things measured rather than assumed. The clipboard: it needs a secure context and a focused document, so a failure selects the reference instead and says which keys to press, rather than leaving the click doing nothing. And the phone rules had to move BELOW the base .cite-* rules instead of joining the 700px block above them: same specificity, last one in the file wins, and from up there they silently lost (the row kept a 74p…61bbb51
-
fix(ui): fit the results views on a phone screen
Reported by Ali Hajian 2026-09-09, after yesterday's runs-table fix: the rest of the app should be usable on a phone too. Three problems at 360 and 390 px, all from rules written for a desktop table. The 820px block sets `table.docs { min-width: 520px }` so wide tables scroll instead of squashing. Right for the admin tables; wrong for the two results tables, which sit in a 328px card and so ALWAYS needed a sideways drag to read a text or a correlation. Score + Text now fits once the text wraps, and a two- or three-construct interrelations matrix fits outright; a wider matrix still scrolls inside .table-wrap, which is what a matrix has to do on any screen. Worse, a multi-construct results page scrolled sideways AS A PAGE, so every paragraph on it moved: each construct's summary line carries .picker-meta, which is nowrap, and "mean 0.133 · SD 0.082 · 6 items · CSV columns mfq_2_equality_*" is wider than a phone. It wraps under the construct name now. Same treatment for the selected-con…5b8f9e6
-
fix(ui): rework the sample-corpus section of Step 1
It looked bolted on, and it was: a grey slab inside the white card, the only element in Step 1 that was not a labelled field like the controls above it, reached through a collapsed link. Now it is the second branch of the step. An "or" rule separates it from the upload row, and it reuses the same .field label and control shape as Upload file / Corpus / Text column, so it belongs to the form instead of sitting in a box inside it. The select, the info button and Use share one height and one radius rather than being three different shapes. The collapse toggle is gone. It existed to keep the step quiet, but that is what hid the corpus name in the first place, and the picker is one row tall now, so there is nothing to hide. Scope is deliberately just this section; the rest of the workspace is untouched. Verified in a real browser at 1280 and 390 wide. That caught a mistake worth noting: an earlier edit had swallowed the closing brace of the 700px media query, so the new rules only appli…db5d011 -
fix(ui): show the sample corpus by default; stack the runs table on phones
Both reported 2026-09-08. The PI could not find CAMEL on the dev site, and a lab member hit the same thing plus a mobile layout problem. The sample-data panel started collapsed, so the only words on screen were "Use sample data" - the corpus is not named until you expand it, which is no help to someone looking for CAMEL. It now starts open. That is cheap because the panel is one row tall since the dropdown rework; the earlier objection was to two large cards, which no longer applies. On a phone the runs table pushed Status and View results past the right edge, so reaching the two things you open that table for meant scrolling sideways. Each run is now a stacked card with every field labelled from data-label. Nothing is dropped, and there is no horizontal scroll. Three things this walked into, all found by measuring in a real browser rather than reading the CSS: - the base rules are `table.docs td`, which outranks a bare `.docs td` and silently kept the table padding; - `@media (m…e639e13
-
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(ui): sample data picker is a dropdown with an info button
Choosing which corpus to run is a choice between mutually exclusive options, so it is a dropdown rather than a stack of rows: adding a dataset later costs one more option instead of another block of Step 1, and the panel stays one line tall whatever the catalogue holds. The per-entry About link becomes a single circular info button sized to the other controls, so it reads as part of the row rather than a glyph dropped into it. The dialog it opens now carries the CAMEL mark beside the name; the logo belongs there, on the dataset being described, and not in the picker, which lists several corpora and would imply they share a source. The selected entry keeps its one-line summary under the row, and an entry this instance cannot run is disabled in the dropdown, labelled "unavailable here", with the reason underneath. Drops the now-unused .samples-list, .sample-row, .sample-text, .sample-name, .sample-meta and .sample-actions rules rather than leaving them behind. Co-Authored-By: Claude …69aff04 -
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 -
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(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(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(ui): show the site identity footer on the welcome view only
The footer rendered on every view. It now renders only on the public welcome/landing page, so the dashboard and results views are uncluttered. Site identity for scanners already lives in the served HTML (da069b3).59b268b
-
fix(site): put lab identity into the served HTML, not just the JS bundle
The SPA served a 506-byte shell: a title, a script tag, and an empty root div. Every word identifying the lab lived in WelcomePage.jsx, which a crawler never executes. A scanner therefore saw a newly registered domain offering Google sign-in and file upload with no statement of who runs it, which reads as a deceptive-site fingerprint and contributed to a Safe Browsing flag. - frontend/index.html: meta description, Open Graph tags, and a <noscript> block naming the lab, the institution, the PI, contact, and source repo. - App.jsx: a site footer on every page carrying the same identity. - styles.css: .site-footer, plain text by design so it stays readable to clients that do not run the bundle. Served HTML goes from 506 to 2026 bytes.da069b3
-
fix(ui): keep the AI drafting tooltip text inside its box
The info bubble inherited the base `button { white-space: nowrap }` rule, so its longest line ("Not a validated scale...") overflowed the 300px box instead of wrapping. Reset white-space (plus overflow-wrap) on .infotip-pop. Also switched the trigger from a <button> to a focusable <span>: the bubble nests a list and a link, which are invalid inside a button and made the "More in the guide" link unreliable. aria-label carries the full text for screen readers; the visual bubble is aria-hidden with its link out of the tab order.e8f94ba -
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
-
ui: redesign the new-construct form as a contained panel with source cards
Tester feedback: the form read as cluttered - two levels of the same choice (entry buttons + tabs), every path's fields visible at once, and a flex-wrap bug stretching the Close button full-width. - One '+ New construct' entry; inside, three self-describing radio-cards (Type or paste / Upload a file / Draft with AI) - Only the chosen path's fields render, in task order; Reference hidden on the AI path, Description shown only there (required, drafts from it) - AI path: items box + Save appear once there's a draft to review; quota sits inline next to the Draft button - Panel is a bordered sub-card with its own header + close069d95d -
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 -
ui: three explicit construct paths + Save gating + visible AI drafts
PI test feedback (2026-08-07): - Step 2 hint enumerates the three acquisition paths; two buttons next to the picker ('+ Your own items', 'Draft with AI') replace the generic '+ Custom construct' - Save construct is disabled until name + items exist, with an inline line saying what's missing (the silent dead-click read as broken - the error banner sits at the top of the page, out of view) - After drafting, scroll/focus the items box and say 'N items drafted - they're in the box below' (drafts landed off-screen unnoticed) - /guide and MANUAL_TESTING.md updated to the new button namesdc9da41
-
ui: item-source chooser in custom-construct form (type / upload / AI)
The flat form stacked all three item sources at once and read as clutter (tester feedback); now a segmented chooser shows one source's controls at a time, with the items textarea below as the shared review surface. Also fixes the Draft-items button baseline (field-row aligns controls).6e3ac9f -
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
-
content: link the lab website (websites.umass.edu/matari)
'Who runs this' on the landing page links the lab name; /product's Questions section gets the same link.2a166b5 -
content: disclaimer as its own card section on the landing page
Same treatment as 'Who runs this' instead of foot-of-page fine print; PI wording unchanged.ce5745a -
content: PI landing-page copy and open-source disclaimer
Hero heading + CCR description, 'Who runs this' with citations (Atari 2023, Chen 2024, PsyEmbedding Atari 2026), anonymous-runs hint removed, and the as-is disclaimer at the page foot - the PI's wording verbatim (2026-07-31). Roadmap: warning-text item done; LICENSE file still pending.f6e17d3 -
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 -
style: modernize UI - design tokens, sticky header, focus rings, status dots
One radius pair, layered shadows, and a single teal focus ring replace the ad-hoc values; sticky gradient header; pressed button states; pulsing dot on running pills; gradient progress fill; chevroned collapsibles; blurred modal backdrop with entrance fades; thin scrollbars. All decorative motion behind prefers-reduced-motion. Grayscale font smoothing is scoped to high-DPI screens only - global 'antialiased' made text fuzzy on 1x displays.52ef91c -
feat: CAML lab branding - CCR logo, ink+teal theme, favicon, attribution
Palette derived from the lab's CCR logo: ink (#151515) header bars with the white logo variant, darkened logo teal (#26736f) for links/buttons (AA on white), pure logo teal (#339B98) for data fills. Correlation matrix now diverges teal (positive) vs copper (negative). Applied across the React app, guide, and product pages with 'Culture & Morality Lab, UMass Amherst' attribution.4e87534
-
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
-
refactor: enhance AdminPage overview display and improve navigation
- Added a Stat component to better visualize user statistics on the AdminPage. - Updated the overview section to use the new Stat component for a cleaner layout. - Improved the header navigation in App component for better user experience. - Changed the brand header to a link for easier navigation back to the dashboard. - Enhanced styling for header elements and added hover effects for links. - Updated empty state messaging and layout in the App component for clarity. - Refined CSS styles for better visual consistency and responsiveness.54bd1cf -
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
-
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
-
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