Skip to content

Portfolio polish: demo seed, world switcher, quality gate, and bug fixes#1

Merged
catherineluse merged 26 commits into
mainfrom
portfolio-demo-seed
Jul 7, 2026
Merged

Portfolio polish: demo seed, world switcher, quality gate, and bug fixes#1
catherineluse merged 26 commits into
mainfrom
portfolio-demo-seed

Conversation

@gennitdev

Copy link
Copy Markdown
Contributor

Turns the repo into a presentable portfolio project: a reproducible demo you can run in one command, a confident-but-honest README, and a real CI quality gate — plus a batch of UX and correctness fixes.

Demo & content

  • npm run seed builds two isolated worlds — Desert Willow (12 residents, 5 households) and Pinehaven (small companion town) — with community + residential lots, family/coworker/schoolmate relationships, and a template library. Each resident has a weekday work/school schedule plus a nightly sleep block, so the town visibly commutes: dispersed by day, home at night.
  • World switcher in the top nav with a default-world landing; switching keeps you on the same section.
  • Hex → character popover (needs / bio / relationships / memories tabs) with a link to the character's current lot.
  • Day/night indicator + green Community / blue Residence lot badges; committed day/night screenshots in the README.
  • Unfinished sections (Tickets, Alerts, Analytics, Reports, Settings, Calendar, theme toggle) hidden behind VITE_SHOW_UNFINISHED so the app reads as finished.

Bug fixes

  • Pause actually stops the sim — async ticks were overlapping at higher speeds and draining after pause; ticks no longer overlap and skip when paused.
  • startActivity no longer floods the logswork is itemless, item hints fall back to any suitable item, and expected conditions return clean GraphQLErrors instead of masked 500s.
  • No romance between blood relatives — residents stopped "dating" family; existing "attracted to" labels self-heal.
  • Diagonal labels on the character-happiness chart; centered side-nav footer; assorted smaller fixes.

Code health / CI

  • Fixed the 3 failing tests and made vue-tsc clean (was 30 errors).
  • Added ESLint (flat config) and fixed all lint errors; removed dead code.
  • GitHub Actions CI on pushes/PRs to main: lint + typecheck + tests. All green locally.

Not included (deliberately)

  • No cloud deploy config — the app is local-first with an embedded on-disk Kùzu DB, which doesn't fit serverless; the roadmap's desktop Electron build is the natural distribution.

🤖 Generated with Claude Code

gennitdev and others added 26 commits July 6, 2026 13:38
Make the project demoable from a fresh clone for portfolio screenshots.

- npm run seed (src/seed.ts): resets the DB and builds Desert Willow —
  12 characters in 5 households, community lots (clinic, library,
  community center, school, campground) + 5 homes. Reuses seedData
  templates; keeps Manny + cat as the retired Reyes household.
- Sleep is now a per-character scheduled activity: Shift.activity is
  "work" | "sleep" (backend schema + serialize/deserialize + resolver),
  sleep shifts point at the home lot. buildWorkIntent generalized to
  buildScheduleIntent; isShiftActive handles midnight-wrap shifts. Lets a
  future night-shift resident carry night work + a daytime sleep block.
- Region view: day/night indicator chip + green Community / blue
  Residence lot badges; two toolbar scene-jump buttons (jumpToScene) to
  pose a weekday morning vs late evening.
- Docs (README, CLAUDE.md) + a sleep-shift decision test.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
- Move the Live/Paused status + clock to its own row below the header
  on screens narrower than lg (stays inline in the toolbar on lg+).
- Stack the "Region Overview" title and its action buttons onto separate
  rows below lg; keep them inline on lg+.
- Prevent the header buttons (incl. "Edit region") from wrapping their
  text; the button group wraps as whole buttons instead.
- Move the fill-metric color scale above the map panel to make it more
  prominent (removed the old bottom legend from the hex maps).
- Rename the panel "Resident node health" -> "Resident health".

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The hex map SVG was set to width="100%", so a lot card that stretched
wider (e.g. a lone card on the last row like the Nguyen house) scaled
its hexes up. Render the SVG at its intrinsic pixel size and center it
so every resident hex is the same size regardless of card width.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Port 4000 is taken by another local service on this setup, so the
backend runs on 4001 (PORT=4001). Match the /graphql dev proxy to it.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Capture the Desert Willow region at a weekday morning (residents at the
clinic, library, community center, and school) and at night (everyone
home asleep), and embed them side by side in the README's day/night
section.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The intro used a screenshot of a one-resident test world (Manny + cat),
which is now superseded by the day/night region screenshots. Remove the
image and file, and repoint the surrounding prose at the new shots.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Make the app feel finished and demonstrate multiple towns.

Seed:
- Build two isolated worlds (src/seed.ts): Desert Willow (full, 12
  residents) and Pinehaven (small companion town, 5 residents), via
  seedDesertWillow()/seedPinehaven() with a parameterized makeSchool().

World switcher + default landing (App.vue):
- Header world dropdown fed by a new getWorldsWithRegions query. Needed a
  World.regions field resolver (resolvers/world.ts + index.ts); the worlds
  query now orders createdAt ASC so the oldest world is the default.
- On load at '/', router.replace to the default world's Overview so data
  shows immediately. Last world persisted in localStorage; regionBase
  falls back to the active world's region so nav works from global pages.
- Switching worlds keeps the current section (Residents -> Residents,
  etc.) instead of dropping to Overview.
- Region views refetch on world/region change: added
  watch([worldId, regionId], loadData) to RegionOverview, ActivityLog,
  and LotsAndHouseholds (vue-router reuses the component across param-only
  navigations).

Unfinished-page flag (frontend/src/config/features.ts):
- showUnfinishedPages (env VITE_SHOW_UNFINISHED, default off) hides the
  placeholder nav items (Tickets, Alerts, Calendar, Analytics, Reports,
  Settings) by filtering the nav, not via route redirects.

Docs: refresh the README seed section for two worlds, the switcher, and
the feature flag.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The world and its single region shared a name (Desert Willow /
Desert Willow), so the breadcrumb read "… > Desert Willow > Desert
Willow > Overview" and looked like a bug. Name the regions after
sub-areas instead: Desert Willow → Willow Flats, Pinehaven → Cedar
Hollow. Regenerate the day/night screenshots to match.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The relationship graph highlighted a character's ties on hover but never
said what they were, and no family/social structure was modelled.

- Seed explicit, labelled relationships for both worlds (src/seed.ts):
  families (spouse, mother/father, daughter/son, siblings), coworkers,
  and schoolmates. A relationship's `labels` is a list, so relate() takes
  several per side (e.g. Manny & Elena are "spouse, best friend").
- RelationshipGraph: hovering a character now lists their relationships
  from their own perspective (e.g. "Priya Lin — daughter"); hovering an
  edge shows both directions; the type also appears on the highlighted
  edges via emphasis edgeLabel.
- graphLinks now carries per-direction label arrays (labelsForward/
  labelsBackward) instead of a single ambiguous label, joining multiple
  labels for display.

The town loader already hydrates each character's relationships (with
labels) from the backend, so these show at tick 0.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
It wrapped to two left-aligned lines in the 72px rail; add text-center
so "SERVICE / DESK" is horizontally centered.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Light mode isn't implemented, so the toggle did nothing. Hide it unless
VITE_SHOW_UNFINISHED is set, like the other placeholder features.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The activity log filled with "Could not perform …: Unexpected error"
(a masked INTERNAL_SERVER_ERROR). Root causes:

- The "work" action has no item affordance, so it could never find an
  item and always threw. Treat work as itemless (like calls/texts) — you
  work by being at the workplace.
- A stale/taken item hint failed the whole action. Fall back to any item
  at the current lot that allows the activity before giving up.
- Expected conditions (no spot / all in use / no location) threw plain
  Errors, which GraphQL Yoga masks as "Unexpected error". Throw
  GraphQLError so the real, readable message reaches the client.

Verified: work now succeeds, and a genuinely impossible action returns
"No <action> spot available at this location" instead of a masked 500.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The Library (Lots/Households template tabs) started empty. Seed it from
the same building templates used by the demo worlds — 6 lot templates
(clinic, library, community center, campground, two houses) and 3
household templates (two-parent family, retired couple, working pair).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Clicking a hex on the region map navigated straight to the character's
lot. Instead open a popover with the existing CharacterDetailPanel
(Needs / Bio / Relationships / Memories tabs), and add a "Go to <name>'s
current lot" link that performs the old navigation. Relationships/
memories are hydrated on open via the character panel store.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The ☀️/🌙 buttons existed only to pose screenshots, and their 14-tick
settle loop ran uninterruptibly — it kept advancing the sim while the
status read "Paused", so play/pause appeared broken during a jump.
Remove the buttons and all the jump machinery (App.vue handler,
simulationStore.jumpToScene, simulationDateTimeAt), restoring plain
play/pause. The day/night indicator, lot colour-coding, and the existing
day/night screenshots stay; run the clock to see the town move.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The fixtures built tasks in the old flat shape (no steps/currentStepIndex),
so advanceTask/buildCompletionIntent crashed on task.steps. Build the
fixtures with the real createTaskFromIntent factory and assert against the
current steps-based ActiveTask shape. Full suite now green (254 passing).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Make `vue-tsc --noEmit` clean (was 30 errors) and add an npm `typecheck`
script.

- Source: remove the dead character side-panel in App.vue (its state was
  never set); optional-chain currentLocation guards in
  CharacterRelationshipsTab; fix applyActionEffects call in
  DebugActionPanel to the {characterId,intent} shape; annotate the
  romance/hobby option builders in useCharacterIntentOptions so
  intent.action/source don't widen to string; default item name in the
  db migration; cast db.close() (kuzu ships no types).
- Tests: type the task/ItemData/CharacterState fixtures to the current
  ActiveTask/ItemData/CharacterState shapes (steps, currentStepIndex,
  classification, social-invitation arrays) and give a mock a typed arg.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Set up ESLint 9 with typescript-eslint and eslint-plugin-vue (essential
ruleset) plus `lint`/`lint:fix` scripts. Relax noisy stylistic rules and
allow `_`-prefixed unused args; keep the correctness rules. Fix the 16
resulting errors — unused imports/vars/consts, an empty interface, dead
computeds, and unused resolver args. `eslint .` is now clean.

Also fix .gitignore (a missing newline had merged .DS_Store with the
data.backup pattern).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
GitHub Actions workflow running on pushes to main and PRs targeting main.
Installs with npm ci on the .nvmrc Node version, then runs lint,
typecheck, and the test suite as separate steps so a failure clearly
shows which gate broke.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
A tick does async GraphQL work, so at faster speeds the auto-tick
interval fired again before the previous tick finished. Those ticks
stacked up and kept draining — advancing happiness/needs — even after
the user hit pause. Guard the interval so ticks never overlap and skip
outright when paused, and clear any existing timer before starting a new
one so intervals can't stack.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
With a line per resident, the names were hard to read off the paginated
legend. Label each line at its right end with the character's name,
tilted diagonally (colliding ones hidden), and tilt the time-axis labels
so dense ticks don't clip. Widen the right margin to fit the labels.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Residents autonomously "date" whoever's around — including parents,
children, and siblings — which then earned "attracted to" / "casual
relationship" labels on the relationship graph. Add a blood-family label
set and: skip blood relatives when choosing a date partner, and never
apply romantic milestone labels between them. Since milestone labels are
recomputed each tick, existing family attractions clear themselves.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Rewrite the apologetic "very early / rough edges / not there yet" note
into a confident status statement that leads with what works and the
engineering behind it, while staying honest that it's not
feature-complete. Update the tests note to the passing lint/typecheck/
test + CI gate, point the empty-DB tip at `npm run seed`, and drop a
defensive parenthetical.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This `let` (never reassigned) failed prefer-const; the earlier eslint
--fix corrected it in the working tree but the fix wasn't committed, so
CI would have flagged it. Commit the const.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The day/night section was renamed, breaking the intro's anchor link;
point it at #day-and-night. Add a GitHub Actions CI status badge.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The eat-together relationship tests assert that characters eat during the
local lunch window (hours 11-14). With ISOs like 19:00Z they landed at noon
in MST (pass) but 19:00 in UTC (fail), so CI failed while local passed.

- Pin TZ=UTC in vitest.config.ts so every invocation (CI, IDE, raw npx) is
  deterministic regardless of machine locale
- Belt-and-suspenders TZ=UTC in the test scripts and the CI test step
- Move the three lunch-window test ISOs to 12:00Z so they sit inside 11-14

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@catherineluse
catherineluse merged commit 83cb380 into main Jul 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants