Shared modules for the Upland Exhibits app suite. One repo, one package, subpath modules — consumed as a tag-pinned git dependency so updates are deliberate:
"@upland/shared": "github:flinthillsdesign/upland-shared#v0.1.0"-
@upland/shared/timing— the firm-wide design/fab phase-shape timing model.TIMING_PROJECTS(the measured calibration set) is the source of truth;TIMING_DESIGN/TIMING_FAB/STD_SPLITare derived from it. Also the reads:resampleTiming,interpTiming,cumShare,expectedBurn, andTIMING_VERSION(stamped into ODIN retros).Recalibrating: edit
TIMING_PROJECTS, bumpTIMING_VERSIONand the package version, tag, then bump the pin in each app when it's ready. -
@upland/shared/months— the absolute-month-key convention the scheduler and ODIN share. -
@upland/shared/palette— the design/fab team colors. -
@upland/shared/dates— Upland days are Kansas days.centralToday,centralDay,centralDayPlus,formatCentral, andparseStamp(a stored SQLite timestamp is UTC, not local time). Safe in the browser. -
@upland/shared/mail—sendMail, the one Postmark sender: links never rewritten, opens never tracked, one From shape, never throws, and "not configured" is not "sent". No dependency (fetch). Server only. -
@upland/shared/db—withRetry: one retry for a dropped Turso connection, every argument passed through. Server only. -
@upland/shared/ai—createAi({ anthropic }): the one wrapper around the Anthropic client (effort default, forced-tool relax on Opus 5.5, fallbacks on refusal, cut-off reporting, retries), plusfirstText,wasTruncated,wasRefused,toolInput. The app passes its own SDK client in; no dependency here. Server only.
Planned next: contracts (cross-app API shapes: project registry, retro_v1,
scheduler feed) and voice (the durable Upland style guide + prompt
fragments for AI features).
| App | Server | Frontend |
|---|---|---|
| upland-scheduler | lib/coaching.ts require |
public/js/timing.js generated at build (globals) |
| upland-odin | lib/retro.ts require |
public/js/timing.ts re-export, bundled at build |
Frontends can't import node_modules at runtime (both apps serve unbundled
ES modules / classic scripts), so each app's build.js bakes this package
into its browser timing file. Server code requires it directly — Netlify
bundles function dependencies.