feat(gildi): Guild Hall page — crests, guilds, drives, chronicle, actions#12
Conversation
Second implementation slice: the deterministic heraldic crest generator (pure FNV-1a hash → blazon honouring the rule of tincture → inline SVG Crest, with a monogram fallback), the typed-catalog-search Guilds section (kind:Group spec.type:guild joined to owned practices), the curated guild card composing the crest with name/description/practice+aspect chips, and a plain plugin README. Grounded in the design (§4 card system, §5 crests, §7 data model); catalog-react import sources flagged for verification against installed versions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Task 0 of Plan 2: a plain README for the gildi plugin (noticed missing post-#11). Also corrects the rule-of-tincture assertion in Plan 2's crest test snippet (the original had a garbled tautology) to the clean "exactly one of field/charge is a metal" check, before the crest task runs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pure FNV-1a hash -> blazon (rule of tincture: a colour field pairs with a metal
charge or vice versa) -> inline SVG Crest component (shield + division + charge),
deterministic per seed. Unit-tested for determinism, rule of tincture, and
distinctness. Task 2's guild card renders <Crest seed={guildName} />.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ards Query kind:Group spec.type:guild (joined to owned type:practice Components), render a responsive grid of curated guild cards (crest + name + description + practice/aspect chips) linking to each guild's entity page. First frontend catalog-querying in the repo. Also hardens the crest clipPath id to useId for unique rendering across the grid, and adds a default case to the crest's charge-shape switch to satisfy the repo-wide lint gate. Updates the existing GuildHallPage smoke test to mock catalogApiRef now that the page mounts the guilds section. ws test + ws lint green; browser visual check of the rendered cards pending human confirmation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add display titles to the guild Groups + security-practice so cards read "Security guild" / "Release Captains" / "Security practice" not raw kebab ids. Refactor GuildCard to theme-aware MUI Typography/Chip + EntityRefLink (consuming GuildView.entityRef) instead of fixed inline pixel styles, fixing the small-font look and dropping the hand-built entity path. Adds @material-ui/core as an explicit gildi plugin dependency (was only hoisted transitively before). Updates GuildsSection.test.tsx to bind entityRouteRef via mountedRoutes, which EntityRefLink now requires under renderInTestApp. ws test + ws lint green; browser re-check pending. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, layout) Third slice, batched onto this branch per Cervator (conserve PR review quota, and the page reads better whole): the Drives band, the Chronicle rail (recent Sagas), the Actions panel (scaffolder links + a light charter-a-practice dogfood template), and the two-zone layout composing all five sections. Each new section mirrors the reviewed Guilds pattern (useX hook + XCard InfoCard/EntityRefLink/ Crest/Typography + XSection states). Grounded in the seed's Cycle/Saga shapes. Deferred (noted): typed+colored chips, front-matter saga previews, skald avatars. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Query Cycle spec.type:drive, render a bounded "Active & upcoming drives" band of curated drive cards (owner-guild crest + title + description + timeframe) linking to the Cycle entity. Mirrors the reviewed Guilds section pattern. Not yet mounted in the page (layout task composes all sections). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Query kind:Saga (newest-first by timeframe.end), render a compact "Recent chronicle" rail of saga preview cards (touched-guild crest + title + one-line description + skald byline + Read link). Mirrors the reviewed Guilds/Drives pattern. Not yet mounted (layout task composes sections). Front-matter previews deferred - uses metadata.description for now. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Query scaffolder Templates tagged guild-hall, render an "Actions" panel of curated action cards (glyph + title + Create-page link). Seed a mock charter-a-practice Template (debug:log only) tagged guild-hall + register it in app-config, so the hub dogfoods its own model. Mirrors the reviewed section pattern. Not yet mounted (layout task). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Compose the full Guild Hall page: intro header, a bounded Drives band up top, then a two-zone Grid - a wide Guilds column and a rail holding Actions above the Chronicle. Extend the page render test's catalog mock for the kinds all sections now query. Full-page browser visual check pending human confirmation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…icle, top-aligned rail Move Drives+Guilds into a wide column and Actions+Chronicle into a top-aligned rail (fills the empty upper-right). Render Actions as small outlined buttons (Create-page links) rather than heavy cards, and Chronicle as compact saga rows rather than InfoCards - so the rail reads "a few buttons, then a list of sagas" per the design mockup. Tests updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ild Hall Flesh out the demo so the Guild Hall reads fuller: - Two more guilds: Platform (aspect-aligned, operational-readiness) and Incident Commanders (craft-aligned) — with egil/sigrid joining them. - A second drive Cycle: operational-readiness-drive (Platform guild). - Display `title`s on the Cycles and Sagas (they were showing raw kebab ids as the card/link text with the human title stuck in the description) — mock-org and the MTL soccer saga. Descriptions reworked into short previews. Verified: org.yaml + cycles.yaml parse and carry the new entities (yq). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…start-a-drive) Complete the Actions set to the mockup's three buttons: add mock (debug:log) scaffolder Templates `establish-a-guild` (New guild) and `start-a-drive` (Start a drive), tagged guild-hall, registered in app-config alongside charter-a-practice. Real scaffolding is future work; these log their plan on the Create page. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds catalog-backed Guild Hall sections for guilds, drives, chronicle Sagas, and actions; introduces deterministic SVG crests, mock catalog data, dogfood templates, responsive page composition, tests, dependencies, and plugin documentation. ChangesGuild Hall hub
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant CatalogAPI
participant GuildHallPage
participant GuildsSection
participant DrivesBand
participant ActionsPanel
participant ChronicleRail
GuildHallPage->>DrivesBand: render drives section
DrivesBand->>CatalogAPI: query drive Cycles
GuildHallPage->>GuildsSection: render guilds section
GuildsSection->>CatalogAPI: query guild Groups and practice Components
GuildHallPage->>ActionsPanel: render actions panel
ActionsPanel->>CatalogAPI: query tagged Templates
GuildHallPage->>ChronicleRail: render chronicle rail
ChronicleRail->>CatalogAPI: query Saga entities
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
This PR builds out the @siliconsaga/plugin-gildi Guild Hall page from a placeholder into a functional, catalog-driven surface, adding deterministic heraldic crests and multiple curated sections (Guilds, Drives, Actions, Chronicle) plus updated mock seed data and catalog ingestion.
Changes:
- Add a deterministic crest generator (FNV-1a → blazon → inline SVG) and use it across Guild/Drive/Saga cards.
- Implement catalog-backed sections for guilds (with practices/aspects), drives, actions (scaffolder templates), and a chronicle rail (recent sagas), composed into a two-zone layout.
- Expand mock org seed entities (titles, guilds, drives, templates) and register new Template locations in
app-config.yaml.
Reviewed changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| plugins/gildi/src/plugin.test.tsx | Updates page smoke test to mount routes and provide a catalog API stub. |
| plugins/gildi/src/guilds/useGuilds.ts | Adds catalog queries/join logic to build a GuildView list. |
| plugins/gildi/src/guilds/GuildsSection.tsx | Renders a loading/error/empty state + responsive guild card grid. |
| plugins/gildi/src/guilds/GuildsSection.test.tsx | Adds render test with mocked catalog results. |
| plugins/gildi/src/guilds/GuildCard.tsx | Adds curated guild InfoCard using crest + chips + entity link. |
| plugins/gildi/src/drives/useDrives.ts | Adds catalog query + view shaping for drive Cycles. |
| plugins/gildi/src/drives/DrivesBand.tsx | Renders drives band with loading/error/empty states and grid. |
| plugins/gildi/src/drives/DrivesBand.test.tsx | Adds render test for drives band with mocked drive Cycle. |
| plugins/gildi/src/drives/DriveCard.tsx | Adds curated drive InfoCard with optional owner guild crest. |
| plugins/gildi/src/crest/index.ts | Barrel exports for crest module. |
| plugins/gildi/src/crest/hash.ts | Deterministic FNV-1a 32-bit hash utility. |
| plugins/gildi/src/crest/Crest.tsx | Inline SVG crest renderer (division + charge) using useId clipPath. |
| plugins/gildi/src/crest/blazon.ts | Blazon generator honoring rule of tincture + palette helpers. |
| plugins/gildi/src/crest/blazon.test.ts | Unit tests for determinism, tincture rule, distinctness. |
| plugins/gildi/src/components/GuildHallPage.tsx | Composes Drives/Guilds/Actions/Chronicle into the page layout. |
| plugins/gildi/src/chronicle/useSagas.ts | Adds catalog query + sorting and view shaping for sagas. |
| plugins/gildi/src/chronicle/SagaCard.tsx | Adds compact saga row with crest, title, description, byline. |
| plugins/gildi/src/chronicle/ChronicleRail.tsx | Renders recent chronicle rail with cap + “more” indicator. |
| plugins/gildi/src/chronicle/ChronicleRail.test.tsx | Adds sorting/byline test for chronicle rail. |
| plugins/gildi/src/actions/useActions.ts | Adds template discovery for guild-hall tagged actions + Create hrefs. |
| plugins/gildi/src/actions/ActionsPanel.tsx | Renders actions as buttons (currently anchor-based) with loading/error states. |
| plugins/gildi/src/actions/ActionsPanel.test.tsx | Adds render test asserting Create-page href. |
| plugins/gildi/README.md | Adds plugin README (status/install/dev notes). |
| plugins/gildi/package.json | Adds missing runtime deps for crest/hooks/MUI usage. |
| examples/mtl.yaml | Adds Saga title and refines description for display. |
| examples/mock-org/repos/security-aspect/catalog-info.yaml | Adds Component title for nicer card rendering. |
| examples/mock-org/org.yaml | Adds new guild groups + titles; updates org children + memberships. |
| examples/mock-org/guildhall/actions/guild-actions.template.yaml | Adds two additional mock scaffolder Templates tagged guild-hall. |
| examples/mock-org/guildhall/actions/charter-practice.template.yaml | Adds mock “charter-a-practice” Template tagged guild-hall. |
| examples/mock-org/cycles.yaml | Adds Cycle/Saga titles + second drive and updated descriptions. |
| docs/plans/2026-07-21-gildi-full-page-shell-plan.md | Adds Plan 3 implementation plan documentation. |
| docs/plans/2026-07-21-gildi-crest-and-guilds-plan.md | Adds Plan 2 implementation plan documentation. |
| app-config.yaml | Registers new mock Template locations for catalog ingestion. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1,34 @@ | |||
| import { Button, Typography } from '@material-ui/core'; | |||
| import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; | |||
| import { Progress, ResponseErrorPanel } from '@backstage/core-components'; | |||
| component="a" | ||
| href={a.createHref} |
| const touches = (s.spec?.touches as string[]) ?? []; | ||
| const guildRef = touches.find(t => t.startsWith('group:') && t.includes('gildi')) ?? touches.find(t => t.startsWith('group:')); | ||
| const tf = (s.spec?.timeframe as { end?: string }) ?? {}; |
|
|
||
| ## Status | ||
|
|
||
| Foundation shipped: a page at `/guild-hall` with a sidebar entry. Sections land incrementally (guilds first). See `docs/plans/2026-07-20-gildi-guildhall-hub-design.md` in the leidangr repo for the full design. |
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/plans/2026-07-21-gildi-crest-and-guilds-plan.md`:
- Line 16: Resolve the mismatch between the documented crest fallback contract
and the Crest behavior: either implement an explicit generation-disabled path
that returns a monogram and add coverage for it, or remove the monogram fallback
requirement from the plan. Update the Crest contract and related plan statements
consistently, while preserving the existing empty-seed behavior.
- Line 175: Update the crest ID guidance around the seed-based ID construction
to use React’s useId() approach from Crest.tsx instead of normalized seed text.
Ensure each SVG instance receives a unique, collision-free ID and remove the
non-injective seed-derived identifier.
In `@docs/plans/2026-07-21-gildi-full-page-shell-plan.md`:
- Around line 120-122: Update the guildRef selection in the views mapping so it
matches only documented guild references: inspect the final reference segment
and require it to end with “-gildi”, or resolve referenced Groups and select
only actual guild Groups. Remove the broad includes('gildi') predicate while
preserving the fallback behavior for valid group references.
In `@examples/mock-org/guildhall/actions/charter-practice.template.yaml`:
- Around line 11-14: Mark the practice input in
examples/mock-org/guildhall/actions/charter-practice.template.yaml lines 11-14
as required and enforce minLength: 1. Apply the same required and minLength: 1
constraints to guild and stewards in
examples/mock-org/guildhall/actions/guild-actions.template.yaml lines 14-18, and
to drive and guild at lines 36-40, so blank submissions are rejected.
In `@plugins/gildi/README.md`:
- Around line 5-7: Update the Status section in the Gildi README to reflect that
the page now includes Guilds, Drives, Chronicle, and Actions, removing the
outdated statement that sections land incrementally with guilds first. Preserve
the existing route, sidebar, and design-document references.
In `@plugins/gildi/src/actions/useActions.ts`:
- Around line 20-28: The template links built in useActions must preserve each
template’s namespace instead of always using default. Update createHref to use
metadata.namespace with default as the fallback, update the button key in
ActionsPanel to use the namespaced href, and add a non-default-namespace case in
ActionsPanel.test.tsx covering the generated link.
In `@plugins/gildi/src/chronicle/ChronicleRail.tsx`:
- Around line 6-10: Update ChronicleRail so the “Recent chronicle” heading
renders outside the loading, error, and empty-state early returns. Keep each
existing state body unchanged while ensuring the heading remains visible and
navigable in every state.
In `@plugins/gildi/src/drives/useDrives.ts`:
- Around line 15-16: Update the drive retrieval and mapping flow in useDrives to
exclude Cycle entities whose timeframe.end has passed, while retaining active
and upcoming drives. Apply the filtering before returning the mapped items,
using the existing status or date fields, and preserve the current UI contract
for “Active & upcoming drives.”
- Line 24: The entity references currently hardcode the default catalog
namespace. In plugins/gildi/src/drives/useDrives.ts:24 and
plugins/gildi/src/chronicle/useSagas.ts:25, update the link construction to use
stringifyEntityRef with each entity’s actual metadata reference, preserving the
correct namespace for Cycle and Saga links.
In `@plugins/gildi/src/guilds/useGuilds.ts`:
- Around line 27-32: Update useGuilds so practice join keys and
GuildView.entityRef preserve each guild’s actual namespace instead of
hard-coding group:default. Normalize both the owner value used in
practicesByOwner and the guild ref created in the guildsRes.items mapping, using
the catalog-model helpers if available, and ensure practices from non-default
namespaces still join correctly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: bc5eeecb-82ce-4f21-9134-1d38084dcc73
📒 Files selected for processing (33)
app-config.yamldocs/plans/2026-07-21-gildi-crest-and-guilds-plan.mddocs/plans/2026-07-21-gildi-full-page-shell-plan.mdexamples/mock-org/cycles.yamlexamples/mock-org/guildhall/actions/charter-practice.template.yamlexamples/mock-org/guildhall/actions/guild-actions.template.yamlexamples/mock-org/org.yamlexamples/mock-org/repos/security-aspect/catalog-info.yamlexamples/mtl.yamlplugins/gildi/README.mdplugins/gildi/package.jsonplugins/gildi/src/actions/ActionsPanel.test.tsxplugins/gildi/src/actions/ActionsPanel.tsxplugins/gildi/src/actions/useActions.tsplugins/gildi/src/chronicle/ChronicleRail.test.tsxplugins/gildi/src/chronicle/ChronicleRail.tsxplugins/gildi/src/chronicle/SagaCard.tsxplugins/gildi/src/chronicle/useSagas.tsplugins/gildi/src/components/GuildHallPage.tsxplugins/gildi/src/crest/Crest.tsxplugins/gildi/src/crest/blazon.test.tsplugins/gildi/src/crest/blazon.tsplugins/gildi/src/crest/hash.tsplugins/gildi/src/crest/index.tsplugins/gildi/src/drives/DriveCard.tsxplugins/gildi/src/drives/DrivesBand.test.tsxplugins/gildi/src/drives/DrivesBand.tsxplugins/gildi/src/drives/useDrives.tsplugins/gildi/src/guilds/GuildCard.tsxplugins/gildi/src/guilds/GuildsSection.test.tsxplugins/gildi/src/guilds/GuildsSection.tsxplugins/gildi/src/guilds/useGuilds.tsplugins/gildi/src/plugin.test.tsx
…er, section headings, SPA action links, saga guild suffix, doc/README accuracy, template validation CodeRabbit + Copilot on PR #12 (all valid): use stringifyEntityRef/namespace instead of hardcoded default across the hooks; filter drives to active/upcoming; keep section headings visible in loading/empty/error; pick the touched guild by -gildi suffix; SPA Link for action buttons (no full reload); required+minLength on the mock template params; refresh the README status; and align the plan docs with the as-built crest (useId, no monogram fallback) + saga selection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/plans/2026-07-21-gildi-crest-and-guilds-plan.md`:
- Line 157: Insert a blank line between the preceding list item and the opening
```tsx fenced block to satisfy Markdownlint MD031, without changing the block
content.
In `@docs/plans/2026-07-21-gildi-full-page-shell-plan.md`:
- Line 122: Update the Drive and Saga hook samples in the plan to construct
entity references with stringifyEntityRef instead of manually building
cycle:default/... and saga:default/... strings. Add the Drive sample’s
active/upcoming end-date filter, matching shipped behavior, while preserving the
corrected guild predicate.
In `@plugins/gildi/src/drives/DrivesBand.tsx`:
- Line 18: Use canonical entity references for catalog list keys: in
plugins/gildi/src/drives/DrivesBand.tsx lines 18-18, update the drives map key
from d.name to d.entityRef; in plugins/gildi/src/chronicle/ChronicleRail.tsx
lines 22-24, update the stories map key from s.name to s.entityRef.
In `@plugins/gildi/src/drives/useDrives.ts`:
- Around line 30-31: Update the today value in the drive filtering logic to
construct YYYY-MM-DD from the local Date year, month, and day fields instead of
using toISOString(). Preserve the existing views.filter condition and date
format.
In `@plugins/gildi/src/guilds/useGuilds.ts`:
- Around line 28-31: Protect the owner parsing in the practices-by-owner loop
around parseEntityRef and stringifyEntityRef: validate or catch malformed
spec.owner values per practice, skip that practice when parsing fails, and
continue processing the remaining practices without rejecting useGuilds.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: f3b41051-086a-4cd9-a4fd-df138242b457
📒 Files selected for processing (13)
docs/plans/2026-07-21-gildi-crest-and-guilds-plan.mddocs/plans/2026-07-21-gildi-full-page-shell-plan.mdexamples/mock-org/guildhall/actions/charter-practice.template.yamlexamples/mock-org/guildhall/actions/guild-actions.template.yamlplugins/gildi/README.mdplugins/gildi/src/actions/ActionsPanel.tsxplugins/gildi/src/actions/useActions.tsplugins/gildi/src/chronicle/ChronicleRail.tsxplugins/gildi/src/chronicle/useSagas.tsplugins/gildi/src/drives/DrivesBand.test.tsxplugins/gildi/src/drives/DrivesBand.tsxplugins/gildi/src/drives/useDrives.tsplugins/gildi/src/guilds/useGuilds.ts
…ve filter, entity-ref keys, namespace action test CodeRabbit re-scan (all valid): - useGuilds: guard parseEntityRef per practice (try/catch, skip malformed owner) so one bad entity can't reject the whole section. - useDrives: build the expiry `today` from local Date fields, not toISOString() (UTC), so a drive ending today doesn't vanish during the prior local evening. - DrivesBand/ChronicleRail: list keys use `entityRef` (namespace-unique), not bare `name`. - ActionsPanel.test: add a non-default-namespace Template case asserting the Create link preserves the namespace (completes the earlier action-links fix so CodeRabbit's thread resolves). - Plan docs: MD031 blank line before a fence; reconcile the useDrives/useSagas samples with the shipped stringifyEntityRef + active/upcoming filter. Verified: ws test (25) + ws lint green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| import useAsync from 'react-use/lib/useAsync'; | ||
| import { useApi } from '@backstage/core-plugin-api'; | ||
| import { catalogApiRef } from '@backstage/plugin-catalog-react'; | ||
| import { stringifyEntityRef } from '@backstage/catalog-model'; | ||
|
|
||
| export interface DriveView { | ||
| name: string; title: string; description?: string; | ||
| entityRef: string; // cycle:default/<name> | ||
| ownerGuildName?: string; // for the crest, when owner is a guild Group | ||
| start?: string; end?: string; | ||
| } | ||
|
|
||
| export function useDrives() { | ||
| const catalog = useApi(catalogApiRef); | ||
| const state = useAsync(async () => { | ||
| const res = await catalog.getEntities({ filter: { kind: 'Cycle', 'spec.type': 'drive' } }); | ||
| const views = res.items.map(c => { | ||
| const owner = (c.spec?.owner as string) ?? ''; | ||
| const ownerName = owner.startsWith('group:') ? owner.split('/').pop() : undefined; | ||
| const tf = (c.spec?.timeframe as { start?: string; end?: string }) ?? {}; |
| const touches = (s.spec?.touches as string[]) ?? []; | ||
| const guildRef = touches.find(t => t.startsWith('group:') && (t.split('/').pop() ?? '').endsWith('-gildi')) ?? touches.find(t => t.startsWith('group:')); | ||
| const tf = (s.spec?.timeframe as { end?: string }) ?? {}; | ||
| return { | ||
| name: s.metadata.name, | ||
| title: s.metadata.title ?? s.metadata.name, | ||
| description: s.metadata.description, | ||
| entityRef: stringifyEntityRef(s), | ||
| skaldRef: s.spec?.skald as string | undefined, | ||
| guildName: guildRef ? guildRef.split('/').pop() : undefined, | ||
| end: tf.end, | ||
| } as SagaView; |
Copilot flagged both crest-seed extractions using naive `owner.split('/').pop()`,
which mis-derives the seed for valid non-canonical refs (e.g. `group:security-gildi`
with no namespace yields the whole string as the seed → wrong/blank crest). The
current seed is all-canonical so there is no bug today, but this is the same
fragility already fixed with parseEntityRef in useGuilds — so make it consistent:
- useDrives: parse `spec.owner` with the backend's { defaultKind: Group,
defaultNamespace: default } and take the name only when kind is Group;
malformed refs leave the crest off rather than seeding it garbage.
- useSagas: resolve each `touches` entry the same way, keep the group-kind names,
then prefer the `-gildi` guild (unchanged selection semantics).
Verified: ws test (25) + ws lint green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Builds the Guild Hall page out from the empty foundation (#11) to a full designed surface, in two batched slices (Plan 2 + Plan 3 in
docs/plans/2026-07-21-gildi-*):plugins/gildi/src/crest/) — a deterministic FNV-1a hash → blazon (honouring the rule of tincture) → inline-SVGCrest, with auseIdclip-path and unit tests. Every guild gets its own coat of arms, generated from its id (no assets).kind:Group spec.type:guildjoined to ownedtype:practiceComponents), rendered as curated guild cards (crest + name + description + practice/aspect chips,EntityRefLinkto the entity page) viaInfoCard+ theme-aware MUITypography/Chip.driveCycles; recent Sagas (newest-first, compact rows with skald byline); and scaffolder-template links (small buttons) sourced by theguild-halltag, backed by three mockdebug:logdogfood Templates (charter-a-practice / establish-a-guild / start-a-drive).titles on the Cycles/Sagas so cards read as human names, not kebab ids.Scope note: presentation deferred (logged in the plans) — typed/colored tag chips, front-matter-fetched Saga previews, skald avatars, and an owner-ref-join test-coverage hardening.
Test plan
ws test leidangr(make test test-app) andws lint leidangr(make lint tsc) green at each step (crest unit tests, section render tests, page render test).make smoke-cataloggreen (the dogfood Templates + new seed entities ingest).Related
ws test= tests,ws lint= lint+tsc). Next: entity-page decoration, the deferred presentation items, and the eventualguild-hallentity retirement / doc rehome.