From 538a17ec4c84577e8f5ade29152e4d8b9b4d20a5 Mon Sep 17 00:00:00 2001 From: DVidal1205 Date: Wed, 12 Aug 2026 15:00:17 -0700 Subject: [PATCH 1/8] document blade refinements bundle Co-authored-by: Codex --- .forge/features/blade-refinements/spec.md | 245 +++++++++ .forge/features/blade-refinements/srd.md | 291 +++++++++++ .forge/features/blade-refinements/status.md | 181 +++++++ .../features/blade-refinements/test-cases.md | 471 ++++++++++++++++++ 4 files changed, 1188 insertions(+) create mode 100644 .forge/features/blade-refinements/spec.md create mode 100644 .forge/features/blade-refinements/srd.md create mode 100644 .forge/features/blade-refinements/status.md create mode 100644 .forge/features/blade-refinements/test-cases.md diff --git a/.forge/features/blade-refinements/spec.md b/.forge/features/blade-refinements/spec.md new file mode 100644 index 000000000..614c7aa8f --- /dev/null +++ b/.forge/features/blade-refinements/spec.md @@ -0,0 +1,245 @@ +# Blade Refinements Spec + +Status: Complete — approved for implementation + +## User-facing purpose + +Blade should feel like one coherent Knight Hacks product rather than a collection +of admin pages and a separate mobile member experience. Navigation should be +deliberate, compact, and understandable; member pages should prioritize the next +useful action; admin pages should spend their space on the actual workspace; and +common failures should explain the real problem without disrupting the user. + +This refinement slice also closes several concrete production defects, improves +issue collaboration and attribution, adds managed screenshots to issue +descriptions, and completes the Forms presentation model with a top banner image. + +## Users / actors + +- Ordinary authenticated members, including dues-paid and unpaid members. +- Members completing initial signup or editing an existing profile. +- Administrators with one or more Blade capabilities. +- Officers with access to all administration destinations. +- Form editors and authenticated form respondents. +- Issue readers, editors, assignees, and Discord reminder recipients. +- System actors represented in issue history or the Admin log. +- Unauthenticated visitors to the public Blade landing page. + +## User-visible interface + +### Public landing and account chrome + +- `/` remains the public Blade landing page whether or not the visitor is + signed in. Its primary call to action adapts to the session: sign in when + unauthenticated and go to the member dashboard when authenticated. +- An ordinary member who has only Dashboard and Settings does not see a sidebar. + The authenticated chrome shows a Settings cog and Sign out together at the + top right. +- The product mark remains a recognizable route back to the public landing page. + +### Administrator navigation + +- Desktop administration uses a compact icon rail with an opener in the top-left + corner. Hover and focus do not expand it. +- Every collapsed icon is directly clickable and has an accessible name and + tooltip, so a user who recognizes it can navigate without opening the rail. +- Clicking the opener reveals labels and section headings. Selecting any + destination closes the expanded rail after navigation. +- Mobile retains its current drawer behavior and closes after a selection. +- External destinations use a consistent external-link marker and accessible + wording. This includes Guild in navigation and outbound Guild/social/profile + actions. +- Dashboard remains first and ungrouped. The administration map is: + - **Club:** Analytics, Members, Alumni, Companies, Events, and Event Check-in. + - **Team:** Issues, Forms, Email, Roles, Discord archive, and Admin logs. + - **Hackathon:** Hackathons, Hackers, Hackathon Events, and Hackathon Check-in. + - **External:** Guild. +- Settings and Sign out remain account utilities rather than administration + destinations. + +### Administration page hierarchy + +- A top-level admin page shows its title and relevant actions without the + visible purple eyebrow or a permanently visible descriptive paragraph. +- A title-adjacent accessible information control preserves useful page context + by placing the current page description in a tooltip. The former eyebrow is + not carried into the tooltip. +- Loading states do not reserve vertical space for the removed lines. +- Configuration panels remove subtitles that merely repeat the panel title or + available choices. Copy that explains consequences, permissions, payment, + deletion, or another non-obvious fact stays visible. + +### Member dashboard and Guild + +- Mobile and desktop present the same member sections, order, labels, and + available actions. Layout may reflow, but neither viewport is a separate + product. +- Guild remains a prominent, top-level part of the dashboard. Profile-picture, + resume, visibility, and Guild preference controls are not moved away solely + because they mutate data. +- The Guild card briefly explains that Guild is Knight Hacks' public member + directory/profile, clearly distinguishes public Guild content from private + Blade content, and marks the public Guild profile action as external. +- Avatar, visibility, name, tagline, company, links, resume controls, and empty + states remain aligned with missing as well as populated data. +- The dashboard includes a compact **Check in** surface with a **View QR code** + action on both mobile and desktop. +- An unpaid member sees a prominent dues banner near the top with a clear + payment action. +- A paid member does not see the dues banner. A compact green paid badge appears + beside `Welcome, ` and its hover/focus tooltip confirms that dues + are paid. +- Previous forms remains reachable at the bottom of the dashboard as a small, + low-emphasis history action rather than a primary tile. + +### Profile and career feedback + +- Uploading or replacing a resume during signup or from an existing profile + confirms success without automatically opening the document viewer. +- The success state offers an explicit **View** action. +- Removing a saved profile picture requires confirmation. Cancelling keeps the + picture and its saved state unchanged. +- Employment editing marks required fields, associates an error with the + precise employment entry and field, and focuses or scrolls to the first + problem. +- Legacy employment records still receive the intentional migration/confirmation + guidance, but a missing position title, experience type, company, or status is + never mislabeled as a different error. + +### Issues and audit identity + +- Typing in admin member search remains focused while results and URL state + update. +- Filtering Issues by assignee loads a valid result or a truthful empty state; + it never replaces the workspace with the generic "Issues could not be loaded" + failure for a valid selection. +- Author-entered line breaks remain visible in issue description preview and + detail views while Markdown continues to work. +- Issue editors can add managed screenshot images to a description through a + file picker, clipboard paste, or drag and drop. The image is inserted at the + description cursor. +- Managed issue images accept PNG, JPEG, WebP, and GIF files up to 10 MB each. + An issue retains at most 10 managed images. Animated GIFs are allowed and SVG + is rejected. +- Uploaded issue images appear in preview and detail, follow issue access, have + editable alternative text, and can be removed by an authorized editor. +- Issue history and Admin log surfaces prefer the linked member's current full + name. If no linked Member profile can be resolved, they fall back to the saved + Discord display name or username. Explicit system actors remain system actors. +- Existing history and log rows benefit from the same display rule; their saved + historical snapshots are not rewritten. + +### Issue reminders + +- Every issue reminder presents the Blade destination as the linked issue + title. +- When the issue has a Discord discussion thread, the title is followed by + ` | Chat`, with **Chat** linked to that thread. +- Without a thread, only the linked title is shown. Mentions, reminder cadence, + delivery idempotency, and message safety remain unchanged. + +### Forms presentation + +- Existing form instruction blocks continue to support text, uploaded images, + and browser-playable uploaded video. Editors can see that capability, upload + media, remove it, and verify its respondent presentation without needing + hidden knowledge of the builder. +- A form editor can upload, replace, or remove one banner image for a form. +- The banner appears once at the top of the respondent experience, above the + form title/description/instructions, in a responsive 4:1 frame with `cover` + cropping. The builder provides crop guidance, preview, and editable alt text. +- Draft preview and the published respondent view use the same banner behavior. +- Banner and instruction media are visible only to actors allowed to view that + form; possession of an object URL does not bypass form access. + +### Responsive and chart readability + +- Member surfaces remain usable at 320 px, common mobile widths, intermediate + desktop widths, and full desktop widths in Chromium-family browsers. +- Long member names, taglines, companies, URLs, event names, and empty-state + sentences wrap or truncate intentionally without being clipped. +- Nearby current/prior deadline labels on hackathon comparison charts remain + distinguishable. The existing textual/table alternative remains available. + +## Scope + +### In scope + +- Public landing behavior for authenticated visitors. +- Member-only header without a sidebar; explicit desktop admin rail behavior; + grouped admin information architecture; mobile close-on-select preservation. +- Central admin header compaction and repetitive configuration-copy cleanup. +- Unified member dashboard hierarchy, Guild explanation, external-link cues, + check-in surface, paid/unpaid dues treatment, and demoted form history. +- Resume success flow, photo-removal confirmation, employment validation, member + search focus, issue-assignee failure, issue newlines, responsive overflow, and + analytics label collisions. +- Managed images in issue descriptions. +- Existing form image/video instruction support verification and discoverability. +- One managed top-of-form banner image. +- Issue reminder `Title | Chat` presentation. +- Member-full-name resolution in issue history and Admin logs with Discord + fallback. + +### Out of scope + +- A Knight Hacks member-benefits page or benefits content. +- Moving all Guild/profile mutations into Settings or making the dashboard + profile read-only. +- Replacing Blade analytics with Grafana or adding observability infrastructure. +- Uploaded video in issue descriptions; arbitrary issue file attachments beyond + the managed image formats approved for this slice. +- Anonymous or public Forms behavior beyond the existing Forms platform. +- A new open-forms directory. +- Redesigning Discord reminder cadence, mentions, or delivery infrastructure. +- A browser-specific Zen workaround when standards-compliant responsive behavior + is already correct; Chrome and Zen reports are both valid reproduction inputs. + +## Vocabulary + +- `Blade`: Knight Hacks' member and administration product. +- `Guild`: Knight Hacks' public member directory and public-profile surface, + distinct from private Blade member data. +- `Icon rail`: the collapsed desktop administration navigation. +- `Instruction block`: ordered form guidance containing text, an image, or a + browser-playable video. +- `Form banner`: one editor-managed image displayed above a form's respondent + content. +- `Managed issue image`: an uploaded image owned and authorized by the issue + system rather than a permanently public third-party URL. +- `Member full name`: the linked Member profile's first and last name, not the + authentication provider/Discord username. +- `Saved actor snapshot`: the immutable fallback label recorded when a history + or audit event was written. + +## Acceptance criteria + +- Ordinary members can reach Dashboard, Settings, Sign out, and the public + landing page without seeing an otherwise two-item sidebar. +- Admins can activate collapsed icons directly, explicitly expand the rail, + understand grouped destinations, and observe it close after selection without + hover causing layout changes. +- Top-level admin workspaces reclaim the eyebrow/description height while useful + context remains keyboard- and screen-reader-accessible. +- Mobile and desktop member dashboards expose the same hierarchy and actions, + with Guild prominent and plainly defined. +- Unpaid members receive the prominent payment CTA; paid members see only the + compact paid badge beside their welcome name. +- Resume uploads across signup and existing-member flows confirm success and wait + for the user to select View; profile image deletion can be cancelled safely. +- Search, issue filtering, employment validation, Markdown line breaks, + responsive content, and comparison-chart labels pass their concrete regression + cases. +- Authorized issue editors can upload, describe, render, and remove managed + screenshots without exposing them to unauthorized users. +- Issue reminders render linked `Title | Chat` when a Discord thread exists and + linked `Title` alone otherwise. +- Issue history and Admin logs show current member full names when resolvable and + truthful fallbacks otherwise, including for pre-existing rows. +- Existing form image/video instructions remain operational and understandable; + a banner image can be managed and appears correctly in preview and response + views. + +## Open questions + +- None. The product bundle is approved for implementation. diff --git a/.forge/features/blade-refinements/srd.md b/.forge/features/blade-refinements/srd.md new file mode 100644 index 000000000..d36ae1187 --- /dev/null +++ b/.forge/features/blade-refinements/srd.md @@ -0,0 +1,291 @@ +# Blade Refinements SRD + +Status: Approved for implementation — technical discovery prerequisites remain + +## Technical purpose + +Refine the current production Blade shell and member/admin surfaces without +creating parallel product architectures. Fix localized regressions at their +owning boundaries, extend the existing attachment model for issue images and a +form banner, and preserve current permission, audit, Forms, and Discord delivery +invariants. + +This bundle starts from `origin/main` commit `78857b85` on branch +`reforge/refinements` in `/Users/dvidal/Documents/forge-refinements`. + +## Relevant principles + +- Follow `docs/agentic-development/forge-engineering-principles.md`: make access + policy explicit, keep apps thin, place business logic in `@forge/api`, validate + at boundaries, preserve external-side-effect idempotency, and keep clients + responsive but non-authoritative. +- Follow `docs/agentic-development/frontend-design.md` and + `apps/blade/DESIGN_SYSTEM.md`: reuse Blade primitives, preserve the grid-shell + visual language, test 320 px and long-content states, and keep focus behavior + deliberate. +- This bundle supersedes the hover/focus desktop-rail decision in + `admin-member-dashboard`, the intentionally divergent mobile composition in + `mobile-member-experience`, and the prominent paid-dues/desktop-QR placement + decisions where they conflict with this spec. It does not erase their other + accepted behavior. +- The existing `forms-and-event-feedback` attachment and instruction-media + contracts remain authoritative. Extend them rather than build a second upload + system. +- The existing `club-operations-issues` contracts remain authoritative except + that native managed **image** attachments are now explicitly in scope and the + reminder link label changes from `Discuss` to `Chat`. + +## Access policy + +- `/` remains available without authentication. Session state changes its CTA, + not its visibility. +- Member Dashboard, Settings, resume/photo operations, QR, dues, previous forms, + and Guild preferences retain their current authenticated-member access. +- The shell derives ordinary-member versus admin navigation from the same + server-authoritative capability projection used today. Hiding the sidebar is + presentation only and grants no access. +- Admin routes and procedures retain their existing capability requirements. +- Issue image create/finalize/remove/read operations require the same issue edit + or read access respectively as the owning issue. A link or guessed attachment + ID is insufficient. +- Form banner upload/finalize/remove requires existing form edit access. Banner + read requires the same respondent/editor visibility required to read the + owning form in its current state. +- Issue history and Admin log reads retain their existing permissions. Name + enrichment must not expose Member data to a caller who cannot read the row. +- System actors stay nullable/non-member actors and never resolve through a + coincidental display-name match. + +## Architecture / data flow + +### Blade shell and page hierarchy + +- Keep Next.js page modules server-first. The server computes the session, + current member, and authorized admin navigation projection; focused client + components own transient rail/drawer state. +- Replace CSS `hover`/`focus-within` expansion in `AuthenticatedShell` with an + explicit desktop state. Do not persist the expanded state: selecting a link + closes it by product definition, and each full navigation begins collapsed. +- Model navigation as destination groups rather than infer sections from labels + in render components. Filter unauthorized children first and omit empty groups. +- Ordinary members render no desktop/mobile nav rail. Reuse the shared account + controls for the top-right Settings and Sign out actions. +- Link the product mark to `/`; remove the authenticated redirect from the root + page and select CTA copy/href from the session. +- Centralize compact title/help behavior in `AdminPageHeader`. Consumers continue + to provide meaningful descriptions during migration; the shared component + exposes the description alone through an accessible tooltip, does not project + the old eyebrow copy, and removes obsolete skeleton rows. + +### Member dashboard and localized regressions + +- Refactor duplicated mobile/desktop dashboard blocks into shared semantic + sections rendered once and laid out responsively. Keep Guild prominent and its + current mutation procedures authoritative. +- Derive paid/unpaid rendering from the existing dues status query. The paid + badge's tooltip is accessible by hover and keyboard focus; unpaid payment + behavior is unchanged. +- Resume upload mutations return/use the existing successful result but set an + inline success state instead of opening the viewer. The explicit View control + opens the existing preview. +- Gate saved-photo removal behind the shared confirmation dialog; do not issue + the mutation before confirmation. +- Remove the `key={input.query}` remount boundary from the admin members page and + preserve debounced URL/result updates without replacing the active input. +- Split legacy employment normalization errors from current entry validation. + Map Zod issues to entry/field UI and retain a form-level summary for assistive + technology. +- Apply issue line-break styling at the issue description renderer/preview + boundary rather than changing every `MarkdownContent` consumer. +- Reproduce the issue-assignee failure against the production-base code before + selecting a fix. Add a regression at the smallest failing UI/API boundary. +- Resolve comparison-chart label collisions through deterministic placement, + abbreviated labels, staggering, or collision-aware rendering; do not remove + the existing text alternative. + +### Managed issue images + +- Reuse the Forms attachment service/object-storage conventions where practical: + create intent, direct upload, finalize after metadata verification, reference + from the owning entity, authorize short-lived read, and durably clean abandoned + or removed objects. +- `@forge/api` owns issue access checks, upload lifecycle, ownership, cleanup + scheduling, and history/audit semantics. Blade only requests intents, uploads + bytes, finalizes, and inserts the returned stable attachment reference into the + description model. +- `@forge/validators` owns image MIME/size/count/alt-text inputs. Server-side + checks are authoritative and verify stored object metadata at finalization. +- Accept PNG, JPEG, WebP, and GIF up to 10 MB per image and at most 10 retained + managed images per issue. Allow animated GIFs and reject SVG. +- Blade supports file selection, clipboard paste, and drag/drop through one + upload pipeline, then inserts the finalized managed reference at the current + description cursor. +- Prefer an additive generic attachment row/ownership relation if the current + Forms model supports a safe new owner type. Do not store data URLs or permanent + public bucket URLs in issue Markdown. +- The rendered description resolves a managed attachment reference only after + issue-read authorization and returns a short-lived/download response. Sanitize + ordinary external Markdown URLs under the existing Markdown policy. +- Removing an image removes its description reference immediately and schedules + object cleanup only when no retained issue revision/reference owns it. Abandoned + intents expire. Issue history records a safe structural image change without + embedding signed URLs or file bytes. + +### Form banner and existing instruction media + +- The current `formDefinitionSchema.instructions` image/video blocks, builder + upload procedures, and authorized respondent rendering already satisfy the + instruction-media capability. Add focused regression coverage and improve + discoverability only; do not revive the unrelated legacy + `FormSchemaValidator.banner` URL field. +- Extend the active forms-platform definition with an optional managed banner + reference, ideally an attachment ID plus alt text. Reuse the existing form + attachment create/finalize/read/cleanup pipeline with a distinct `banner` + purpose. +- Form create/update validation verifies the banner belongs to the same form, + was finalized by an authorized editor, and is an allowed image. Replacing or + removing it schedules the old unreferenced object for cleanup. +- Draft preview and respondent acquisition both project the same authorized + banner model in a responsive 4:1 `cover` frame. The builder provides crop + guidance, preview, and editable alt text. Do not expose a raw object name, + storage key, or durable public URL to the client. +- A schema migration may be avoidable if the versioned JSON definition owns the + banner reference; confirm the current persisted form revision model before + changing `@forge/db`. Any required migration must be additive and separately + documented in the implementation plan/status before execution. + +### Actor display enrichment + +- Issue history and Admin log queries should batch-resolve non-null linked Member + IDs and project `firstName + lastName` when both/available name fields produce a + non-empty display value. +- Fall back to the immutable stored actor snapshot when no linked Member exists, + the profile was removed, or the resolved name is empty. Preserve explicit + system labels. +- Resolve at read time so historical rows improve without mutation or migration. + Avoid N+1 queries and do not join actors by Discord username/text. + +### Discord reminder projection + +- In `@forge/api` reminder presentation, render the sanitized issue title as the + Blade link and append a literal separator plus linked `Chat` only when a valid + thread URL is present. +- Preserve current Components V2 limits, allowed mentions, role/user pings, + grouping, delivery ledger, stable nonce/idempotency, and no-live-write test + defaults. + +## tRPC/API behavior + +- Existing procedures should remain backward-compatible unless a new result + field is required for attachment/banner projection. +- Add issue-image intent/finalize/remove/read procedures under the issue router or + shared attachment service, each with `.meta({ description })`, shared Zod input, + permission checks before object work, and non-enumerating `NOT_FOUND` behavior + where appropriate. +- Extend existing Forms upload procedures with `purpose: "banner"` rather than + creating an unrelated endpoint. Existing `instruction` and respondent-file + purposes remain unchanged. +- Name-enriched history/log results may add/replace the presentation label while + retaining stored snapshot data internally for fallback. Do not make clients + assemble Member names. +- The issue assignee filter fix must preserve pagination and all other filters; + capture the failing query before changing it. +- Business logic remains tRPC/platform logic. Do not add REST business endpoints. + +## Validation + +- Use shared Zod validators for navigation group shape where shared, active form + definition/banner data, attachment lifecycle inputs, image policy, and actor + projection outputs. +- Keep client file `accept` hints aligned with server policy, but validate size, + MIME, owner, authorization, and finalized state on the server. +- Normalize and reject empty alt text for managed images unless the approved UI + explicitly supports a decorative-image flag. +- Preserve existing issue title/description, employment, Forms, and reminder + limits. Newline presentation does not alter stored description text. +- Validate all external/thread URLs before Discord projection and escape user + text under existing message safety rules. + +## Data / migration / compatibility + +- Navigation, headers, member composition, local regressions, and reminder label + changes require no data migration. +- Actor display enrichment is read-time only and preserves all immutable history + and audit rows. +- Managed issue images require durable owner/reference metadata and cleanup + state. Prefer extending the existing attachment schema; if a new owner relation + or enum requires a migration, make it additive and keep old issues readable. +- Form banner references should live in the active versioned definition when that + maintains historical response rendering. Never reuse the stale legacy banner + URL shape as authority without a compatibility analysis. +- Old forms with no banner and old issues with external Markdown images remain + readable. Rollback hides new authoring controls but must not corrupt retained + references or make existing issue/form pages fail. + +## Discord integration + +- No role, permission, guild, cadence, or channel configuration changes. +- Guild links in Blade are visually identified as external, but Guild remains an + intentional prominent member feature. +- Reminder content changes only from the previous secondary link wording/layout + to linked `Title | Chat` when a thread exists. +- Actor name presentation in Blade does not rewrite Discord messages or Discord + identities. + +## Configurability review + +Would this require a developer change next year? + +- Answer: Navigation groups and stable platform destinations remain code-owned; + form banners, instruction media, issue images, dues state, profile content, and + names are data-driven. +- Hard-coding the information architecture is acceptable because destinations + and permission enums ship with code. Adding a new admin domain should require + assigning its route to a group in the same navigation registry. +- Upload limits should use shared named policy constants rather than scattered UI + literals once the human approves them. + +## React / frontend constraints + +- Do not add `"use client"` to route pages. Keep transient rail, tooltip, dialog, + upload, and focus behavior in the smallest existing client component. +- Use existing `@forge/ui` Button, Tooltip, Dialog/AlertDialog, Badge, Skeleton, + and Markdown primitives. Promote a new primitive only if a second real consumer + justifies it. +- Preserve semantic navigation, `aria-current`, accessible icon labels, keyboard + activation, focus visibility, tooltip focus support, dialog focus return, and + reduced-motion behavior. +- No hover-only action may be required. Rail opener, collapsed destinations, + paid tooltip, info tooltip, uploads, and removal remain keyboard-accessible. +- Avoid parallel mobile/desktop component trees for the member dashboard. Use + responsive CSS around one semantic composition. +- Test long strings and 320 px; do not rely on `overflow-x-hidden` to conceal + layout bugs. +- Reserve media geometry to avoid layout shift and provide loading, broken-media, + upload progress, success, and failure states. + +## Testing / verification strategy + +- Component/unit tests in `apps/blade` for header semantics, navigation state and + grouping, member dues/dashboard composition, resume/photo behavior, employment + errors, member-search focus, issue Markdown/images, Forms banner/instruction + media, and responsive class/DOM contracts. +- `@forge/api` integration/unit tests for issue filter reproduction, attachment + authorization/lifecycle, form banner ownership, actor enrichment, and reminder + formatting/idempotency preservation. +- Validator tests for managed-image and active form-definition banner shapes. +- Playwright desktop, intermediate-width, and 320 px journeys with long-content + fixtures, ordinary member/admin actors, paid/unpaid states, and authorized/ + unauthorized media access. +- Run focused package tests while implementing; then React analyzer for changed + frontend surfaces, `pnpm verify:precommit`, relevant reviewers selected from + the diff, and final `git diff --check`. +- No live Discord write during automated tests. Any staging reminder smoke must + be separately intentional and retain the approved no-ping safety path. + +## Open questions + +- Before implementation, confirm whether the active attachment schema can accept + Issue and Form-banner owner types without a database migration. +- Reproduce and record the exact issue-assignee failure before selecting its + repair. diff --git a/.forge/features/blade-refinements/status.md b/.forge/features/blade-refinements/status.md new file mode 100644 index 000000000..542b08a98 --- /dev/null +++ b/.forge/features/blade-refinements/status.md @@ -0,0 +1,181 @@ +# Blade Refinements Status + +Current phase: Bundle approved / ready for technical discovery + +## Decision log + +- 2026-08-12: Start from production `origin/main`, not `reforge/main` or an + existing dirty worktree. Created `/Users/dvidal/Documents/forge-refinements` + on `reforge/refinements` at `78857b85`. +- 2026-08-12: Use one `blade-refinements` bundle for the long refinement slice; + the human explicitly waived the preference to split unrelated small changes. +- 2026-08-12: Desktop admin navigation uses a top-left explicit opener, never + hover expansion. Collapsed icons navigate directly; an expanded rail closes + after selection. Mobile retains close-on-select. +- 2026-08-12: Ordinary members with only Dashboard and Settings get no sidebar; + Settings and Sign out live together at the top right. +- 2026-08-12: Admin destinations are grouped by product domain rather than + alphabetical order. The approved map is Club (Analytics, Members, Alumni, + Companies, Events, Event Check-in), Team (Issues, Forms, Email, Roles, Discord + archive, Admin logs), Hackathon (Hackathons, Hackers, Hackathon Events, + Hackathon Check-in), and External (Guild), with Dashboard first and ungrouped. +- 2026-08-12: `/` stays public for authenticated visitors and adapts its CTA. +- 2026-08-12: Unpaid dues stays prominent at the top. Paid dues becomes a green + badge beside the Welcome name with a hover/focus tooltip; no paid banner. +- 2026-08-12: Guild remains a prominent top-level member capability, and its + profile/photo/resume/preferences controls are not moved into Settings as part + of this slice. The dashboard must instead explain what Guild is and which data + is public/external. +- 2026-08-12: Resume upload success must not auto-open preview and applies to + signup plus existing-member flows. Success retains an explicit View action. +- 2026-08-12: Previous forms remains at the bottom as a small, low-emphasis + action. +- 2026-08-12: Managed issue screenshot uploads are in scope despite the earlier + `club-operations-issues` non-goal. The approved editor supports file picker, + clipboard paste, and drag/drop with cursor insertion. It accepts PNG, JPEG, + WebP, and GIF up to 10 MB each and 10 retained images per issue; animated GIFs + are allowed and SVG is rejected. +- 2026-08-12: Forms already supports managed image/video instruction blocks in + the active production platform. This slice verifies/polishes that behavior and + adds the genuinely missing managed top banner image. +- 2026-08-12: Issue reminders use linked `Title | Chat`; Chat is omitted when no + Discord thread exists. +- 2026-08-12: Issue history and Admin logs prefer linked current Member full + names at read time, fall back to saved Discord identity, preserve system actors, + and do not rewrite historical rows. +- 2026-08-12: Member benefits and Grafana are deferred. +- 2026-08-12: Remove visible admin-page eyebrows/descriptions centrally. Preserve + the current description alone through an accessible title tooltip, omit the + eyebrow from that tooltip, and reclaim matching skeleton space. +- 2026-08-12: Treat the Chrome and Zen clipping reports as responsive defects; + do not dismiss them as browser-specific without standards-compliant evidence. +- 2026-08-12: Form banners use a responsive 4:1 `cover` frame with builder crop + guidance, preview, and editable alt text. +- 2026-08-12: The human approved all five remaining product decisions and the + spec/SRD/test-case bundle for implementation. + +## Open questions + +1. Technical discovery: confirm whether the existing attachment schema can gain + Issue/Form-banner owners without a database migration. +2. Technical reproduction: capture the exact production-base issue-assignee + filter failure before selecting a repair. + +## Contributor coordination + +- `reforge/refinements` is the shared integration branch and bundle source of + truth. Create a short-lived work branch from it for an implementation slice; + do not push implementation commits straight to the integration branch unless + the maintainer assigns that workflow. +- Before editing, announce the refinement IDs you intend to claim and your work + branch in the shared development thread. Check the latest remote status and + recent commits first. +- Replace `Unclaimed` below with your name/handle, work branch, and claim date. + If someone already owns an overlapping row or file set, coordinate before + continuing. +- Keep changes limited to the claimed rows. If implementation reveals a spec, + SRD, test-case, access, upload, or schema conflict, stop and record it under + Open questions instead of silently changing the approved contract. +- Update each row to `In progress`, `Blocked`, or `Complete` as work moves. Add + focused checks, screenshots/video, and PR links to this file before handoff. + +## Refinement inventory + +| ID | Refinement | State | Claim | Proof | +| ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | --------- | ------------------------------ | +| R-01 | Keep `/` public for signed-in users, adapt its CTA, and make the product mark return there. | Ready | Unclaimed | TC-001 | +| R-02 | Remove the sidebar for ordinary members; place Settings and Sign out together at the top right. | Ready | Unclaimed | TC-002 | +| R-03 | Replace hover expansion with a top-left admin rail opener; keep collapsed icons clickable, close after selection, and preserve mobile close-on-select. | Ready | Unclaimed | TC-003, TC-NEG-001 | +| R-04 | Group admin destinations into the approved Club, Team, Hackathon, and External map; omit empty groups and mark Guild/outbound destinations as external. | Ready | Unclaimed | TC-004 | +| R-05 | Remove visible admin eyebrows/descriptions, expose description-only title help, and shrink matching skeletons. | Ready | Unclaimed | TC-005 | +| R-06 | Remove repetitive configuration subtitles while preserving consequential guidance. | Ready | Unclaimed | TC-005 | +| R-07 | Use one member-dashboard hierarchy and action set across mobile and desktop. | Ready | Unclaimed | TC-007, TC-020 | +| R-08 | Keep Guild prominent and editable; define Guild, separate public Guild data from private Blade data, and mark its public actions as external. | Ready | Unclaimed | TC-007, TC-008 | +| R-09 | Replace the isolated QR action with a compact Check in surface and View QR code action on every viewport. | Ready | Unclaimed | TC-007 | +| R-10 | Keep unpaid dues prominent; replace the paid tile with a green paid badge and accessible tooltip beside the Welcome name. | Ready | Unclaimed | TC-006 | +| R-11 | Keep Previous forms as a small, low-emphasis action at the bottom of the dashboard. | Ready | Unclaimed | TC-007 | +| R-12 | Align sparse and populated Guild/profile content and handle long names, links, companies, filenames, events, and empty states without clipping. | Ready | Unclaimed | TC-008, TC-020 | +| R-13 | Change resume upload/replace in signup and existing-member flows to success plus explicit View, without automatic preview. | Ready | Unclaimed | TC-009 | +| R-14 | Require confirmation before removing a saved profile picture. | Ready | Unclaimed | TC-010 | +| R-15 | Mark employment fields required and report/focus the precise invalid entry and field without mislabeling legacy validation. | Ready | Unclaimed | TC-011 | +| R-16 | Preserve admin member-search focus and keystrokes while debounced results and URL state update. | Ready | Unclaimed | TC-012, TC-NEG-001 | +| R-17 | Reproduce and fix the Issue assignee filter failure without breaking other filters, pagination, or access policy. | Discovery | Unclaimed | TC-013 | +| R-18 | Preserve author-entered issue-description line breaks in preview/detail without changing unrelated Markdown consumers. | Ready | Unclaimed | TC-014 | +| R-19 | Add authorized managed issue images through picker, paste, and drag/drop with cursor insertion, alt text, approved limits, rendering, removal, and cleanup. | Discovery | Unclaimed | TC-015, TC-NEG-002, TC-NEG-003 | +| R-20 | Prefer linked current Member full names in Issue history and Admin logs; fall back to stored Discord labels and preserve system actors. | Ready | Unclaimed | TC-016, TC-NEG-003 | +| R-21 | Render issue reminders as linked `Title \| Chat` when a Discord thread exists and linked title alone otherwise. | Ready | Unclaimed | TC-017 | +| R-22 | Prevent overlapping current/prior hackathon comparison labels while preserving the accessible text/table alternative. | Ready | Unclaimed | TC-021 | +| R-23 | Fix reported Chrome/Zen member and shell overflow at 320 px, intermediate widths, and desktop without hiding content behind overflow rules. | Ready | Unclaimed | TC-020 | +| R-24 | Verify and polish existing Forms text/image/video instruction-card authoring, upload feedback, ordering, cleanup, and respondent rendering. | Ready | Unclaimed | TC-018, TC-NEG-003 | +| R-25 | Add one managed form banner with upload/replace/remove, editable alt text, preview guidance, and responsive 4:1 `cover` presentation. | Discovery | Unclaimed | TC-019, TC-NEG-002, TC-NEG-003 | +| R-26 | Knight Hacks member-benefits content/page. | Deferred | Unclaimed | Out of scope | +| R-27 | Grafana analytics replacement or observability infrastructure. | Deferred | Unclaimed | Out of scope | + +## Task list + +- [x] Fetch current `origin/main` and create the isolated worktree/branch. +- [x] Review repository skills, engineering principles, design guidance, existing + feature artifacts, screenshots, issue reports, and production code paths. +- [x] Instantiate `.forge/features/blade-refinements/`. +- [x] Record the human's eight scoping/product decisions and later identity/ + reminder asks. +- [x] Discover that form image/video instruction media already exists and isolate + form banner as the new capability. +- [x] Draft `spec.md`, `srd.md`, and `test-cases.md`. +- [x] Resolve the five focused product questions and amend all artifacts. +- [x] Human approves the artifact bundle before implementation/test generation. +- [ ] Reproduce issue-assignee filter failure and document evidence. +- [ ] Inspect attachment schema compatibility and document migration/no-migration + decision before any schema change. +- [ ] Contributors claim refinement IDs and implementation branches before + editing. +- [ ] Implement claimed slices in checkpoints and keep the inventory current. +- [ ] Run focused verification, React analysis, `pnpm verify:precommit`, derived + reviewers, browser QA, and `git diff --check`. + +## Validation / commands + +- `git fetch origin main`: fetched production base successfully. +- `git worktree add -b reforge/refinements /Users/dvidal/Documents/forge-refinements origin/main`: + created isolated worktree at `78857b85`. +- `/Users/dvidal/Documents/forge/node_modules/.bin/tsx scripts/create-forge-feature.ts blade-refinements "Blade Refinements"`: + created the four-file bundle. The fresh worktree has no local `node_modules`, so + the already-installed runtime from the source worktree was used without + changing dependency files. +- Code/artifact inspection: confirmed centralized admin headers, hover/focus rail, + ordinary member/admin projections, dashboard duplication, resume auto-preview, + photo deletion without confirmation, search remount, employment error collapse, + current issue reminder/thread data, actor snapshots, existing Forms instruction + uploads, and lack of an active managed Forms banner field. +- `/Users/dvidal/Documents/forge/node_modules/.bin/prettier --no-config --write +.forge/features/blade-refinements/*.md`: all four Markdown files formatted; the + repository config could not resolve from a fresh worktree without its own + `node_modules`, so this artifact-only pass used Prettier defaults. +- `git diff --check --no-index /dev/null ` for all four files: passed. +- Direct Git object verification: worktree `HEAD` and `origin/main` both resolve + to `78857b85`; that commit contains the Reforge shell, `.forge` artifacts, + issue reminder code, and Forms platform inspected by this bundle. +- 2026-08-12 approval sync: updated all four artifacts with the five approved + decisions, formatted them with Prettier, confirmed no stale product-question + placeholders remain, and reran per-file whitespace checks successfully. +- `pnpm install --frozen-lockfile`: passed in the refinement worktree; pnpm + reported only the repository's existing warning about moving `pnpm.overrides` + to the newer settings location. +- `pnpm format`: passed across all 23 packages. +- `pnpm lint`: passed across all 27 tasks with 0 errors. Existing repository + size/style warnings remain and are unrelated to this artifact-only commit. +- `pnpm typecheck`: passed across all 29 tasks. +- `pnpm verify:push`: passed after the contributor inventory and coordination + instructions were added. It reran format, lint, and typecheck successfully. + +## Links + +- Triage artifact: + `/Users/dvidal/Documents/Codex/2026-08-12/pleas/outputs/forge-refinements-triage.md` +- Worktree: `/Users/dvidal/Documents/forge-refinements` +- Branch: `reforge/refinements` +- Remote branch: `https://github.com/KnightHacks/forge/tree/reforge/refinements` +- Feature bundle: `.forge/features/blade-refinements/` +- PRs: none +- Issues: GitHub #503, GitHub #504, and the supplied Blade issue/thread context +- Discord/thread context: supplied in the Codex task on 2026-08-12 diff --git a/.forge/features/blade-refinements/test-cases.md b/.forge/features/blade-refinements/test-cases.md new file mode 100644 index 000000000..80851b3d7 --- /dev/null +++ b/.forge/features/blade-refinements/test-cases.md @@ -0,0 +1,471 @@ +# Blade Refinements Test Cases + +Status: Complete — approved for test generation and implementation + +## Scope + +These cases prove the revised Blade shell, member/admin hierarchy, localized +production regressions, issue collaboration/identity behavior, managed issue +images, form banner, existing instruction media, and responsive/chart behavior. + +They intentionally exclude a benefits page, Grafana, uploaded issue video, +arbitrary issue files, and changes to Discord cadence or mention rules. + +## Test placement plan + +- `apps/blade/src/tests`: shared header/navigation, dashboard/profile, issue, + Forms, and focused regression component tests. +- `apps/blade/src/tests/e2e`: authenticated public/member/admin journeys at 320 + px, intermediate desktop, and full desktop. +- `packages/api/src/**/__tests__`: issue filters/uploads/history projection, + form-banner lifecycle, audit actor projection, and reminder formatting. +- `packages/validators`: active form definition and managed issue-image policy. +- Expected focused commands will use package filters; final gates are React + analysis for changed surfaces, `pnpm verify:precommit`, reviewers selected from + the diff, and `git diff --check`. + +## Test cases + +### TC-001: Public root adapts to authentication + +Setup: + +- Prepare an unauthenticated browser and an authenticated ordinary member. + +Action: + +- Each opens `/` and follows the primary action. + +Expected observations: + +- Both see the public landing page. +- The unauthenticated action begins sign-in; the authenticated action opens the + member dashboard without a redirect loop. +- The authenticated product mark returns to `/`. + +### TC-002: Ordinary member chrome has no sidebar + +Setup: + +- Sign in as a member with no admin destinations. + +Action: + +- Open Dashboard and Settings at desktop and mobile widths, then sign out. + +Expected observations: + +- No icon rail or navigation drawer is present. +- Settings cog and Sign out are together at the top right and work by keyboard + and pointer. +- No admin destination is exposed or made accessible by direct requests. + +### TC-003: Desktop admin rail is explicit and closes on selection + +Setup: + +- Sign in as an admin with destinations in multiple groups. + +Action: + +- Hover and tab across the collapsed rail; activate a recognizable icon; return, + open the rail, and select a labeled destination. + +Expected observations: + +- Hover/focus never expands the rail. +- Collapsed icons expose tooltips/accessibility names and navigate directly. +- The opener expands the rail; grouped labels are visible. +- Selecting a destination closes it, active state remains understandable, and + focus is not stranded in hidden content. + +### TC-004: Navigation grouping follows access + +Setup: + +- Prepare an officer, a limited Club admin, a Hackathon-only admin, and an admin + with no entries in one approved group. + +Action: + +- Inspect desktop and mobile navigation. + +Expected observations: + +- Each actor sees only authorized destinations. +- Dashboard is first; non-empty domain headings follow the approved map; empty + headings are omitted. +- Guild and other outbound destinations are marked external. +- Mobile closes after selection. + +### TC-005: Admin page header reclaims content space accessibly + +Setup: + +- Open representative list, detail, analytics, issue, event, and configuration + admin pages plus their loading states. + +Action: + +- Inspect visually, tab to help controls, and query accessible names/descriptions. + +Expected observations: + +- No visible purple eyebrow or permanent description consumes header height. +- Title and actions remain aligned; the current page description, without the + former eyebrow, is available in an accessible tooltip. +- Skeletons do not reserve removed rows. +- Consequential configuration guidance stays visible while repetitive subtitles + do not. + +### TC-006: Paid and unpaid dashboards choose different hierarchy + +Setup: + +- Prepare otherwise equivalent paid and unpaid members. + +Action: + +- Open the member dashboard on mobile and desktop. + +Expected observations: + +- Unpaid shows the prominent top dues banner and working payment CTA. +- Paid has no dues banner and shows a compact green badge beside the Welcome + name; its hover/focus tooltip confirms payment. +- Neither state repeats an unnecessary explanatory dues sentence. + +### TC-007: Member sections are consistent across viewports + +Setup: + +- Prepare a member with Guild data, events, a resume, and previous forms. + +Action: + +- Compare 390 px and desktop presentations and use every available action. + +Expected observations: + +- Both expose the same ordered sections, labels, and actions. +- Guild stays prominent and is defined as the public member directory/profile; + public links are external-marked. +- Check in is a clear box with View QR code. +- Previous forms is a small bottom action, not a primary tile. + +### TC-008: Guild/profile content handles missing and long values + +Setup: + +- Prepare one sparse member and one member with long name, tagline, company, + portfolio URL, social values, and resume filename. + +Action: + +- Open the dashboard across tested widths and operate photo, resume, visibility, + preferences, and public Guild actions. + +Expected observations: + +- Guild remains plainly separate from private Blade/account data. +- Empty and populated layouts align without overlap or clipped controls. +- Long values wrap/truncate intentionally; outbound actions remain reachable and + identified as external. +- Existing Guild mutation actions remain available. + +### TC-009: Resume upload confirms before optional View + +Setup: + +- Prepare initial signup and an existing member with/without a resume. + +Action: + +- Upload and replace valid resumes in each flow. + +Expected observations: + +- The viewer does not open automatically. +- A clear successful-upload state appears and offers View. +- Selecting View opens the uploaded document; failure preserves the previous + saved resume and reports a truthful error. + +### TC-010: Profile picture removal is confirmable + +Setup: + +- Prepare a member with a saved picture. + +Action: + +- Start removal, cancel, then repeat and confirm. + +Expected observations: + +- Cancel issues no remove mutation and retains the image. +- Confirm removes it once, provides feedback, and returns focus safely. + +### TC-011: Employment errors identify the actual field + +Setup: + +- Prepare a legacy employment row and a current row missing position title, + company, experience type, or employment status one at a time. + +Action: + +- Submit each invalid state and then correct it. + +Expected observations: + +- Required labels are marked. +- Each error names and associates with the actual entry/field and the first error + receives focus/scroll treatment. +- Legacy confirmation guidance appears only for the legacy condition. +- Corrected valid values save successfully. + +### TC-012: Member search keeps focus through result updates + +Setup: + +- Open Members with data including `Alejandro` and a debounced URL query. + +Action: + +- Type the full name continuously while results update. + +Expected observations: + +- No keystrokes are lost, the search retains focus/caret, results converge, and + URL state remains shareable. + +### TC-013: Issue assignee filtering is safe and composable + +Setup: + +- Capture the current production-base failure and prepare teams with zero, one, + and many eligible assignees plus issues assigned across teams. + +Action: + +- Filter by team/assignee alone and alongside status, priority, search, and view + changes. + +Expected observations: + +- Valid choices return correct results or a truthful empty state, never the + generic workspace-load failure. +- Pagination and other filters remain correct; unauthorized data is not leaked. + +### TC-014: Issue Markdown preserves authored line breaks + +Setup: + +- Create descriptions containing adjacent plain lines, blank paragraphs, lists, + links, code, and long unbroken content. + +Action: + +- Compare edit preview and saved detail. + +Expected observations: + +- Author-entered plain line breaks remain visible and preview matches detail. +- Markdown semantics and long-content wrapping remain intact. +- Other shared Markdown consumers do not change unexpectedly. + +### TC-015: Authorized issue image lifecycle + +Setup: + +- Prepare an issue editor, read-only issue reader, unauthorized admin; valid PNG, + JPEG, WebP, static GIF, and animated GIF images; an SVG; a file above 10 MB; + and an issue already retaining 10 managed images. + +Action: + +- Upload/finalize through file picker, clipboard paste, and drag/drop; confirm + cursor insertion; add alt text; save/render the description; replace or remove + an image; and request it as each actor. + +Expected observations: + +- Only editors can create/finalize/remove; readers can view only when they may + read the issue; unauthorized/guessed IDs reveal no object. +- Preview and detail render authorized images with stable geometry and alt text. +- PNG, JPEG, WebP, and both GIF variants at or below 10 MB are accepted through + every gesture. SVG, oversized files, and an eleventh retained image are + rejected without partially mutating the issue. +- Removed and abandoned objects enter safe cleanup without breaking retained + references or history. + +### TC-016: Actor labels prefer current member names + +Setup: + +- Prepare issue-history and Admin-log rows for a linked renamed Member, a deleted + or missing Member with a saved Discord snapshot, and a system actor. + +Action: + +- Read list/detail/history pages, including rows created before this change. + +Expected observations: + +- Linked rows display current Member full name. +- Missing profiles display the saved Discord fallback. +- System rows retain their explicit system label. +- Reads are batched/no-N+1, access policy is unchanged, and stored rows are not + rewritten. + +### TC-017: Issue reminder renders `Title | Chat` + +Setup: + +- Build otherwise identical due/overdue reminders with a valid Discord thread, + no thread, long/special-character titles, assignees, and an owning-role fallback. + +Action: + +- Build reminder payloads under deterministic tests. + +Expected observations: + +- Threaded issue: linked sanitized title, literal `|`, linked `Chat`. +- No thread: linked sanitized title only. +- Existing mentions, limits, grouping, cadence, idempotency, and allowed-mention + behavior are unchanged; tests perform no live Discord write. + +### TC-018: Existing form instruction media remains complete + +Setup: + +- Prepare an authorized form editor and a draft with text, image, and browser- + playable video instruction blocks. + +Action: + +- Upload, remove, save, preview, publish, and view as an eligible respondent. + +Expected observations: + +- The builder makes media actions discoverable and reports progress/failure. +- Saved ordering, authorized playback/rendering, alt text, replacement cleanup, + and existing 100 MB server policy continue to work. + +### TC-019: Form banner lifecycle and presentation + +Setup: + +- Prepare an editable draft, published form, authorized respondent, unauthorized + actor, valid images, and rejected image fixtures. + +Action: + +- Upload/finalize a banner, preview it, publish, view/respond across widths, + replace it, then remove it. + +Expected observations: + +- Exactly one banner appears above respondent content in a responsive 4:1 + `cover` frame with its alt text. +- The builder provides crop guidance, preview, and editable alt text before + publication. +- Preview and respondent views match; loading/failure do not collapse the page. +- Only authorized actors can manage/read it; durable public storage URLs are not + exposed. +- Replacement/removal safely cleans the old unreferenced object; forms without a + banner remain unchanged. + +### TC-020: Responsive content does not clip + +Setup: + +- Use 320 px, common mobile, intermediate desktop/zoom-equivalent, and full + desktop viewports with the long/sparse member fixtures and Chrome-family + engines available in Playwright. + +Action: + +- Navigate member dashboard/settings, events empty/populated states, Guild links, + QR, resume, and account actions. + +Expected observations: + +- No horizontal page overflow or hidden actionable content. +- Long name, globe/portfolio row, event title, and empty-state text remain + readable; responsive layout does not depend on concealed overflow. + +### TC-021: Comparison chart labels remain readable + +Setup: + +- Prepare current/prior hackathons whose application and confirmation deadlines + coincide or fall on adjacent chart positions. + +Action: + +- Render the Application pace comparison at supported widths. + +Expected observations: + +- Current/prior labels remain distinguishable without covering each other or + essential data. +- Keyboard/screen-reader and textual table alternatives still identify every + deadline and series. + +## Negative / regression cases + +### TC-NEG-001: Focus and rail state do not regress through navigation + +Setup: + +- Open expanded admin navigation and a focused search/filter control. + +Action: + +- Navigate, use back/forward, and update query state. + +Expected observations: + +- The rail returns collapsed after selection; hover never opens it; active input + remounts only when structurally necessary and ordinary query updates retain + focus. + +### TC-NEG-002: Upload authorization cannot be bypassed + +Setup: + +- Obtain another actor's issue/form attachment ID, stale upload intent, malformed + reference, and mismatched owner/form/issue IDs. + +Action: + +- Attempt finalize, attach, read, replace, and remove operations. + +Expected observations: + +- Every operation fails with the approved non-enumerating authorization/error + class, no foreign object is exposed, and no partial entity/history change is + committed. + +### TC-NEG-003: Existing data remains readable + +Setup: + +- Load old issues with plain/external Markdown images, old forms without banners, + old form instruction media, and old actor snapshots. + +Action: + +- Read them after rollout and under a UI rollback with new authoring hidden. + +Expected observations: + +- Existing pages remain functional, old media retains authorized behavior, old + actor rows get truthful enrichment/fallback, and no migration fabricates or + rewrites history. + +## Open questions + +- None. These observable cases are approved for test generation. From 376990f7b77ac36af665a14c2125ece26353205b Mon Sep 17 00:00:00 2001 From: DVidal1205 Date: Wed, 12 Aug 2026 15:03:01 -0700 Subject: [PATCH 2/8] allow shared refinements branch workflow Co-authored-by: Codex --- .forge/features/blade-refinements/status.md | 32 ++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/.forge/features/blade-refinements/status.md b/.forge/features/blade-refinements/status.md index 542b08a98..c2f95b110 100644 --- a/.forge/features/blade-refinements/status.md +++ b/.forge/features/blade-refinements/status.md @@ -53,6 +53,9 @@ Current phase: Bundle approved / ready for technical discovery guidance, preview, and editable alt text. - 2026-08-12: The human approved all five remaining product decisions and the spec/SRD/test-case bundle for implementation. +- 2026-08-12: Contributors may implement directly on the shared + `reforge/refinements` branch for this agent-first trial. Claims, frequent sync, + small commits, and no force-pushes are required to prevent collisions. ## Open questions @@ -63,21 +66,24 @@ Current phase: Bundle approved / ready for technical discovery ## Contributor coordination -- `reforge/refinements` is the shared integration branch and bundle source of - truth. Create a short-lived work branch from it for an implementation slice; - do not push implementation commits straight to the integration branch unless - the maintainer assigns that workflow. -- Before editing, announce the refinement IDs you intend to claim and your work - branch in the shared development thread. Check the latest remote status and - recent commits first. -- Replace `Unclaimed` below with your name/handle, work branch, and claim date. - If someone already owns an overlapping row or file set, coordinate before - continuing. +- `reforge/refinements` is the shared implementation branch and bundle source of + truth. Contributors work and push directly on this branch for now. +- Before each work session, fetch and pull the latest remote branch with rebase, + then check this inventory, the shared development thread, and recent commits. +- Before editing implementation code, announce the refinement IDs and expected + file areas you intend to claim in the shared development thread. Replace + `Unclaimed` below with your name/handle and claim date, set the row to + `In progress`, and push that status-only claim so everyone can see it. +- If someone already owns an overlapping row or file set, coordinate before + continuing. Keep commits small, pull with rebase immediately before every + push, and never force-push the shared branch. Stop and coordinate instead of + resolving a conflict by discarding another contributor's work. - Keep changes limited to the claimed rows. If implementation reveals a spec, SRD, test-case, access, upload, or schema conflict, stop and record it under Open questions instead of silently changing the approved contract. - Update each row to `In progress`, `Blocked`, or `Complete` as work moves. Add - focused checks, screenshots/video, and PR links to this file before handoff. + focused checks, screenshots/video, and relevant commit or PR links to this file + before handoff. ## Refinement inventory @@ -127,8 +133,8 @@ Current phase: Bundle approved / ready for technical discovery - [ ] Reproduce issue-assignee filter failure and document evidence. - [ ] Inspect attachment schema compatibility and document migration/no-migration decision before any schema change. -- [ ] Contributors claim refinement IDs and implementation branches before - editing. +- [ ] Contributors announce and push refinement-ID claims before editing + implementation code. - [ ] Implement claimed slices in checkpoints and keep the inventory current. - [ ] Run focused verification, React analysis, `pnpm verify:precommit`, derived reviewers, browser QA, and `git diff --check`. From 6b0a7f0870c090e91472230f2de5693ce4786ce6 Mon Sep 17 00:00:00 2001 From: DVidal1205 Date: Wed, 12 Aug 2026 15:04:02 -0700 Subject: [PATCH 3/8] clarify agent first contributor ownership Co-authored-by: Codex --- .forge/features/blade-refinements/status.md | 29 ++++++++++++--------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/.forge/features/blade-refinements/status.md b/.forge/features/blade-refinements/status.md index c2f95b110..1d97d5c50 100644 --- a/.forge/features/blade-refinements/status.md +++ b/.forge/features/blade-refinements/status.md @@ -54,8 +54,11 @@ Current phase: Bundle approved / ready for technical discovery - 2026-08-12: The human approved all five remaining product decisions and the spec/SRD/test-case bundle for implementation. - 2026-08-12: Contributors may implement directly on the shared - `reforge/refinements` branch for this agent-first trial. Claims, frequent sync, - small commits, and no force-pushes are required to prevent collisions. + `reforge/refinements` branch for this agent-first trial. +- 2026-08-12: Treat agent-assisted development as a first-class contribution + experience. Agent use is encouraged, while each contributor remains responsible + for the scope, code, decisions, validation, and result they submit. The bundle + exists to reduce errors and miscommunication between developers and agents. ## Open questions @@ -68,22 +71,24 @@ Current phase: Bundle approved / ready for technical discovery - `reforge/refinements` is the shared implementation branch and bundle source of truth. Contributors work and push directly on this branch for now. -- Before each work session, fetch and pull the latest remote branch with rebase, - then check this inventory, the shared development thread, and recent commits. +- This is an agent-first development trial. Contributors are encouraged to use + coding agents as real collaborators, not as an afterthought. The approved + artifacts give the developer and agent the same product, technical, and test + contract before code changes begin. +- Agent assistance does not transfer ownership. The contributor is responsible + for understanding and reviewing the changes, keeping the work in scope, asking + when the contract is unclear, and verifying the result. - Before editing implementation code, announce the refinement IDs and expected file areas you intend to claim in the shared development thread. Replace `Unclaimed` below with your name/handle and claim date, set the row to - `In progress`, and push that status-only claim so everyone can see it. + `In progress`, and make the claim visible to the other contributors. - If someone already owns an overlapping row or file set, coordinate before - continuing. Keep commits small, pull with rebase immediately before every - push, and never force-push the shared branch. Stop and coordinate instead of - resolving a conflict by discarding another contributor's work. + continuing. - Keep changes limited to the claimed rows. If implementation reveals a spec, SRD, test-case, access, upload, or schema conflict, stop and record it under Open questions instead of silently changing the approved contract. -- Update each row to `In progress`, `Blocked`, or `Complete` as work moves. Add - focused checks, screenshots/video, and relevant commit or PR links to this file - before handoff. +- Update each row to `In progress`, `Blocked`, or `Complete` as work moves, and + record the checks and visual evidence used to verify completed work. ## Refinement inventory @@ -133,7 +138,7 @@ Current phase: Bundle approved / ready for technical discovery - [ ] Reproduce issue-assignee filter failure and document evidence. - [ ] Inspect attachment schema compatibility and document migration/no-migration decision before any schema change. -- [ ] Contributors announce and push refinement-ID claims before editing +- [ ] Contributors announce and record refinement-ID claims before editing implementation code. - [ ] Implement claimed slices in checkpoints and keep the inventory current. - [ ] Run focused verification, React analysis, `pnpm verify:precommit`, derived From 7e352799b6eceeca72e4fa9ae4be46c1c948ea4c Mon Sep 17 00:00:00 2001 From: DVidal1205 Date: Wed, 12 Aug 2026 15:04:37 -0700 Subject: [PATCH 4/8] simplify agent first contributor entrypoint Co-authored-by: Codex --- .forge/features/blade-refinements/status.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.forge/features/blade-refinements/status.md b/.forge/features/blade-refinements/status.md index 1d97d5c50..5c3ed13f4 100644 --- a/.forge/features/blade-refinements/status.md +++ b/.forge/features/blade-refinements/status.md @@ -59,6 +59,10 @@ Current phase: Bundle approved / ready for technical discovery experience. Agent use is encouraged, while each contributor remains responsible for the scope, code, decisions, validation, and result they submit. The bundle exists to reduce errors and miscommunication between developers and agents. +- 2026-08-12: The contributor entrypoint is the feature bundle. A developer may + point an agent at it and let the agent load the repository instructions, + relevant skills, and code context. Humans may read those materials themselves + if useful, but are not expected to study agent skill files before contributing. ## Open questions @@ -75,6 +79,13 @@ Current phase: Bundle approved / ready for technical discovery coding agents as real collaborators, not as an afterthought. The approved artifacts give the developer and agent the same product, technical, and test contract before code changes begin. +- The simplest way to start is to point an agent at + `.forge/features/blade-refinements/`, give it the refinement IDs being claimed, + and tell it to follow the repository's agent instructions. The agent should + read the bundle, `AGENTS.md`, and any relevant repo skills or code context it + needs. Contributors can read the bundle and supporting material themselves if + they are curious or prefer to work without an agent; they are not expected to + read skill files manually. - Agent assistance does not transfer ownership. The contributor is responsible for understanding and reviewing the changes, keeping the work in scope, asking when the contract is unclear, and verifying the result. From 68af0c87f5a727b006034d750a519c25076dcd35 Mon Sep 17 00:00:00 2001 From: DVidal1205 Date: Wed, 12 Aug 2026 15:12:34 -0700 Subject: [PATCH 5/8] rename refinements branch for forge Co-authored-by: Codex --- .forge/features/blade-refinements/srd.md | 2 +- .forge/features/blade-refinements/status.md | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.forge/features/blade-refinements/srd.md b/.forge/features/blade-refinements/srd.md index d36ae1187..09a8463db 100644 --- a/.forge/features/blade-refinements/srd.md +++ b/.forge/features/blade-refinements/srd.md @@ -11,7 +11,7 @@ form banner, and preserve current permission, audit, Forms, and Discord delivery invariants. This bundle starts from `origin/main` commit `78857b85` on branch -`reforge/refinements` in `/Users/dvidal/Documents/forge-refinements`. +`forge/refinements` in `/Users/dvidal/Documents/forge-refinements`. ## Relevant principles diff --git a/.forge/features/blade-refinements/status.md b/.forge/features/blade-refinements/status.md index 5c3ed13f4..744e1156e 100644 --- a/.forge/features/blade-refinements/status.md +++ b/.forge/features/blade-refinements/status.md @@ -4,9 +4,11 @@ Current phase: Bundle approved / ready for technical discovery ## Decision log -- 2026-08-12: Start from production `origin/main`, not `reforge/main` or an - existing dirty worktree. Created `/Users/dvidal/Documents/forge-refinements` - on `reforge/refinements` at `78857b85`. +- 2026-08-12: Start from production `origin/main` in the isolated + `/Users/dvidal/Documents/forge-refinements` worktree at `78857b85`. +- 2026-08-12: Forge is the current product/project name. Reforge is retired and + must not be used for new branch or workflow naming. The shared branch is + `forge/refinements`. - 2026-08-12: Use one `blade-refinements` bundle for the long refinement slice; the human explicitly waived the preference to split unrelated small changes. - 2026-08-12: Desktop admin navigation uses a top-left explicit opener, never @@ -54,7 +56,7 @@ Current phase: Bundle approved / ready for technical discovery - 2026-08-12: The human approved all five remaining product decisions and the spec/SRD/test-case bundle for implementation. - 2026-08-12: Contributors may implement directly on the shared - `reforge/refinements` branch for this agent-first trial. + `forge/refinements` branch for this agent-first trial. - 2026-08-12: Treat agent-assisted development as a first-class contribution experience. Agent use is encouraged, while each contributor remains responsible for the scope, code, decisions, validation, and result they submit. The bundle @@ -73,7 +75,7 @@ Current phase: Bundle approved / ready for technical discovery ## Contributor coordination -- `reforge/refinements` is the shared implementation branch and bundle source of +- `forge/refinements` is the shared implementation branch and bundle source of truth. Contributors work and push directly on this branch for now. - This is an agent-first development trial. Contributors are encouraged to use coding agents as real collaborators, not as an afterthought. The approved @@ -158,8 +160,8 @@ Current phase: Bundle approved / ready for technical discovery ## Validation / commands - `git fetch origin main`: fetched production base successfully. -- `git worktree add -b reforge/refinements /Users/dvidal/Documents/forge-refinements origin/main`: - created isolated worktree at `78857b85`. +- Isolated worktree creation from `origin/main`: created + `/Users/dvidal/Documents/forge-refinements` at `78857b85`. - `/Users/dvidal/Documents/forge/node_modules/.bin/tsx scripts/create-forge-feature.ts blade-refinements "Blade Refinements"`: created the four-file bundle. The fresh worktree has no local `node_modules`, so the already-installed runtime from the source worktree was used without @@ -195,8 +197,8 @@ Current phase: Bundle approved / ready for technical discovery - Triage artifact: `/Users/dvidal/Documents/Codex/2026-08-12/pleas/outputs/forge-refinements-triage.md` - Worktree: `/Users/dvidal/Documents/forge-refinements` -- Branch: `reforge/refinements` -- Remote branch: `https://github.com/KnightHacks/forge/tree/reforge/refinements` +- Branch: `forge/refinements` +- Remote branch: `https://github.com/KnightHacks/forge/tree/forge/refinements` - Feature bundle: `.forge/features/blade-refinements/` - PRs: none - Issues: GitHub #503, GitHub #504, and the supplied Blade issue/thread context From 1bb41684aa726208314aa672df783702a6c5de25 Mon Sep 17 00:00:00 2001 From: hector1128 Date: Fri, 14 Aug 2026 16:48:14 -0400 Subject: [PATCH 6/8] R-14 confirmation before deleting pfp (dashboard + settings) --- .forge/features/blade-refinements/status.md | 85 +++++++++----- .../member/member-profile-picture-upload.tsx | 104 ++++++++++++++---- 2 files changed, 140 insertions(+), 49 deletions(-) diff --git a/.forge/features/blade-refinements/status.md b/.forge/features/blade-refinements/status.md index 744e1156e..cedfd60a9 100644 --- a/.forge/features/blade-refinements/status.md +++ b/.forge/features/blade-refinements/status.md @@ -105,35 +105,35 @@ Current phase: Bundle approved / ready for technical discovery ## Refinement inventory -| ID | Refinement | State | Claim | Proof | -| ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | --------- | ------------------------------ | -| R-01 | Keep `/` public for signed-in users, adapt its CTA, and make the product mark return there. | Ready | Unclaimed | TC-001 | -| R-02 | Remove the sidebar for ordinary members; place Settings and Sign out together at the top right. | Ready | Unclaimed | TC-002 | -| R-03 | Replace hover expansion with a top-left admin rail opener; keep collapsed icons clickable, close after selection, and preserve mobile close-on-select. | Ready | Unclaimed | TC-003, TC-NEG-001 | -| R-04 | Group admin destinations into the approved Club, Team, Hackathon, and External map; omit empty groups and mark Guild/outbound destinations as external. | Ready | Unclaimed | TC-004 | -| R-05 | Remove visible admin eyebrows/descriptions, expose description-only title help, and shrink matching skeletons. | Ready | Unclaimed | TC-005 | -| R-06 | Remove repetitive configuration subtitles while preserving consequential guidance. | Ready | Unclaimed | TC-005 | -| R-07 | Use one member-dashboard hierarchy and action set across mobile and desktop. | Ready | Unclaimed | TC-007, TC-020 | -| R-08 | Keep Guild prominent and editable; define Guild, separate public Guild data from private Blade data, and mark its public actions as external. | Ready | Unclaimed | TC-007, TC-008 | -| R-09 | Replace the isolated QR action with a compact Check in surface and View QR code action on every viewport. | Ready | Unclaimed | TC-007 | -| R-10 | Keep unpaid dues prominent; replace the paid tile with a green paid badge and accessible tooltip beside the Welcome name. | Ready | Unclaimed | TC-006 | -| R-11 | Keep Previous forms as a small, low-emphasis action at the bottom of the dashboard. | Ready | Unclaimed | TC-007 | -| R-12 | Align sparse and populated Guild/profile content and handle long names, links, companies, filenames, events, and empty states without clipping. | Ready | Unclaimed | TC-008, TC-020 | -| R-13 | Change resume upload/replace in signup and existing-member flows to success plus explicit View, without automatic preview. | Ready | Unclaimed | TC-009 | -| R-14 | Require confirmation before removing a saved profile picture. | Ready | Unclaimed | TC-010 | -| R-15 | Mark employment fields required and report/focus the precise invalid entry and field without mislabeling legacy validation. | Ready | Unclaimed | TC-011 | -| R-16 | Preserve admin member-search focus and keystrokes while debounced results and URL state update. | Ready | Unclaimed | TC-012, TC-NEG-001 | -| R-17 | Reproduce and fix the Issue assignee filter failure without breaking other filters, pagination, or access policy. | Discovery | Unclaimed | TC-013 | -| R-18 | Preserve author-entered issue-description line breaks in preview/detail without changing unrelated Markdown consumers. | Ready | Unclaimed | TC-014 | -| R-19 | Add authorized managed issue images through picker, paste, and drag/drop with cursor insertion, alt text, approved limits, rendering, removal, and cleanup. | Discovery | Unclaimed | TC-015, TC-NEG-002, TC-NEG-003 | -| R-20 | Prefer linked current Member full names in Issue history and Admin logs; fall back to stored Discord labels and preserve system actors. | Ready | Unclaimed | TC-016, TC-NEG-003 | -| R-21 | Render issue reminders as linked `Title \| Chat` when a Discord thread exists and linked title alone otherwise. | Ready | Unclaimed | TC-017 | -| R-22 | Prevent overlapping current/prior hackathon comparison labels while preserving the accessible text/table alternative. | Ready | Unclaimed | TC-021 | -| R-23 | Fix reported Chrome/Zen member and shell overflow at 320 px, intermediate widths, and desktop without hiding content behind overflow rules. | Ready | Unclaimed | TC-020 | -| R-24 | Verify and polish existing Forms text/image/video instruction-card authoring, upload feedback, ordering, cleanup, and respondent rendering. | Ready | Unclaimed | TC-018, TC-NEG-003 | -| R-25 | Add one managed form banner with upload/replace/remove, editable alt text, preview guidance, and responsive 4:1 `cover` presentation. | Discovery | Unclaimed | TC-019, TC-NEG-002, TC-NEG-003 | -| R-26 | Knight Hacks member-benefits content/page. | Deferred | Unclaimed | Out of scope | -| R-27 | Grafana analytics replacement or observability infrastructure. | Deferred | Unclaimed | Out of scope | +| ID | Refinement | State | Claim | Proof | +| ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------------------- | ------------------------------ | +| R-01 | Keep `/` public for signed-in users, adapt its CTA, and make the product mark return there. | Ready | Unclaimed | TC-001 | +| R-02 | Remove the sidebar for ordinary members; place Settings and Sign out together at the top right. | Ready | Unclaimed | TC-002 | +| R-03 | Replace hover expansion with a top-left admin rail opener; keep collapsed icons clickable, close after selection, and preserve mobile close-on-select. | Ready | Unclaimed | TC-003, TC-NEG-001 | +| R-04 | Group admin destinations into the approved Club, Team, Hackathon, and External map; omit empty groups and mark Guild/outbound destinations as external. | Ready | Unclaimed | TC-004 | +| R-05 | Remove visible admin eyebrows/descriptions, expose description-only title help, and shrink matching skeletons. | Ready | Unclaimed | TC-005 | +| R-06 | Remove repetitive configuration subtitles while preserving consequential guidance. | Ready | Unclaimed | TC-005 | +| R-07 | Use one member-dashboard hierarchy and action set across mobile and desktop. | Ready | Unclaimed | TC-007, TC-020 | +| R-08 | Keep Guild prominent and editable; define Guild, separate public Guild data from private Blade data, and mark its public actions as external. | Ready | Unclaimed | TC-007, TC-008 | +| R-09 | Replace the isolated QR action with a compact Check in surface and View QR code action on every viewport. | Ready | Unclaimed | TC-007 | +| R-10 | Keep unpaid dues prominent; replace the paid tile with a green paid badge and accessible tooltip beside the Welcome name. | Ready | Unclaimed | TC-006 | +| R-11 | Keep Previous forms as a small, low-emphasis action at the bottom of the dashboard. | Ready | Unclaimed | TC-007 | +| R-12 | Align sparse and populated Guild/profile content and handle long names, links, companies, filenames, events, and empty states without clipping. | Ready | Unclaimed | TC-008, TC-020 | +| R-13 | Change resume upload/replace in signup and existing-member flows to success plus explicit View, without automatic preview. | Ready | Unclaimed | TC-009 | +| R-14 | Require confirmation before removing a saved profile picture. | Complete | hector1128 (2026-08-14) | TC-010 | +| R-15 | Mark employment fields required and report/focus the precise invalid entry and field without mislabeling legacy validation. | Ready | Unclaimed | TC-011 | +| R-16 | Preserve admin member-search focus and keystrokes while debounced results and URL state update. | Ready | Unclaimed | TC-012, TC-NEG-001 | +| R-17 | Reproduce and fix the Issue assignee filter failure without breaking other filters, pagination, or access policy. | Discovery | Unclaimed | TC-013 | +| R-18 | Preserve author-entered issue-description line breaks in preview/detail without changing unrelated Markdown consumers. | Ready | Unclaimed | TC-014 | +| R-19 | Add authorized managed issue images through picker, paste, and drag/drop with cursor insertion, alt text, approved limits, rendering, removal, and cleanup. | Discovery | Unclaimed | TC-015, TC-NEG-002, TC-NEG-003 | +| R-20 | Prefer linked current Member full names in Issue history and Admin logs; fall back to stored Discord labels and preserve system actors. | Ready | Unclaimed | TC-016, TC-NEG-003 | +| R-21 | Render issue reminders as linked `Title \| Chat` when a Discord thread exists and linked title alone otherwise. | Ready | Unclaimed | TC-017 | +| R-22 | Prevent overlapping current/prior hackathon comparison labels while preserving the accessible text/table alternative. | Ready | Unclaimed | TC-021 | +| R-23 | Fix reported Chrome/Zen member and shell overflow at 320 px, intermediate widths, and desktop without hiding content behind overflow rules. | Ready | Unclaimed | TC-020 | +| R-24 | Verify and polish existing Forms text/image/video instruction-card authoring, upload feedback, ordering, cleanup, and respondent rendering. | Ready | Unclaimed | TC-018, TC-NEG-003 | +| R-25 | Add one managed form banner with upload/replace/remove, editable alt text, preview guidance, and responsive 4:1 `cover` presentation. | Discovery | Unclaimed | TC-019, TC-NEG-002, TC-NEG-003 | +| R-26 | Knight Hacks member-benefits content/page. | Deferred | Unclaimed | Out of scope | +| R-27 | Grafana analytics replacement or observability infrastructure. | Deferred | Unclaimed | Out of scope | ## Task list @@ -192,6 +192,33 @@ Current phase: Bundle approved / ready for technical discovery - `pnpm verify:push`: passed after the contributor inventory and coordination instructions were added. It reran format, lint, and typecheck successfully. +- 2026-08-14: R-14 implemented. Confirmation now gates saved-photo removal in + `MemberProfilePictureUpload` (used by the member dashboard and settings + page), which is the only shared component behind that surface. The signup + flow's `saveMode="deferred"` remove path is unaffected, since nothing is + saved yet there and the SRD scopes confirmation to saved-photo removal. + Cancel closes without a mutation call; confirm runs the existing + `saveMemberProfilePicture` mutation once and returns focus to the upload + file input (the only control still present after the remove button + unmounts). Checks run: `pnpm format` (pass), `pnpm --filter=@forge/blade +typecheck` (pass), `pnpm --filter=@forge/blade lint` (0 errors; pre-existing + unrelated file/function-length warnings only), `pnpm analyze:react:changed` + (pass, prop API unchanged), and the two existing Vitest files that + reference this component (`member-profile-settings-form.test.tsx`, + `member-dashboard.test.tsx`, 8/8 passing — both mock the component and + don't cover the new confirm flow). No automated browser/E2E verification + was performed. Manual verification in a real logged-in session is still + recommended before merge. +- 2026-08-14: `forge/refinements` was rebased onto latest `main` (through + `dfac35c1`, "Adding Leetcode and /eightball responses #487") and + force-pushed to origin at the human's explicit request, replacing an + already-pushed merge commit (`9583bdb5`, "Merge branch 'main' into + forge/refinements") with a linear history so `rename refinements branch +for forge` is the branch tip again. The merged content was verified + byte-identical before rewriting history. Any other local clone of this + branch (e.g. the `dvidal` worktree referenced below) will need to reset to + the new tip rather than merge/pull normally. + ## Links - Triage artifact: diff --git a/apps/blade/src/app/_components/member/member-profile-picture-upload.tsx b/apps/blade/src/app/_components/member/member-profile-picture-upload.tsx index 5686dc14c..550ec45f4 100644 --- a/apps/blade/src/app/_components/member/member-profile-picture-upload.tsx +++ b/apps/blade/src/app/_components/member/member-profile-picture-upload.tsx @@ -1,10 +1,19 @@ "use client"; -import { useState } from "react"; +import { useRef, useState } from "react"; import { Camera, Loader2, X } from "lucide-react"; import { cn } from "@forge/ui"; import { Avatar, AvatarFallback, AvatarImage } from "@forge/ui/avatar"; +import { Button } from "@forge/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@forge/ui/dialog"; import { Input } from "@forge/ui/input"; import { checkUploadMetadata, @@ -52,6 +61,8 @@ export function MemberProfilePictureUpload({ ); const [uploadError, setUploadError] = useState(null); const [previewUrl, setPreviewFile] = useObjectPreviewUrl(); + const [confirmRemoveOpen, setConfirmRemoveOpen] = useState(false); + const fileInputRef = useRef(null); const savedProfilePicture = api.profilePicture.getProfilePicture.useQuery( undefined, @@ -130,6 +141,34 @@ export function MemberProfilePictureUpload({ } }; + const removeSavedPicture = async () => { + setUploadError(null); + + try { + await updateProfilePicture.mutateAsync({ profilePictureUrl: "" }); + setProfilePictureUrl(""); + setPreviewFile(null); + } catch { + setUploadError("Profile picture could not be removed."); + } finally { + setConfirmRemoveOpen(false); + fileInputRef.current?.focus(); + } + }; + + const handleRemoveClick = () => { + setUploadError(null); + + if (saveMode === "deferred") { + setProfilePictureUrl(""); + setPreviewFile(null); + onChange?.(""); + return; + } + + setConfirmRemoveOpen(true); + }; + return (
@@ -160,6 +199,7 @@ export function MemberProfilePictureUpload({ )} Upload profile picture { - setUploadError(null); - try { - if (saveMode === "deferred") { - setProfilePictureUrl(""); - setPreviewFile(null); - onChange?.(""); - return; - } - - await updateProfilePicture.mutateAsync({ - profilePictureUrl: "", - }); - setProfilePictureUrl(""); - setPreviewFile(null); - } catch { - setUploadError("Profile picture could not be removed."); - } - }} + onClick={handleRemoveClick} >
+ + {saveMode === "member" && ( + { + if (!updateProfilePicture.isPending) setConfirmRemoveOpen(open); + }} + > + + + Remove profile picture? + + This removes your saved profile picture. You can upload a new + one anytime. + + + + + + + + + )}
); } From 8d5221ae9fead795458d139bc67b74329d637e3c Mon Sep 17 00:00:00 2001 From: hector1128 Date: Fri, 14 Aug 2026 17:42:24 -0400 Subject: [PATCH 7/8] made about and guild prefernces bigger for more visibility --- .../_components/member/guild-preferences-dialog.tsx | 10 +++++++++- .../src/app/_components/member/member-dashboard.tsx | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/blade/src/app/_components/member/guild-preferences-dialog.tsx b/apps/blade/src/app/_components/member/guild-preferences-dialog.tsx index 696248677..0816fd068 100644 --- a/apps/blade/src/app/_components/member/guild-preferences-dialog.tsx +++ b/apps/blade/src/app/_components/member/guild-preferences-dialog.tsx @@ -5,6 +5,7 @@ import { ExternalLink, Loader2, SlidersHorizontal } from "lucide-react"; import type { GUILD } from "@forge/consts"; import { GUILD as GUILD_CONSTS } from "@forge/consts"; +import { cn } from "@forge/ui"; import { Button } from "@forge/ui/button"; import { Checkbox } from "@forge/ui/checkbox"; import { @@ -19,6 +20,7 @@ import { import { Switch } from "@forge/ui/switch"; import type { CurrentMember } from "~/hooks/use-member"; +import { dashboardNestedSurfaceClass } from "~/app/_components/member/member-dashboard"; import { getGuildMemberUrl } from "~/lib/guild-urls"; import { api } from "~/trpc/react"; @@ -69,7 +71,13 @@ export function GuildPreferencesDialog({ member }: { member: CurrentMember }) { }} > - diff --git a/apps/blade/src/app/_components/member/member-dashboard.tsx b/apps/blade/src/app/_components/member/member-dashboard.tsx index 300ac3917..f22096e08 100644 --- a/apps/blade/src/app/_components/member/member-dashboard.tsx +++ b/apps/blade/src/app/_components/member/member-dashboard.tsx @@ -478,7 +478,7 @@ function GuildProfileCard({
From 6d28433434e403812ea2062d7bbf07a14416b9f8 Mon Sep 17 00:00:00 2001 From: hector1128 Date: Fri, 14 Aug 2026 18:05:02 -0400 Subject: [PATCH 8/8] R-11 discuss -> chat for issue reminders --- .forge/features/blade-refinements/status.md | 25 ++++++++- .../api/src/tests/issues/reminders.test.ts | 52 ++++++++++++++++++- packages/api/src/utils/issues/reminders.ts | 2 +- 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/.forge/features/blade-refinements/status.md b/.forge/features/blade-refinements/status.md index cedfd60a9..401492179 100644 --- a/.forge/features/blade-refinements/status.md +++ b/.forge/features/blade-refinements/status.md @@ -117,7 +117,7 @@ Current phase: Bundle approved / ready for technical discovery | R-08 | Keep Guild prominent and editable; define Guild, separate public Guild data from private Blade data, and mark its public actions as external. | Ready | Unclaimed | TC-007, TC-008 | | R-09 | Replace the isolated QR action with a compact Check in surface and View QR code action on every viewport. | Ready | Unclaimed | TC-007 | | R-10 | Keep unpaid dues prominent; replace the paid tile with a green paid badge and accessible tooltip beside the Welcome name. | Ready | Unclaimed | TC-006 | -| R-11 | Keep Previous forms as a small, low-emphasis action at the bottom of the dashboard. | Ready | Unclaimed | TC-007 | +| R-11 | Keep Previous forms as a small, low-emphasis action at the bottom of the dashboard. | Complete | hector1128 (2026-08-14) | TC-007 | | R-12 | Align sparse and populated Guild/profile content and handle long names, links, companies, filenames, events, and empty states without clipping. | Ready | Unclaimed | TC-008, TC-020 | | R-13 | Change resume upload/replace in signup and existing-member flows to success plus explicit View, without automatic preview. | Ready | Unclaimed | TC-009 | | R-14 | Require confirmation before removing a saved profile picture. | Complete | hector1128 (2026-08-14) | TC-010 | @@ -127,7 +127,7 @@ Current phase: Bundle approved / ready for technical discovery | R-18 | Preserve author-entered issue-description line breaks in preview/detail without changing unrelated Markdown consumers. | Ready | Unclaimed | TC-014 | | R-19 | Add authorized managed issue images through picker, paste, and drag/drop with cursor insertion, alt text, approved limits, rendering, removal, and cleanup. | Discovery | Unclaimed | TC-015, TC-NEG-002, TC-NEG-003 | | R-20 | Prefer linked current Member full names in Issue history and Admin logs; fall back to stored Discord labels and preserve system actors. | Ready | Unclaimed | TC-016, TC-NEG-003 | -| R-21 | Render issue reminders as linked `Title \| Chat` when a Discord thread exists and linked title alone otherwise. | Ready | Unclaimed | TC-017 | +| R-21 | Render issue reminders as linked `Title \| Chat` when a Discord thread exists and linked title alone otherwise. | Complete | hector1128 (2026-08-14) | TC-017 | | R-22 | Prevent overlapping current/prior hackathon comparison labels while preserving the accessible text/table alternative. | Ready | Unclaimed | TC-021 | | R-23 | Fix reported Chrome/Zen member and shell overflow at 320 px, intermediate widths, and desktop without hiding content behind overflow rules. | Ready | Unclaimed | TC-020 | | R-24 | Verify and polish existing Forms text/image/video instruction-card authoring, upload feedback, ordering, cleanup, and respondent rendering. | Ready | Unclaimed | TC-018, TC-NEG-003 | @@ -218,6 +218,27 @@ for forge` is the branch tip again. The merged content was verified byte-identical before rewriting history. Any other local clone of this branch (e.g. the `dvidal` worktree referenced below) will need to reset to the new tip rather than merge/pull normally. +- 2026-08-14: R-11 confirmed already satisfied by existing dashboard code + (both the desktop `Card` and the mobile `lg:hidden` block already render + Previous forms as a small `dashboardNestedSurfaceClass` row with a `size="sm"` + outline button) and marked Complete; no code change was needed. +- 2026-08-14: R-21 implemented in `packages/api/src/utils/issues/reminders.ts` + (`targetBlock`). The Discord discussion link changed from ` · [Discuss](url)` + to ` | [Chat](url)`, appended after the bold linked-title heading only when + `target.discordThreadUrl` is set; omitted entirely otherwise. No other + reminder builder (events, hackathon events) shares this pattern, so the + change is scoped to issue reminders only. Existing `[Discuss]` assertions in + `packages/api/src/tests/issues/reminders.test.ts` were updated to `[Chat]`, + and a new `TC-017` test was added covering both the threaded and + threadless cases with a title containing a raw mention and a line break, to + confirm the existing sanitize/escape behavior around the new separator is + unchanged. Checks run: `pnpm --filter=@forge/api typecheck` (pass), + `pnpm --filter=@forge/cron typecheck` (pass, only consumer of + `deliverIssueReminders`), `pnpm --filter=@forge/api lint` (0 errors, + pre-existing unrelated file-length warning only), and + `packages/api/src/tests/issues/reminders.test.ts` (7/7 passing). No message + was sent to a live Discord channel; the cron job that calls this + (`apps/cron/src/crons/issue-reminders.ts`) was not run. ## Links diff --git a/packages/api/src/tests/issues/reminders.test.ts b/packages/api/src/tests/issues/reminders.test.ts index 105f80c18..bfebcf618 100644 --- a/packages/api/src/tests/issues/reminders.test.ts +++ b/packages/api/src/tests/issues/reminders.test.ts @@ -149,7 +149,7 @@ describe("Club Operations Issues reminders", () => { expect(displays[1]).toContain("\n-# Alex"); expect(displays[1]).toContain("\n-# Development Team"); expect(displays[1]).toContain( - "[Discuss]()", + "| [Chat]()", ); expect(displays[1]).not.toContain("Open in Blade"); const [productionMessage] = splitIssueReminderMessages( @@ -169,7 +169,7 @@ describe("Club Operations Issues reminders", () => { expect(bladeOnlyText).toContain( `https://blade.knighthacks.org/admin/issues/${assigned.id}`, ); - expect(bladeOnlyText).not.toContain("[Discuss]"); + expect(bladeOnlyText).not.toContain("[Chat]"); expect(issueReminderAllowedMentions(message.targets)).toEqual({ parse: [], roles: ["333333333333333333"], @@ -177,6 +177,54 @@ describe("Club Operations Issues reminders", () => { }); }); + it("TC-017 renders the linked title followed by `| Chat` only when a Discord thread exists", () => { + const now = new Date("2026-07-21T13:00:00.000Z"); + const [threaded] = buildIssueReminderPlan( + [{ ...baseIssue, name: "Ship <@123> update\nnow" }], + now, + ); + const [threadless] = buildIssueReminderPlan( + [ + { + ...baseIssue, + discordThreadUrl: null, + id: "00000000-0000-4000-8000-000000000003", + name: "Ship <@123> update\nnow", + }, + ], + now, + ); + if (!threaded || !threadless) throw new Error("Expected reminder targets."); + + const [threadedMessage] = splitIssueReminderMessages( + [threaded], + "https://blade.test", + ); + const [threadlessMessage] = splitIssueReminderMessages( + [threadless], + "https://blade.test", + ); + if (!threadedMessage || !threadlessMessage) { + throw new Error("Expected reminder messages."); + } + + const threadedText = containerTextDisplays(threadedMessage).join("\n"); + const threadlessText = containerTextDisplays(threadlessMessage).join("\n"); + + const escapedTitle = sanitizeIssueReminderTitle( + "Ship <@123> update\nnow", + ).replace(/([\\`*_[\]{}()~|>])/g, "\\$1"); + + expect(threadedText).toContain( + `${escapedTitle} (8/4)]()** | [Chat](<${threaded.discordThreadUrl}>)`, + ); + expect(threadlessText).toContain( + `${escapedTitle} (8/4)]()**\n`, + ); + expect(threadlessText).not.toContain("| [Chat]"); + expect(threadlessText).not.toContain("[Chat]"); + }); + it("TC-REMINDER-005 splits deterministic messages at Discord component limits", () => { const targets = Array.from({ length: 80 }, (_, index) => { const [target] = buildIssueReminderPlan( diff --git a/packages/api/src/utils/issues/reminders.ts b/packages/api/src/utils/issues/reminders.ts index 50c99cb36..9ddb28433 100644 --- a/packages/api/src/utils/issues/reminders.ts +++ b/packages/api/src/utils/issues/reminders.ts @@ -194,7 +194,7 @@ function targetBlock(target: IssueReminderTarget, bladeUrl: string) { const url = targetUrl(target, bladeUrl); const title = escapeMarkdown(truncate(target.name, TARGET_TITLE_LIMIT)); const discussion = target.discordThreadUrl - ? ` · [Discuss](<${target.discordThreadUrl}>)` + ? ` | [Chat](<${target.discordThreadUrl}>)` : ""; const heading = `**(${priorityMarks(target.priority)}) [${title} (${shortDate(target.dueAt)})](<${url}>)**${discussion}`; const audience = truncate(