-
Notifications
You must be signed in to change notification settings - Fork 2
Feature/maturity attribution #115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
meganrm
wants to merge
33
commits into
main
Choose a base branch
from
feature/maturity-attribution
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 30 commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
8bb1887
Add maturity label feature design spec
meganrm d5cf561
Fix type update note in maturity label spec
meganrm 52dc277
Add maturity label implementation plan
meganrm 44bf646
feat: add maturity resolver with speculative default
meganrm 1979d16
feat: add maturity field to IdeaPost GraphQL schema
meganrm fc61499
feat: add maturity level config and getMaturityConfig utility
meganrm 4dcc5a2
feat: add maturity select field to CMS config
meganrm 3e1ca81
feat: add MaturityBadge component
meganrm 4b96777
fix: use correct CSS module import pattern and normalize badge border
meganrm 88e253c
feat: show maturity badge in idea detail metadata strip
meganrm bc646e8
feat: show maturity badge in idea list
meganrm 46a6171
fix: white badge background, teal border ramp, spacing from tags
meganrm 43232cd
add example maturity labels
meganrm 68c787d
change to popover to share styles
meganrm b9bd27f
add maturity level
meganrm bf2bbed
update docs based on changes
meganrm 50b85bc
change hint text
meganrm 9b4dbdb
move badges to the right
meganrm 35c32a1
change colors
meganrm e129360
style: replace teal with ALLEN_BLUE opacity ramp on maturity badge
meganrm 991e62e
style: consolidate inlineDotted text-decoration into shorthand
meganrm 43b738e
feat: add variant prop to MaturityBadge for inline dotted rendering
meganrm 642a4f2
style: explicitly inherit font sizing in inlineDotted variant
meganrm 9780300
feat: show maturity badge inline with title in idea list
meganrm 360c671
style: use gap on titleRow for badge spacing; fix title display
meganrm 906a2be
feat: use inline maturity badge variant in post metadata strip
meganrm 388d3cb
update cursors
meganrm 1f63fcd
adjust alignment
meganrm d1f7978
fix text color
meganrm 1524a23
fix format
meganrm e50c911
remove unused class
meganrm bb63205
put specs into a changelog
meganrm e95261a
Merge branch 'main' into feature/maturity-attribution
meganrm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| # Maturity Label Feature Design | ||
|
|
||
| **Date:** 2026-06-04 (updated 2026-06-11) | ||
| **Branch:** feature/maturity-attribution | ||
|
|
||
| ## Problem | ||
|
|
||
| Scientists are reluctant to share early-stage ideas because they don't want to be held accountable if the idea turns out to be wrong. A maturity label gives authors a way to signal "this is as-is" — framing uncertainty positively rather than as a warning. | ||
|
|
||
| ## Decision Summary | ||
|
|
||
| - 4 fixed levels with evidence-based labels: Speculative, Exploratory, Supported, Validated | ||
| - Visual: ALLEN_BLUE opacity ramp pill badge with tooltip hint text (no emoji on rendered badge) | ||
| - Two rendering contexts: pill badge in the idea list (after title), plain text with dotted underline in the idea detail metadata strip | ||
| - Appears in both the idea list and the idea detail page (metadata strip) | ||
| - Required in the CMS for new ideas; existing ideas default to Speculative via a schema resolver | ||
|
|
||
| --- | ||
|
|
||
| ## 1. Data Model | ||
|
|
||
| ### Frontmatter | ||
|
|
||
| New optional string field on idea `.md` files. Existing files omit it; the resolver supplies the default. | ||
|
|
||
| ```yaml | ||
| maturity: speculative # speculative | exploratory | supported | validated | ||
| ``` | ||
|
|
||
| ### GraphQL Schema (`gatsby/schema/base.gql`) | ||
|
|
||
| Added to `IdeaPost` as a nullable `String`: | ||
|
|
||
| ```graphql | ||
| type IdeaPost implements Node { | ||
| ... | ||
| maturity: String | ||
| } | ||
| ``` | ||
|
|
||
| ### Gatsby Resolver (`gatsby/resolvers/resolvers.js`) | ||
|
|
||
| Returns `"speculative"` when the frontmatter field is absent: | ||
|
|
||
| ```js | ||
| IdeaPost: { | ||
| maturity: { | ||
| resolve: (source) => source.maturity ?? "speculative" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| This ensures every idea always has a maturity value at query time without touching existing files. | ||
|
|
||
| --- | ||
|
|
||
| ## 2. CMS Configuration (`static/admin/config.yml`) | ||
|
|
||
| New `select` widget in the `ideas` collection, placed after the `type` field: | ||
|
|
||
| ```yaml | ||
| - label: "Maturity" | ||
| name: "maturity" | ||
| widget: "select" | ||
| required: true | ||
| default: "speculative" | ||
| options: | ||
| - { label: "🌱 Speculative — Untested, shared to invite discussion", value: "speculative" } | ||
| - { label: "🌿 Exploratory — Early investigation, findings are preliminary", value: "exploratory" } | ||
| - { label: "🌳 Supported — Backed by data or analysis, not yet exhaustive", value: "supported" } | ||
| - { label: "🍎 Validated — Well-evidenced and reproducible", value: "validated" } | ||
| ``` | ||
|
|
||
| The emoji in the CMS dropdown helps authors scan options visually. The rendered badge on the site uses the ALLEN_BLUE color scale only (no emoji). | ||
|
|
||
| --- | ||
|
|
||
| ## 3. `MaturityBadge` Component | ||
|
|
||
| **Files:** | ||
| - `src/components/MaturityBadge.tsx` | ||
| - `src/style/maturity-badge.module.css` | ||
|
|
||
| ### Level config | ||
|
|
||
| | Value | Label | Tooltip hint | | ||
| | ------------- | ----------- | ---------------------------------------------------------------------------- | | ||
| | `speculative` | Speculative | Untested: shared to invite discussion and future investigation. Not a claim. | | ||
| | `exploratory` | Exploratory | Early investigation — findings are preliminary | | ||
| | `supported` | Supported | Backed by data or analysis, but not yet exhaustive. Needs further work. | | ||
| | `validated` | Validated | Well-evidenced and reproducible | | ||
|
|
||
| ### Visual | ||
|
|
||
| Four CSS classes (`.speculative`, `.exploratory`, `.supported`, `.validated`) on a shared pill shape. Colors use `--ALLEN_BLUE` with a `color-mix` opacity ramp — faint at Speculative, full blue at Validated: | ||
|
|
||
| | Level | Text opacity | Border opacity | Background opacity | | ||
| |-------------|-------------|----------------|-------------------| | ||
| | speculative | 45% | 25% | 8% | | ||
| | exploratory | 65% | 40% | 12% | | ||
| | supported | 85% | 60% | 16% | | ||
| | validated | 100% | 80% | 20% | | ||
|
|
||
| Tooltip uses Ant Design `<Popover>` component. Unknown values fall back to Speculative rendering. | ||
|
|
||
| A `.inlineDotted` class provides the non-pill rendering: `text-decoration: underline dotted`, `cursor: help`, no border or background. Used with the `"inline"` variant (see Props). | ||
|
|
||
| ### Props | ||
|
|
||
| ```ts | ||
| interface MaturityBadgeProps { | ||
| maturity: string; | ||
| className?: string; | ||
| variant?: "badge" | "inline"; // default: "badge" | ||
| } | ||
| ``` | ||
|
|
||
| - **`"badge"`** (default): pill rendering using `.badge` + level class. Used in the idea list. | ||
| - **`"inline"`**: plain text rendering using `.inlineDotted` + level class (color only, no pill). Used in the post detail metadata strip. The `Popover` wraps both variants. | ||
|
|
||
| --- | ||
|
|
||
| ## 4. Rendering Locations | ||
|
|
||
| ### List view (`src/components/IdeaRoll.tsx`) | ||
|
|
||
| - `maturity` is already in the GraphQL query | ||
| - Render `<MaturityBadge maturity={item.maturity} />` (default `"badge"` variant) inline after the title `<Link>`, as a sibling element | ||
| - `FigureThumbnail` appears on the right side of each list row (88×56px pill shape). Rows without a figure have no reserved gap. | ||
| - **Note:** `IdeaRoll.tsx` and `idea-roll.module.css` have unresolved merge conflicts (`UU` git status) from integrating the thumbnail branch. Resolution keeps both the thumbnail and the badge rendering. | ||
|
|
||
| ### Detail page (`src/templates/idea-post.tsx`) | ||
|
|
||
| - `maturity` is already in the `IdeaPostByID` page query | ||
| - `<MaturityBadge maturity={maturity} variant="inline" />` renders as colored text with a dotted underline in the `metaGroup` — no pill. The `Popover` hint remains active on hover. | ||
|
|
||
| ### Type update | ||
|
|
||
| `IdeaPostNode` in `src/types/index.ts` is auto-derived from the GraphQL query via `Queries.IdeaPostByIDQuery`. No manual edit is needed — running `gatsby develop` after updating the query regenerates the Gatsby type and `maturity` flows through automatically. The inline `IdeaListItem` type in `IdeaRoll.tsx` similarly picks up the field once the query is updated. | ||
|
|
||
| --- | ||
|
|
||
| ## 5. Testing | ||
|
|
||
| ### `MaturityBadge` unit test | ||
|
|
||
| - Each of the four level values renders the correct label and tooltip hint text | ||
| - An unknown value falls back gracefully to Speculative rendering | ||
|
|
||
| ### Resolver unit test | ||
|
|
||
| - Returns the field value when `maturity` is present in frontmatter | ||
| - Returns `"speculative"` when `maturity` is absent | ||
|
|
||
| No changes to existing tests are required — the new field is additive. | ||
|
|
||
| --- | ||
|
|
||
| ## Out of Scope | ||
|
|
||
| - Allowing authors to update the maturity level of existing ideas in bulk (authors update individually via CMS) | ||
| - Filtering or sorting ideas by maturity level on the index page | ||
| - Any automated progression of maturity level | ||
| - Thumbnail design changes (size, shape, shadow — kept as-is) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import { describe, expect, it } from "vitest"; | ||
|
|
||
| import { createIdeaPostResolver } from "../resolvers"; | ||
|
|
||
| const mockReporter = { error: () => {} }; | ||
|
|
||
| describe("createIdeaPostResolver - maturity", () => { | ||
| const resolver = createIdeaPostResolver(mockReporter); | ||
|
|
||
| it("returns the maturity value when present", () => { | ||
| expect(resolver.maturity.resolve({ maturity: "speculative" })).toBe( | ||
| "speculative", | ||
| ); | ||
| expect(resolver.maturity.resolve({ maturity: "exploratory" })).toBe( | ||
| "exploratory", | ||
| ); | ||
| expect(resolver.maturity.resolve({ maturity: "supported" })).toBe( | ||
| "supported", | ||
| ); | ||
| expect(resolver.maturity.resolve({ maturity: "validated" })).toBe( | ||
| "validated", | ||
| ); | ||
| }); | ||
|
|
||
| it("returns 'speculative' when maturity is absent", () => { | ||
| expect(resolver.maturity.resolve({})).toBe("speculative"); | ||
| expect(resolver.maturity.resolve({ maturity: null })).toBe( | ||
| "speculative", | ||
| ); | ||
| expect(resolver.maturity.resolve({ maturity: undefined })).toBe( | ||
| "speculative", | ||
| ); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import React from "react"; | ||
|
|
||
| import { Popover } from "antd"; | ||
|
|
||
| import { getMaturityConfig } from "../constants/maturityLevels"; | ||
| import * as styles from "../style/maturity-badge.module.css"; | ||
| import * as popoverStyles from "../style/tag-popover.module.css"; | ||
|
|
||
| interface MaturityBadgeProps { | ||
| maturity: string; | ||
| className?: string; | ||
| variant?: "badge" | "inline"; | ||
| } | ||
|
|
||
| export const MaturityBadge: React.FC<MaturityBadgeProps> = ({ | ||
| className, | ||
| maturity, | ||
| variant = "badge", | ||
| }) => { | ||
| const config = getMaturityConfig(maturity); | ||
| const levelClass = styles[maturity] ?? styles.speculative; | ||
| const baseClass = variant === "inline" ? styles.inlineDotted : styles.badge; | ||
| return ( | ||
| <Popover title={config.hint} className={popoverStyles.popoverContent}> | ||
| <span | ||
| className={[baseClass, levelClass, className] | ||
| .filter(Boolean) | ||
| .join(" ")} | ||
| > | ||
| {config.label} | ||
| </span> | ||
| </Popover> | ||
| ); | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import { describe, expect, it } from "vitest"; | ||
|
|
||
| import { getMaturityConfig } from "./maturityLevels"; | ||
|
|
||
| describe("getMaturityConfig", () => { | ||
| it("returns the correct label for each level", () => { | ||
| expect(getMaturityConfig("speculative").label).toBe("Speculative"); | ||
| expect(getMaturityConfig("exploratory").label).toBe("Exploratory"); | ||
| expect(getMaturityConfig("supported").label).toBe("Supported"); | ||
| expect(getMaturityConfig("validated").label).toBe("Validated"); | ||
| }); | ||
|
|
||
| it("returns the correct hint for each level", () => { | ||
| expect(getMaturityConfig("speculative").hint).toBe( | ||
| "Untested: shared to invite discussion and future investigation. Not a claim.", | ||
| ); | ||
| expect(getMaturityConfig("exploratory").hint).toBe( | ||
| "Early investigation — findings are preliminary", | ||
| ); | ||
| expect(getMaturityConfig("supported").hint).toBe( | ||
| "Backed by data or analysis, but not yet exhaustive. Needs further work.", | ||
| ); | ||
| expect(getMaturityConfig("validated").hint).toBe( | ||
| "Well-evidenced and reproducible", | ||
| ); | ||
| }); | ||
|
|
||
| it("falls back to Speculative for unknown values", () => { | ||
| expect(getMaturityConfig("unknown").label).toBe("Speculative"); | ||
| expect(getMaturityConfig("").label).toBe("Speculative"); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| interface MaturityConfig { | ||
| label: string; | ||
| hint: string; | ||
| } | ||
|
|
||
| export const MATURITY_CONFIG: Record<string, MaturityConfig> = { | ||
| speculative: { | ||
| label: "Speculative", | ||
| hint: "Untested: shared to invite discussion and future investigation. Not a claim.", | ||
| }, | ||
| exploratory: { | ||
| label: "Exploratory", | ||
| hint: "Early investigation — findings are preliminary", | ||
| }, | ||
| supported: { | ||
| label: "Supported", | ||
| hint: "Backed by data or analysis, but not yet exhaustive. Needs further work.", | ||
| }, | ||
| validated: { | ||
| label: "Validated", | ||
| hint: "Well-evidenced and reproducible", | ||
| }, | ||
| }; | ||
|
|
||
| export function getMaturityConfig(maturity: string): MaturityConfig { | ||
| return MATURITY_CONFIG[maturity] ?? MATURITY_CONFIG.speculative; | ||
| } |
11 changes: 6 additions & 5 deletions
11
...ges/ideas/2025-10-29-relate-cell-cell-junction-remodeling-to-migration-onset.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,15 @@ | ||
| --- | ||
| templateKey: idea-post | ||
| title: Relate cell-cell junction remodeling to migration onset | ||
| resources: | ||
| - released-emt-dataset | ||
| program: | ||
| - EMT | ||
| date: 2025-10-28T19:10:00.000Z | ||
|
Comment on lines
-4
to
-8
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why were these removed?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. they were just reordered |
||
| type: analysis of existing data | ||
| maturity: exploratory | ||
| date: 2025-10-28T19:10:00.000Z | ||
| authors: | ||
| - Gokhan Dalgin | ||
| program: | ||
| - EMT | ||
| resources: | ||
| - released-emt-dataset | ||
| tags: | ||
| - EMT | ||
| - migration | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have little to no attention paid to keyboard nav, I'd argue we should basically ignore it until we get to a more complete place and then do a concentrated accessibility pass rather than add it in piece meal right now.