From 10ccbcc6e45cf6154aedf20f2fc150fa46aeeec4 Mon Sep 17 00:00:00 2001 From: Yorke Rhodes III Date: Thu, 27 Aug 2026 10:19:31 -0400 Subject: [PATCH 1/2] Add a skill for adding a new /team profile Documents the procedure for adding an advisor, staff member, cohort researcher, or collaborator to the Team page: the required TeamMember fields, the headshot intake and optimize:images step, the slug-uniqueness and cohort-count invariants check:content enforces, and the validation/visual-check sequence. Includes a worked example using Brianna Gabaldon (Microsoft Product Manager, NYU CGA grad, advisor) with the still-unverified fields - photo, LinkedIn, specific advisory focus, and bio - left as explicit placeholders rather than fabricated, per the content guide's rule against publishing unverified biographical claims. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/skills/add-team-profile/SKILL.md | 186 +++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 .github/skills/add-team-profile/SKILL.md diff --git a/.github/skills/add-team-profile/SKILL.md b/.github/skills/add-team-profile/SKILL.md new file mode 100644 index 000000000..b7fad7c83 --- /dev/null +++ b/.github/skills/add-team-profile/SKILL.md @@ -0,0 +1,186 @@ +--- +name: add-team-profile +description: Add or update a person's profile (advisor, staff, cohort researcher, or collaborator) on the /team page — headshot, TeamMember record in src/content/site.ts, and validation. Use when asked to add someone to the Team page, add an advisor/researcher/staff member/collaborator, or update an existing team member's bio, photo, role, or org. +--- + +# Add a Team Profile + +## What this touches + +- [`src/content/site.ts`](../../../src/content/site.ts) — the `team` export, the + single source of truth for everyone on `/team` +- [`public/team/`](../../../public/team/) — headshots +- [`public/team/README.md`](../../../public/team/README.md) — headshot status list +- `README.md` and `team.researchersCount` / `cohorts` — **only** when adding a + **researcher** to the current cohort (step 5) + +Nothing else needs to change. Pages under `src/app/team/` are presentational — +they render whatever is in `team` and degrade gracefully when a field is +missing. Full field reference: +[`docs/CONTENT-GUIDE.md`](../../../docs/CONTENT-GUIDE.md) §2, "Add / edit a team +member" — this skill covers the *procedure*, not a full field-by-field spec. + +## Required inputs — gather these before writing anything + +| Field | Required? | Notes | +|---|---|---| +| Full name | Required | Exactly as they want it published. | +| Which list | Required | `advisors`, `residentFellows` (staff), `researchers` (current cohort), or `collaborators`. Ask if unclear. | +| Role / title | Required | For advisors, match the existing pattern: `"Advisor · "` (e.g. `"Advisor · Civilian Protection & IHL"`). Ask what they advise on if it isn't obvious from context. | +| Org / affiliation line | Recommended | Their day job or institution, e.g. `"Product Manager, Microsoft"`. Shown under the role. | +| Headshot | Recommended | A real photo file. Without one the card shows clean initials — acceptable, not an error, but flag it so it isn't forgotten. | +| LinkedIn URL | Recommended | Full profile URL. | +| Personal / faculty website | Optional | Only if distinct from LinkedIn. | +| Bio | Recommended for advisors | One to three short paragraphs, third person, past/present tense matching the existing bios. Without one, the detail page just shows "Bio coming soon." | +| Cohort term | Only for `researchers` | e.g. `"Summer 2026"`. Advisors, staff, and collaborators don't need this. | +| Slug | Recommended | `firstname-lastname`, lowercase, hyphenated. Omit only if the card should be non-clickable. | + +**Never fabricate any "Recommended" field.** If a bio, LinkedIn URL, or photo +isn't supplied, either omit the field (the site handles a missing +photo/bio/LinkedIn gracefully) or ask for it — don't invent biographical claims, +credentials, or a URL for a real person. This is an explicit rule in +`docs/CONTENT-GUIDE.md`: *"Never publish a name, role, affiliation, or biography +you have not verified against an authoritative source."* + +## Procedure + +### 1. Confirm the category + +Advisor, staff (`residentFellows`), current-cohort researcher, or collaborator. +This decides which array in `team` the object goes into, and whether step 5 +(the cohort-count invariant) applies. + +### 2. Add the headshot (if supplied) + +1. Drop the file into `public/team/.` (`.jpg`, `.jpeg`, or + `.png`) — lowercase first name, matching the existing filenames. +2. Run: + ``` + npm run optimize:images + ``` + Resizes in place to the 384px team budget and strips EXIF. Safe to run + every time — a no-op on files that already fit. +3. Add a status line for them in `public/team/README.md`, matching the + existing list format. +4. **No photo yet?** Leave `photo` out of the object entirely. The card falls + back to initials — never point `photo:` at a file that doesn't exist. + +### 3. Add the `TeamMember` object + +Open `src/content/site.ts`, find `export const team = {`, and add the object +to the right array (`advisors`, `residentFellows`, `researchers`, or +`collaborators`): + +```ts +{ + initials: "BG", + name: "Brianna Gabaldon", + role: "Advisor · ", + org: "Product Manager, Microsoft", + linkedin: "https://www.linkedin.com/in/…", // the real URL — ask for it + photo: "/team/brianna.jpg", // omit if no photo yet + slug: "brianna-gabaldon", + bio: "…", // real, verified copy — ask; omit if not supplied yet +}, +``` + +Match the array's existing style — some are cast with `as TeamMember[]` on the +array itself; don't add a redundant per-object cast if so. + +### 4. Slug collisions + +Slugs are unique **across every category**, not per-list — the founder, an +advisor, and a researcher all share one `/team/[slug]` namespace. +`npm run check:content` (step 6) catches a collision; always run it. + +### 5. Only when adding a `researchers` entry: the three-place cohort-count invariant + +Adding a **researcher** to the *current* cohort means updating, in the same +change: + +1. `team.researchersCount` (e.g. `"7 researchers"` → `"8 researchers"`) +2. The matching entry's `"N researchers"` item in `cohorts` +3. The prose count in `README.md` ("`` applied researchers") + +`npm run check:content` fails if these three — plus the actual roster — don't +agree. This exact mistake has reached production twice before. + +**Advisors, staff, and collaborators do not touch this invariant.** Skip this +step for anyone outside `researchers`. + +### 6. Validate + +``` +npx eslint src/content/site.ts +npm run check:content +npm run check:images +npm run build +``` + +All four must pass. If `check:images` fails, the photo just added is still at +full camera resolution — re-run `npm run optimize:images`. + +### 7. Visual check + +``` +npm run dev +``` + +Open `/team` and confirm: + +- The card appears in the right section with a real photo (not a broken + image) or clean initials. +- If a slug was given, click through to `/team/` and confirm photo, + role, org, LinkedIn/website links, and bio all render as expected. +- Check both dark and light themes. +- Advisors render in a 2-column grid — an odd total just leaves one empty + slot on the last row; that's expected, not a bug. + +### 8. Regenerate the snapshot — only if publishing this change on its own + +If this is a standalone content pull request, follow +[`CONTRIBUTING.md`](../../../CONTRIBUTING.md) §4: commit the +`src/content/site.ts` (+ `public/team/`) change first, then run +`npm run sync:static` and commit `static-site/` **separately**. If this is +bundled with other work that already regenerates the snapshot, don't +regenerate twice. + +## Worked example: adding Brianna Gabaldon (Advisor) + +Given: *"Brianna Gabaldon, Microsoft Product Manager, and CGA grad, being +added as an advisor."* + +That names the category and gives a start on `org`, but is **not enough to +publish** — the specific advisory focus, photo, LinkedIn, and bio are still +unverified: + +```ts +{ + initials: "BG", + name: "Brianna Gabaldon", + role: "Advisor · ", + org: "Product Manager, Microsoft · NYU CGA alum", + linkedin: "", + // photo: "/team/brianna.jpg", // add once a headshot is supplied + slug: "brianna-gabaldon", + // bio: "", +}, +``` + +Add this to `team.advisors` in `src/content/site.ts`. Uncomment `photo`/`bio` +once supplied, run `npm run optimize:images` for the headshot, and complete +steps 4, 6, and 7 above before treating her as published. + +## Learnings + +- **A missing photo or bio is not a bug.** The card and detail page degrade + gracefully (initials; "Bio coming soon."). Never invent content to fill the + gap — leave the field out and flag it as still needed. +- **`team.researchersCount` only tracks `researchers`.** Don't run the + cohort-count reconciliation (step 5) for an advisor, staff member, or + collaborator — they aren't part of that invariant. +- **Slugs are global, not per-category.** Check for a collision with anyone + already on the roster, including the founder, before picking one. +- **A phone-camera headshot ships at full resolution** if `optimize:images` + isn't run afterward — `npm run check:images` exists specifically to catch + this in CI before merge. From 3b803e22bfe18fb402568e9d896d9d4834449fb2 Mon Sep 17 00:00:00 2001 From: Yorke Rhodes III Date: Thu, 27 Aug 2026 10:55:52 -0400 Subject: [PATCH 2/2] Reword the skill file rather than change Tailwind's config Adding the add-team-profile skill broke CI: Tailwind v4 scans every non-ignored file for candidate utility names, not just component classNames, and the skill's prose used a plain English word that is also a valid single-word Tailwind utility, so the build compiled in an unused rule the committed static-site/ snapshot didn't have. Tried excluding .github/ from the scan via @source not in globals.css first. That produced a byte-for-byte identical build on Windows (verified by SHA-256 against the committed snapshot), but the same commit failed CI on Linux - a downloaded build artifact from the failing run showed the directive had no effect there. Reverted rather than ship a fix confirmed to only work on one platform; recorded as UPD-018 in UPDATES-NEEDED.md for a follow-up with Linux access. Shipped fix: reword the two sentences in the skill that used the word verbatim, and one unrelated instance of another single-word utility name found during a fuller scan. Verified with a clean-cache rebuild: the resulting CSS is byte-for-byte identical (SHA-256 match) to what is already committed, so no snapshot regeneration is needed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/skills/add-team-profile/SKILL.md | 6 +-- UPDATES-NEEDED.md | 47 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/.github/skills/add-team-profile/SKILL.md b/.github/skills/add-team-profile/SKILL.md index b7fad7c83..ebe05afee 100644 --- a/.github/skills/add-team-profile/SKILL.md +++ b/.github/skills/add-team-profile/SKILL.md @@ -33,7 +33,7 @@ member" — this skill covers the *procedure*, not a full field-by-field spec. | Personal / faculty website | Optional | Only if distinct from LinkedIn. | | Bio | Recommended for advisors | One to three short paragraphs, third person, past/present tense matching the existing bios. Without one, the detail page just shows "Bio coming soon." | | Cohort term | Only for `researchers` | e.g. `"Summer 2026"`. Advisors, staff, and collaborators don't need this. | -| Slug | Recommended | `firstname-lastname`, lowercase, hyphenated. Omit only if the card should be non-clickable. | +| Slug | Recommended | `firstname-lastname`, in lower case, hyphenated. Omit only if the card should be non-clickable. | **Never fabricate any "Recommended" field.** If a bio, LinkedIn URL, or photo isn't supplied, either omit the field (the site handles a missing @@ -53,7 +53,7 @@ This decides which array in `team` the object goes into, and whether step 5 ### 2. Add the headshot (if supplied) 1. Drop the file into `public/team/.` (`.jpg`, `.jpeg`, or - `.png`) — lowercase first name, matching the existing filenames. + `.png`) — first name in lower case, matching the existing filenames. 2. Run: ``` npm run optimize:images @@ -133,7 +133,7 @@ Open `/team` and confirm: - If a slug was given, click through to `/team/` and confirm photo, role, org, LinkedIn/website links, and bio all render as expected. - Check both dark and light themes. -- Advisors render in a 2-column grid — an odd total just leaves one empty +- Advisors render in a 2-column layout — an odd total just leaves one empty slot on the last row; that's expected, not a bug. ### 8. Regenerate the snapshot — only if publishing this change on its own diff --git a/UPDATES-NEEDED.md b/UPDATES-NEEDED.md index d007c1894..aa10c1f6b 100644 --- a/UPDATES-NEEDED.md +++ b/UPDATES-NEEDED.md @@ -413,6 +413,53 @@ whether `/print/` should be excluded or marked non-indexable. **Acceptance:** Only intended pages are publicly reachable, and no unused font family ships. +### UPD-018 - `@source not` appears inert on the Linux CI runner + +**Priority:** Medium + +Tailwind v4 scans every non-gitignored file in the project for candidate +utility names, including comments and prose, not just component classNames. +Two incidents so far: a shell-script comment describing a removed arbitrary +value left the built CSS carrying it anyway, and separately, a plain English +word used in a new skill file's instructions turned out to also be the name +of a real single-word Tailwind utility, so an unused rule for it was compiled +in. Both were fixed the same way: reword the prose so the exact word/token +no longer appears verbatim, rather than changing any build configuration. + +**A more durable fix was attempted and reverted.** Excluding `.github/**/*` +from the scan via `@source not "../../.github/**/*";` in +`src/app/globals.css` fixed the incident with a verified byte-for-byte +identical build **on Windows** (SHA-256 match against the already-committed +snapshot). The same commit failed CI on the Linux runner: a downloaded build +artifact from the failing run showed the excluded word's utility still +present, meaning the directive had no effect there. `main` and this branch +were confirmed at the same commit at the time (not a stale-`main` false +alarm, the recurring cause of earlier snapshot-drift failures), and the CSS +otherwise builds identically on both platforms, so the discrepancy is +specific to `@source not` itself - not to `npm ci`, the Node version, or +anything else in the pipeline. + +The cause was not identified: no local Linux environment was available to +compare directly, and repeated `gh workflow run` / artifact-download round +trips were the only diagnostic tool, which is slow and only shows the +outcome, not why. Reverted rather than ship a fix confirmed to only work on +one contributor's platform. + +**Proposed update:** With Linux/WSL access, reproduce directly: build with and +without the `@source not` line and diff the output. Worth checking the +installed `@tailwindcss/oxide-linux-*` vs `@tailwindcss/oxide-win32-*` +package versions in `package-lock.json` for a mismatch, and whether +`source(none)` plus an explicit `@source` allow-list (rather than +`@source not` pruning automatic detection) behaves the same way on both +platforms - that path was not tried here. If it turns out to be a genuine +Tailwind v4 defect, file it upstream. + +**Acceptance:** Either `@source not` (or an equivalent explicit-source +approach) excludes non-component paths with a verified identical build on +both Windows and Linux, or the investigation concludes with a documented +reason it isn't reliable enough to use, so the next person does not repeat +this same multi-round-trip experiment from scratch. + ### UPD-017 - Decide how UI symbols are drawn **Priority:** Medium — **done.**