feat(isac): link skill chips to abilities
This commit is contained in:
+40
-44
@@ -1,9 +1,9 @@
|
||||
# Container, staging, and production deployment
|
||||
|
||||
SHD.build is packaged as a Node 24 container using the official SvelteKit Node
|
||||
adapter. The image contains application code, migration and notification
|
||||
scripts, and native runtime dependencies. It does not contain an environment
|
||||
file, database, backups, or user uploads.
|
||||
SHD.build runs in a Node 24 container through the official SvelteKit Node
|
||||
adapter. The image includes application code, migration and notification
|
||||
scripts, and native runtime dependencies. Environment files, databases,
|
||||
backups, and user uploads remain outside the image.
|
||||
|
||||
## Persistent data
|
||||
|
||||
@@ -15,10 +15,9 @@ mounted at `/app/data`. It contains:
|
||||
- normalized user avatars under `avatars/`;
|
||||
- uploaded game-data icons under `game-icons/`.
|
||||
|
||||
Never remove this volume during routine deployments. `docker compose down`
|
||||
without `--volumes` preserves it. Before any manual backup or restore, stop
|
||||
both the `app` and `scheduler` services because both access the embedded
|
||||
database.
|
||||
Keep this volume during routine deployments. `docker compose down` without
|
||||
`--volumes` preserves it. Before a manual backup or restore, stop the `app`
|
||||
and `scheduler` services because both access the embedded database.
|
||||
|
||||
## Staging environment
|
||||
|
||||
@@ -46,9 +45,9 @@ port on the host interfaces so a containerized reverse proxy or tunnel can
|
||||
reach it; firewall the port to trusted LAN/tunnel sources when the host is
|
||||
reachable from other networks.
|
||||
|
||||
The deployment decrypts `.env.staging` on the trusted staging runner into a
|
||||
mode-`0600` runtime file. The decryption key is held by Gitea Actions, not
|
||||
committed or included in the container image.
|
||||
The trusted staging runner decrypts `.env.staging` into a mode-`0600` runtime
|
||||
file. Gitea Actions holds the decryption key; Git and the container image do
|
||||
not.
|
||||
|
||||
The staging container disables default-user seeding. On a brand-new staging
|
||||
database, the existing bootstrap policy grants Admin to the first real account
|
||||
@@ -93,9 +92,9 @@ In `https://git.jb.codes/jb/shd.build/settings/actions`:
|
||||
3. Optionally add repository variable `STAGE_PORT`. It defaults to `5569`.
|
||||
4. Ensure Actions are enabled and the `stage-deploy` runner is online.
|
||||
|
||||
The workflow at `.gitea/workflows/deploy-staging.yml` runs on every push to
|
||||
`main` and can also be started manually. It does not need registry credentials
|
||||
because the trusted staging runner builds a commit-tagged image locally.
|
||||
`.gitea/workflows/deploy-staging.yml` runs for pushes to `main` and manual
|
||||
dispatches. The trusted staging runner builds each commit-tagged image locally,
|
||||
so the workflow needs no registry credentials.
|
||||
|
||||
## Deployment lifecycle
|
||||
|
||||
@@ -110,8 +109,7 @@ The workflow:
|
||||
7. waits for `/api/health/database` to report healthy;
|
||||
8. retains the logs and exits unsuccessfully if health verification fails.
|
||||
|
||||
The short outage is limited to backup, migration, and container startup. This
|
||||
follows the database-preservation policy while minimizing data-entry downtime.
|
||||
The outage covers backup, migration, and container startup.
|
||||
The scheduler calls a bearer-authenticated endpoint on the application service
|
||||
once per minute. Scheduled message delivery therefore uses the web process's
|
||||
existing embedded-database connection instead of opening the locked database
|
||||
@@ -120,11 +118,10 @@ consolidation, and email digest checks and is not published through a host port.
|
||||
|
||||
## Initial development-data bootstrap
|
||||
|
||||
The first staging release can be initialized from the current development
|
||||
database, including users, roles, builds, guides, proposals, audit history,
|
||||
avatars, and uploaded game-data icons. This is deliberately a one-time flow:
|
||||
the staging importer refuses to run after users, builds, audits, or proposals
|
||||
exist in its target database.
|
||||
Initialize the first staging release from the development database, including
|
||||
users, roles, builds, guides, proposals, audit history, avatars, and uploaded
|
||||
game-data icons. The staging importer refuses a target database that contains
|
||||
users, builds, audits, or proposals.
|
||||
|
||||
First stop this project's development server. The embedded database must not be
|
||||
open while its complete transfer bundle is created:
|
||||
@@ -134,8 +131,8 @@ npm run stage:bootstrap:export
|
||||
```
|
||||
|
||||
The command writes a timestamped, ignored JSON bundle under `data/backups/`.
|
||||
It contains user records and password hashes, so transfer it over an encrypted
|
||||
channel and never add it to Git. Copy it to the staging host, then run:
|
||||
The bundle includes user records and password hashes. Transfer it over an
|
||||
encrypted channel, keep it out of Git, then copy it to the staging host and run:
|
||||
|
||||
```sh
|
||||
/opt/shd-build-stage/bootstrap-staging.sh \
|
||||
@@ -160,8 +157,8 @@ Actions. It briefly stops staging database writers, exports only canonical
|
||||
builder-source tables plus uploaded game-data icons, restarts staging, and
|
||||
publishes a `shd-stage-isac-data` artifact for 14 days.
|
||||
|
||||
Download `isac-stage.json` from that workflow run. Then stop this project's
|
||||
development server and run:
|
||||
Download `isac-stage.json` from that workflow run, stop this project's
|
||||
development server, and run:
|
||||
|
||||
```sh
|
||||
npm run isac:import -- /path/to/isac-stage.json --confirm-replace-isac-data
|
||||
@@ -206,10 +203,10 @@ environment, port, and deployment workflow:
|
||||
- Database: `/app/data/shd-build-production.db`
|
||||
- Persistent volume: `shd-build-production-data`
|
||||
|
||||
Routine pushes never import or replace production data. The production deploy
|
||||
stops only its application and scheduler, creates a timestamped JSON backup,
|
||||
applies repeatable migrations, restarts, and verifies database health. Never
|
||||
run `docker compose down --volumes` against production.
|
||||
Routine pushes leave production data unchanged. The production deploy stops its
|
||||
application and scheduler, creates a timestamped JSON backup, applies repeatable
|
||||
migrations, restarts, and verifies database health. Keep
|
||||
`docker compose down --volumes` away from production.
|
||||
|
||||
Add these repository Action secrets:
|
||||
|
||||
@@ -234,8 +231,8 @@ reuse the staging scheduler token.
|
||||
|
||||
### Initial production bootstrap
|
||||
|
||||
The one-time production bootstrap clones current development data and uploads.
|
||||
Stop this project's development server before creating the bundle:
|
||||
The production bootstrap clones current development data and uploads. Stop this
|
||||
project's development server before creating the bundle:
|
||||
|
||||
```sh
|
||||
npm run production:bootstrap:export
|
||||
@@ -269,9 +266,8 @@ notifications, settings, and audit history untouched.
|
||||
|
||||
Manually run **Replace staging ISAC data from production** in Gitea Actions.
|
||||
The workflow exports a consistent production snapshot, restarts production,
|
||||
then stops staging and imports it. The staging importer creates both full and
|
||||
ISAC-only safety backups before replacement. This is intentionally manual and
|
||||
never runs as part of either deployment.
|
||||
then stops staging and imports it. The staging importer creates full and
|
||||
ISAC-only safety backups before replacement. Deployments do not run this flow.
|
||||
|
||||
### Promote managed images without replacing other data
|
||||
|
||||
@@ -280,9 +276,9 @@ development. Export a bundle after finishing local uploads and metadata edits:
|
||||
|
||||
Set the server-only `REMOVE_BG_API_KEY` in each encrypted environment that
|
||||
accepts ISAC uploads. It is sent only from the server in the `X-Api-Key` header.
|
||||
Icon uploads that already contain transparency do not call remove.bg; opaque
|
||||
icon sources use its preview output and then continue through local trimming,
|
||||
white normalization, WebP conversion, and storage.
|
||||
Transparent icon uploads skip remove.bg. Opaque sources use its preview output,
|
||||
then pass through local trimming, white normalization, WebP conversion, and
|
||||
storage.
|
||||
|
||||
```sh
|
||||
npm run images:export
|
||||
@@ -311,13 +307,13 @@ docker compose -f compose.production.yml run --rm \
|
||||
docker compose -f compose.production.yml up -d app scheduler
|
||||
```
|
||||
|
||||
Each import creates a full target database backup before changing anything.
|
||||
New stable IDs are inserted, identical existing assets are skipped, and a
|
||||
different existing record blocks the import. After reviewing an intentional
|
||||
metadata or file replacement, add `--update-existing`. Imported changes append
|
||||
`image.imported` or `image.import_updated` audit events. Remove the transferred
|
||||
bundle after both environments have been verified because it may contain
|
||||
unpublished visual assets and creator identifiers.
|
||||
Each import creates a full target database backup before changes. The importer
|
||||
adds new stable IDs, skips identical assets, and blocks conflicting records.
|
||||
After reviewing an intentional metadata or file replacement, add
|
||||
`--update-existing`. Imported changes append `image.imported` or
|
||||
`image.import_updated` audit events. Remove the transferred bundle after
|
||||
verifying both environments because it may contain unpublished visual assets
|
||||
and creator identifiers.
|
||||
|
||||
The `.env.staging` and `.env.production` npm import commands are intended for
|
||||
maintenance performed directly in an environment where those database and
|
||||
|
||||
+92
-92
@@ -2,14 +2,13 @@
|
||||
|
||||
This document defines the visual language for SHD.build, the independent
|
||||
Division Resurgence community for builds, guides, game data, news, and
|
||||
discussion. New pages should feel like part of the same tactical intelligence
|
||||
network as the admin dashboard, while public pages may use more space and larger
|
||||
editorial typography.
|
||||
discussion. New pages use the same tactical intelligence network as the admin
|
||||
dashboard. Public pages may use more space and larger editorial typography.
|
||||
|
||||
## Design principles
|
||||
|
||||
1. **Tactical, not military cosplay.** Use operational language, compact information density, restrained status indicators, and precise alignment. Avoid camouflage, distressed textures, or decorative weapon imagery in utility interfaces.
|
||||
2. **Data first.** Hierarchy comes from grouping, typography, borders, and spacing. Admin tools should resemble a dependable control surface rather than a collection of marketing cards.
|
||||
1. **Tactical.** Use operational language, compact information density, restrained status indicators, and precise alignment. Avoid camouflage, distressed textures, and decorative weapon imagery in utility interfaces.
|
||||
2. **Data first.** Grouping, typography, borders, and spacing establish hierarchy. Admin tools use a dependable control surface.
|
||||
3. **Orange means action.** SHD orange identifies primary actions, active navigation, focus, and important values. It should not cover large areas.
|
||||
4. **State is always visible.** Loading, disabled, active, archived, configured, error, and success states need both visual and textual indicators.
|
||||
5. **Square and engineered.** Panels and controls use square corners, fine borders, tight spacing, and deliberate grid alignment.
|
||||
@@ -20,42 +19,41 @@ editorial typography.
|
||||
### Routed error console
|
||||
|
||||
Expected HTTP errors and unexpected server faults use one shared SHD exception
|
||||
console inside the normal application shell. The console always displays the
|
||||
console inside the application shell. The console displays the
|
||||
numeric status, an operational title, a safe explanation, request path, node,
|
||||
and context-appropriate recovery controls. Common statuses receive distinct
|
||||
language: authentication for 401, clearance for 403, lost location for 404,
|
||||
conflict for 409, saturation for 429, and service/fault language for 5xx.
|
||||
|
||||
Orange identifies recoverable request errors and actions; red identifies server
|
||||
faults. Never display stack traces, database messages, credentials, request
|
||||
queries, or raw unexpected exceptions to users. Unexpected server faults receive
|
||||
a short reference ID from the global error handler for support correlation while
|
||||
the complete error remains server-side. If the root application shell itself
|
||||
cannot render, `src/error.html` provides a dependency-free SHD fallback surface.
|
||||
faults. Do not display stack traces, database messages, credentials, request
|
||||
queries, or raw unexpected exceptions. The global error handler gives each
|
||||
unexpected server fault a short support reference ID and keeps the full error
|
||||
server-side. If the root application shell cannot render, `src/error.html`
|
||||
provides a dependency-free SHD fallback surface.
|
||||
Error controls retain square geometry, focus-visible treatment, pressed motion,
|
||||
plain-language labels, and stacked mobile recovery actions.
|
||||
|
||||
### Contact relay and legal requests
|
||||
|
||||
Public support uses the SHD Contact Relay surface: a broad transmission form
|
||||
paired with a compact destination guide. Two-to-six destinations use the shared
|
||||
segmented-choice pattern. Signed-in identity details may be prefilled, but the
|
||||
public form remains available to people who cannot sign in. Discord community
|
||||
access is a distinct provider-colored card; it supplements rather than replaces
|
||||
the durable contact channel.
|
||||
Public support uses the SHD Contact Relay: a broad transmission form and a
|
||||
compact destination guide. Two-to-six destinations use the shared
|
||||
segmented-choice pattern. Signed-in identity details may be prefilled; people
|
||||
who cannot sign in retain access to the public form. Discord community access
|
||||
uses a distinct provider-colored card alongside the durable contact channel.
|
||||
|
||||
The Contact hero explicitly resets the global site-header height: semantic page
|
||||
`header` elements must use content-driven height and spacing rather than inherit
|
||||
the 76-pixel navigation-shell constraint. Its title, description, and Discord
|
||||
The Contact hero resets the global site-header height: semantic page `header`
|
||||
elements use content-driven height and spacing instead of the 76-pixel
|
||||
navigation-shell constraint. Its title, description, and Discord
|
||||
card align within a minimum 230-pixel desktop band before the form grid begins.
|
||||
The Contact page itself remains content-height-driven so high-resolution browser
|
||||
zoom does not multiply a viewport minimum into a large empty region above the
|
||||
footer.
|
||||
|
||||
The application frame is a full-viewport vertical flex stack. The shared footer
|
||||
uses automatic top margin, so it rests against the bottom edge on short routes
|
||||
and follows normal document flow after longer content. Individual pages must
|
||||
not add spacer heights solely to position the footer. The frame compensates for
|
||||
The application frame uses a full-viewport vertical flex stack. The shared
|
||||
footer uses automatic top margin. It rests against the bottom edge on short
|
||||
routes and follows document flow after longer content. Do not add spacer heights
|
||||
only to position the footer. The frame compensates for
|
||||
`--shd-display-scale` when calculating its viewport minimum so high-resolution
|
||||
browser zoom does not push a short-page footer below the visible edge.
|
||||
|
||||
@@ -73,8 +71,8 @@ color the complete product name orange.
|
||||
|
||||
### Community platform positioning
|
||||
|
||||
SHD.build is a full Division Resurgence community, not solely a loadout tool.
|
||||
Primary product language and global navigation must represent five durable
|
||||
SHD.build is a Division Resurgence community. Primary product language and
|
||||
global navigation represent five durable
|
||||
channels: News, Database, Builds, Guides, and Builder. Loadout-specific terms
|
||||
remain appropriate inside build routes, but the global brand descriptor,
|
||||
homepage metadata, hero, sign-in introduction, and footer describe the broader
|
||||
@@ -211,12 +209,10 @@ The canonical tokens live in `src/app.css` under `:root`.
|
||||
|
||||
### High-resolution display scaling
|
||||
|
||||
The application uses restrained, stepped interface scaling when the effective
|
||||
CSS viewport is substantially wider than a conventional desktop. At 2200px the
|
||||
complete interface scales to 115%, at 2800px to 130%, and at 3400px to 150%.
|
||||
This preserves the established relationship between typography, controls,
|
||||
tables, spacing, hit targets, and fixed-position surfaces instead of enlarging
|
||||
text alone.
|
||||
The application uses restrained stepped interface scaling on wide CSS
|
||||
viewports. At 2200px, the interface scales to 115%; at 2800px, to 130%; and at
|
||||
3400px, to 150%. This maintains the established relationship among typography,
|
||||
controls, tables, spacing, hit targets, and fixed-position surfaces.
|
||||
|
||||
Use the shared `--shd-display-scale` token and the root-level `zoom` treatment;
|
||||
do not add module-specific 4K font-size overrides. Responsive layouts below
|
||||
@@ -233,7 +229,7 @@ Use the spacing tokens for new components. Prefer `8px`, `12px`, and `20px` gaps
|
||||
|
||||
## Typography
|
||||
|
||||
SHD.build self-hosts two intentional type voices through Fontsource packages:
|
||||
SHD.build self-hosts two type voices through Fontsource packages:
|
||||
|
||||
- **Barlow** is the operational family for display headings, navigation,
|
||||
controls, data, labels, metadata, and general interface copy. Use the shared
|
||||
@@ -243,10 +239,9 @@ SHD.build self-hosts two intentional type voices through Fontsource packages:
|
||||
`--shd-font-editorial`; do not apply it to controls, tables, metrics, or
|
||||
telemetry labels.
|
||||
|
||||
Only the used Barlow weights (400, 600, 700, 800, and 900) and Source Serif 4
|
||||
weights (400 and 600) are loaded. Both families are self-hosted, preserve
|
||||
fallback stacks, and render immediately without a third-party font request.
|
||||
The two-voice rule remains semantic: Barlow operates the system; Source Serif 4
|
||||
Load Barlow weights 400, 600, 700, 800, and 900, plus Source Serif 4 weights
|
||||
400 and 600. Both families are self-hosted, keep fallback stacks, and render
|
||||
without a third-party font request. Barlow operates the system; Source Serif 4
|
||||
explains it.
|
||||
|
||||
- Page and module headings are uppercase, heavy, and tightly spaced.
|
||||
@@ -265,7 +260,7 @@ explains it.
|
||||
### Global mobile navigation
|
||||
|
||||
At viewports up to 900px, replace the hidden desktop link rail and account
|
||||
controls with a clearly labeled square `MENU` control in the shared site
|
||||
controls with a labeled square `MENU` control in the shared site
|
||||
header. Activating it opens a right-side SHD navigation panel over a dismissible
|
||||
backdrop. The panel includes Home, Discover, Builder, Guides, Sign In or My
|
||||
Profile, and ISAC when authorized; authenticated agents also receive identity
|
||||
@@ -289,7 +284,7 @@ squeezed beside long module headings on mobile.
|
||||
### Public pages
|
||||
|
||||
- Maximum content width: approximately 1400–1640px.
|
||||
- Larger headings and more generous vertical rhythm are appropriate.
|
||||
- Public pages may use larger headings and more generous vertical rhythm.
|
||||
- Preserve the grid background and orange accents.
|
||||
- Catalogue and editorial pages use `catalogue-hero` for an angular introductory
|
||||
panel and may pair it with a compact operational control bar.
|
||||
@@ -377,7 +372,8 @@ squeezed beside long module headings on mobile.
|
||||
white Apple mark on black, Discord uses its unmodified white symbol on
|
||||
Blurple, and Steam uses its white linked-circle mark on Steam navy.
|
||||
- Providers that are not configured are disabled, desaturated, and marked
|
||||
`PLANNED`; they must not navigate, submit, or visually imply availability.
|
||||
`PLANNED`; they must not navigate, submit, or imply availability through
|
||||
styling.
|
||||
- When each provider is implemented, replace its placeholder with the
|
||||
provider-rendered control or current pre-approved sign-in artwork where the
|
||||
provider requires it. In particular, use Google Identity Services or its
|
||||
@@ -560,18 +556,17 @@ Do not communicate state by color alone; include a label, icon, or pattern.
|
||||
5. Use the shared tokens instead of introducing near-duplicate colors.
|
||||
6. Verify keyboard focus, pending behavior, empty states, mobile layout, and archived data behavior.
|
||||
|
||||
If a new interface needs a genuinely new pattern, document it here when introducing it.
|
||||
Document a new interface pattern here when you introduce it.
|
||||
|
||||
## Design-system governance
|
||||
|
||||
Significant visual and interaction changes must update this document and the
|
||||
shared implementation in the same change. A change is significant when it
|
||||
introduces or materially revises a reusable token, component pattern, layout,
|
||||
responsive rule, accessibility behavior, motion behavior, or state convention.
|
||||
Update this document and the shared implementation with each significant visual
|
||||
or interaction change. A change qualifies when it introduces or revises a
|
||||
reusable token, component pattern, layout, responsive rule, accessibility
|
||||
behavior, motion behavior, or state convention.
|
||||
|
||||
Small alignment corrections and isolated visual bug fixes do not require a
|
||||
documentation change unless they establish a rule that other interfaces should
|
||||
follow.
|
||||
Small alignment corrections and isolated visual bug fixes need no documentation
|
||||
change unless they establish a reusable rule.
|
||||
|
||||
Repository agents and contributors should follow the maintenance checklist in
|
||||
`AGENTS.md`.
|
||||
@@ -753,7 +748,7 @@ numbers.
|
||||
|
||||
When only one page exists, omit page navigation and row-count presets; retain
|
||||
only the useful result range and any registry search. Do not render disabled
|
||||
pagination controls simply to preserve the rail's shape.
|
||||
pagination controls to preserve the rail's shape.
|
||||
|
||||
Registries with a meaningful name, title, or agent-identity column place the
|
||||
shared server-side search inside this pagination rail. Results update after a
|
||||
@@ -787,13 +782,13 @@ bookmarkable, and expose the selected state with both orange emphasis and
|
||||
textually identifiable active states. Every card explicitly says `FILTER →`
|
||||
or `SELECTED`; do not rely on KPI-card styling alone to communicate that the
|
||||
metrics are controls. Counts describe the full search scope,
|
||||
not merely the visible page. Do not use aggregate facts that cannot filter the
|
||||
affected registry—replace them with actionable subsets.
|
||||
not merely the visible page. Replace aggregate facts that cannot filter the
|
||||
affected registry with actionable subsets.
|
||||
|
||||
### Registry row interaction
|
||||
|
||||
Desktop registry rows are passive data containers unless the entire row
|
||||
actually opens a detail view. Do not apply a whole-row hover surface when only
|
||||
Desktop registry rows are passive data containers unless the entire row opens
|
||||
a detail view. Do not apply a whole-row hover surface when only
|
||||
the trailing Edit or View action is interactive. Render that action as a
|
||||
compact bordered control with hover, focus-visible, and pressed states so its
|
||||
interaction boundary is unmistakable.
|
||||
@@ -812,7 +807,7 @@ and normalization checks remain authoritative.
|
||||
|
||||
### Moderated data states
|
||||
|
||||
ISAC must clearly distinguish direct editing, contributor proposals, and read-only
|
||||
ISAC must distinguish direct editing, contributor proposals, and read-only
|
||||
access. Use a compact access banner above the module rather than changing the
|
||||
registry layout. Pending proposal counts appear as orange numeric indicators in
|
||||
data-navigation items. Proposal lifecycle states use text as well as color:
|
||||
@@ -1077,7 +1072,7 @@ copy; do not apply the system-transmission frame to agent comments.
|
||||
|
||||
Inbox entries expose two distinct actions. `VIEW DETAILS` opens the complete
|
||||
message when preview copy is truncated to two lines. A separate bordered orange
|
||||
response action navigates directly to the known workflow and uses specific
|
||||
response action opens the known workflow and uses specific
|
||||
language such as `UPLOAD NEW AVATAR`, `REVISE CONTRIBUTION`, or
|
||||
`VIEW BUILD & REPLY`; never substitute a generic destination label when the
|
||||
notification type is known. The same response label appears in the detail
|
||||
@@ -1119,20 +1114,18 @@ unfocusable and unchanged.
|
||||
may omit visible text when a canonical, unambiguous game icon exists; they must
|
||||
retain an accessible label and hover title. Text-only segments may omit the
|
||||
default square indicator when the selected background, orange text, and lower
|
||||
rail already communicate state clearly. Do not remove both visible text and an
|
||||
rail communicate state. Do not remove both visible text and an
|
||||
icon from the same option.
|
||||
|
||||
### Skill Chip data console
|
||||
|
||||
Skill Chip editing follows the dense ISAC staged-definition pattern: identity
|
||||
and icon first, the 2-piece numeric bonus second, mutually exclusive primary
|
||||
and secondary roll pools next, and the 3-piece talent last. Superior blue and
|
||||
High-end yellow communicate the only supported qualities; orange remains
|
||||
reserved for actions and current interactive state. A four-step segmented
|
||||
preview exposes the linear zero-to-three High-end interpolation without storing
|
||||
duplicate values. At narrow widths the roll pools and talent columns stack,
|
||||
preview segments become a two-by-two grid, and interactive targets remain at
|
||||
least 44 pixels tall.
|
||||
Skill Chip editing uses the dense ISAC staged-definition pattern: identity and
|
||||
icon, 2-piece numeric bonus, mutually exclusive primary and secondary roll
|
||||
pools, then 3-piece talent. Superior blue and High-end yellow identify the
|
||||
supported qualities; reserve orange for actions and the current interactive
|
||||
state. A four-step segmented preview shows the linear zero-to-three High-end
|
||||
interpolation without duplicate values. At narrow widths, stack roll-pool and
|
||||
talent columns, use a two-by-two preview grid, and retain 44-pixel targets.
|
||||
|
||||
### ISAC record identity controls
|
||||
|
||||
@@ -1145,12 +1138,12 @@ drop-zone to fill spare identity-row space.
|
||||
Icon-enabled consecutive-entry editors pass a successful-add result token into
|
||||
the shared upload control. Advancing that token clears the native file input,
|
||||
temporary preview URL, filename, drag/paste feedback, and validation state.
|
||||
This reset occurs only after a validated add succeeds; failed saves preserve
|
||||
the selected image, and successful edits close the editor normally.
|
||||
Reset only after a validated add succeeds. Failed saves preserve the selected
|
||||
image; successful edits close the editor.
|
||||
|
||||
The primary `name` field in every ISAC data editor is a header-level input:
|
||||
46 pixels tall with larger, heavier text. This creates a consistent first
|
||||
reading point for long registries while keeping secondary metadata compact.
|
||||
The primary `name` field in each ISAC data editor is a 46-pixel header-level
|
||||
input with larger, heavier text. It gives long registries a consistent first
|
||||
reading point while keeping secondary metadata compact.
|
||||
|
||||
`SearchableAssignment` is the standard relationship picker for long attribute,
|
||||
talent, trigger, and compatibility lists. Multiple mode manages additive sets;
|
||||
@@ -1621,9 +1614,9 @@ second. Use a compact angular command band rather than a cinematic display
|
||||
hero: keep the archive identity, state how many registries are online, state the
|
||||
record total, and present contribution as a bordered secondary action.
|
||||
|
||||
Place one discovery bar directly below the command band. Its label presents
|
||||
browsing and global search as equal paths—select a registry or search all
|
||||
data—and its search field searches across all public registry names and useful
|
||||
Place one discovery bar below the command band. Its label gives users two equal
|
||||
paths: select a registry or search all data. Its search field searches across
|
||||
all public registry names and useful
|
||||
descriptive data such as Talent text, Damage Types, item slots, qualities, and
|
||||
Brand bonus Attributes. Do not stack a separate registry-heading bar above or
|
||||
below it. Results name both the matching record and its registry, then link into
|
||||
@@ -1640,12 +1633,11 @@ workflow-centric states are not exposed as primary public labels.
|
||||
|
||||
### ISAC progression-tree composer
|
||||
|
||||
Use the Specialization workbench pattern for ordered game systems whose
|
||||
meaning depends on both hierarchy and spatial placement. Keep the registry,
|
||||
selected parent identity, child navigation, readiness telemetry, and visual
|
||||
tree in one operational surface. Editors still open above the registry and
|
||||
follow the standard ISAC lifecycle; the workbench is navigation and context,
|
||||
not a replacement modal.
|
||||
Use the Specialization workbench pattern for ordered game systems that depend
|
||||
on hierarchy and spatial placement. Keep the registry, selected parent identity,
|
||||
child navigation, readiness telemetry, and visual tree in one operational
|
||||
surface. Editors open above the registry and follow the standard ISAC lifecycle.
|
||||
Use the workbench for navigation and context.
|
||||
|
||||
The tree uses a stable 12-column track per row. Row and column are explicit
|
||||
numeric fields so placement is keyboard operable, auditable, and portable;
|
||||
@@ -1662,20 +1654,19 @@ the orange identity rail plus the explicit `1 / 1 · FREE` state; pending,
|
||||
archived, incomplete, and builder-ready states must remain textual as well as
|
||||
colored.
|
||||
|
||||
Treat allocation scope as first-class telemetry. Specialization progression is
|
||||
unlocked once for a specialization, but every focus receives the full point
|
||||
allocation independently. Show both the point total and that scope near the
|
||||
tree; do not imply that focuses draw from one shared spend pool. A focus is
|
||||
builder-ready only after it has three distinct active abilities, contiguous
|
||||
tree rows, at least one paid node, and exactly the configured available point
|
||||
total.
|
||||
Show allocation scope as first-class telemetry. A specialization unlocks once;
|
||||
each focus receives its own full point allocation. Show the point total and
|
||||
scope near the tree. A focus becomes builder-ready after it has three distinct
|
||||
active abilities, each ability has exactly three active Skill Chip definitions,
|
||||
tree rows are contiguous, at least one paid node exists, and the tree uses the
|
||||
configured available point total.
|
||||
|
||||
Compose focus identity from the uploaded specialization artwork and the
|
||||
canonical Firepower, Toughness, or Engineering icon. Keep the specialization
|
||||
art primary and the attribute mark as a smaller corner badge. Ability slots 1
|
||||
and 3 are mirrored as free tree nodes; slot 2 is labeled Combat Skills-only and
|
||||
must not appear in the upgrade tree. Universal grenade and medical-kit actions
|
||||
are not specialization abilities.
|
||||
Compose focus identity from uploaded specialization artwork and the canonical
|
||||
Firepower, Toughness, or Engineering icon. Keep the specialization art primary
|
||||
and place the attribute mark in a smaller corner badge. Mirror ability slots 1
|
||||
and 3 as free tree nodes. Label slot 2 Combat Skills-only and exclude it from
|
||||
the upgrade tree. Universal grenade and medical-kit actions are outside the
|
||||
specialization ability set.
|
||||
|
||||
Use `StructuredEffectEditor` whenever an ISAC record needs an ordered set of
|
||||
calculation-ready effects. It provides Attribute, Ability when applicable, and
|
||||
@@ -1686,6 +1677,15 @@ do not yet support. Oversight previews must render effect and rank summaries as
|
||||
structured cards. Serialized JSON belongs only in the expandable raw-evidence
|
||||
panel.
|
||||
|
||||
Exclusive child-record relationships use a searchable assignment block directly
|
||||
after the record identity. The header names the parent type, states the
|
||||
relationship and capacity in plain language, and shows current usage as `N / N`.
|
||||
The registry repeats the assigned parent beside each child so exclusivity can be
|
||||
audited without opening the editor. Legacy records may remain visibly
|
||||
`UNASSIGNED`, but creation, restoration, and successful edits require a valid
|
||||
active parent. Capacity rules are enforced on the server and never communicated
|
||||
by color alone.
|
||||
|
||||
Rank cards present the complete rank description, structured effects, point
|
||||
cost, Credits, and material cost together. Specialized Alpha, Beta, and Gamma
|
||||
components use the shared blue quality treatment and may be combined in any
|
||||
|
||||
+32
-36
@@ -1,25 +1,23 @@
|
||||
# Weapon and gear stat level scaling
|
||||
|
||||
This document records the empirically observed rules for adjusting weapon and
|
||||
gear stat ranges by item level. The rules apply independently to both endpoints
|
||||
of a configured range.
|
||||
Use these observed rules to adjust weapon and gear stat ranges by item level.
|
||||
Apply each rule to both endpoints of a configured range.
|
||||
|
||||
These formulas are based on in-game observations rather than published game
|
||||
mechanics. Preserve source observations when adding new samples and revise the
|
||||
constants if a larger data set disproves them.
|
||||
In-game observations, rather than published game mechanics, support these
|
||||
formulas. Preserve source observations with new samples and revise constants
|
||||
when a larger data set disproves them.
|
||||
|
||||
## Numeric representation and display
|
||||
|
||||
Stat ranges must retain their unrounded decimal values. The integer shown by the
|
||||
game is consistent with truncation:
|
||||
Store stat ranges as unrounded decimals. The game display truncates them:
|
||||
|
||||
```text
|
||||
displayedValue = floor(rawValue)
|
||||
```
|
||||
|
||||
Do not use `ceil`, and do not repeatedly level an already displayed integer.
|
||||
Both approaches accumulate errors. A level-1 value displayed as `0` can still
|
||||
have a positive fractional raw value that becomes visible at a higher level.
|
||||
Avoid `ceil` and repeated leveling of displayed integers. Both accumulate
|
||||
errors. A level-1 display of `0` can hide a positive fractional raw value that
|
||||
appears at a higher level.
|
||||
|
||||
For example, a standard gear item displayed `0 ENG` at level 1 and `2 ENG` at
|
||||
level 42. With a level-42 multiplier of `3.01`, a raw base near `0.7` explains
|
||||
@@ -30,12 +28,12 @@ floor(0.7) = 0
|
||||
floor(0.7 × 3.01) = 2
|
||||
```
|
||||
|
||||
When only displayed integers are available, a prediction can be off by one.
|
||||
That is expected information loss, not evidence for rounding upward.
|
||||
Predictions from displayed integers can miss by one because truncation loses
|
||||
information. That error does not indicate rounding upward.
|
||||
|
||||
## Shared progression model
|
||||
|
||||
The stable portion of both progressions is linear, not compounded:
|
||||
The stable portion of both progressions is linear:
|
||||
|
||||
```text
|
||||
rawStat(level) = hiddenBase × multiplier(level)
|
||||
@@ -48,8 +46,8 @@ fixed `5%` of the hidden base:
|
||||
rawStat(level + 1) - rawStat(level) = hiddenBase × 0.05
|
||||
```
|
||||
|
||||
Never calculate a new level as `previousDisplayedValue × 1.05`; that describes
|
||||
compound growth and does not match the observations.
|
||||
Do not calculate a new level as `previousDisplayedValue × 1.05`. That compound
|
||||
growth model conflicts with the observations.
|
||||
|
||||
For a range `[minimum, maximum]`, calculate each endpoint separately:
|
||||
|
||||
@@ -58,7 +56,7 @@ scaledMinimum = hiddenMinimum × multiplier(level)
|
||||
scaledMaximum = hiddenMaximum × multiplier(level)
|
||||
```
|
||||
|
||||
Only truncate when producing a game-matching display value.
|
||||
Truncate at the game-display boundary.
|
||||
|
||||
## Gear
|
||||
|
||||
@@ -68,9 +66,9 @@ The observed stable gear multiplier is:
|
||||
gearMultiplier(level) = 0.91 + 0.05 × level
|
||||
```
|
||||
|
||||
This rule is validated for levels 10 through 50 across Standard, Specialized,
|
||||
and High-end samples. Quality changes the hidden stat ranges; the observed
|
||||
late-level multiplier does not change with quality.
|
||||
Samples across Standard, Specialized, and High-end qualities validate this rule
|
||||
for levels 10 through 50. Quality changes hidden stat ranges; the observed
|
||||
late-level multiplier remains the same.
|
||||
|
||||
Given hidden level-independent endpoints:
|
||||
|
||||
@@ -115,11 +113,10 @@ ENG = floor(0.7 × 3.01) = 2
|
||||
|
||||
### Early gear levels
|
||||
|
||||
Levels 1 through 9 show a quality-dependent transition and do not follow the
|
||||
stable multiplier directly. Do not extrapolate that transition from a single
|
||||
level-1-to-level-2 change. Until the early curves are sampled more completely,
|
||||
store authoritative ranges for those levels or mark calculated results as
|
||||
estimates.
|
||||
Levels 1 through 9 show a quality-dependent transition outside the stable
|
||||
multiplier. Do not extrapolate it from one level-1-to-level-2 change. Until
|
||||
more samples define the early curves, store authoritative ranges or label
|
||||
calculated results as estimates.
|
||||
|
||||
## Weapons
|
||||
|
||||
@@ -179,9 +176,9 @@ multiplier, then derive the combined pool bounds by multiplying each endpoint
|
||||
by three. Redistribution changes the split between the three cores without
|
||||
changing the selected item's total allocation.
|
||||
|
||||
The `1.14` offset is provisional and is currently validated only for the
|
||||
level-40-to-level-50 interval. Preserve enough decimal precision to revise it
|
||||
without migrating rounded data.
|
||||
The `1.14` offset remains provisional; observations only validate it from level
|
||||
40 to 50. Retain enough decimal precision to revise it without migrating
|
||||
rounded data.
|
||||
|
||||
### Weapon example
|
||||
|
||||
@@ -192,9 +189,9 @@ A weapon changed from `753 DMG` at level 40 to `873 DMG` at level 50:
|
||||
floor(872.95) = 872
|
||||
```
|
||||
|
||||
The displayed-source calculation misses by one because `753` is already
|
||||
truncated. Applying the ratio to the unrounded source value produces the
|
||||
observed `873`. This is why calculations must use stored decimal endpoints.
|
||||
The calculation misses by one because `753` is truncated. The ratio applied to
|
||||
the unrounded source value produces `873`, so calculations require stored
|
||||
decimal endpoints.
|
||||
|
||||
The observed changes over those ten levels were linear:
|
||||
|
||||
@@ -207,11 +204,10 @@ ENG: 45 → 52 (about 0.7 per level)
|
||||
|
||||
### Early weapon levels
|
||||
|
||||
Do not apply the provisional late-level weapon offset to level 1. An Exotic
|
||||
sample's displayed level-1 values and level-50 values fit a `2.91` endpoint
|
||||
ratio for most stats, which is incompatible with simply extending the
|
||||
level-40-to-level-50 offset backward. Weapons therefore also appear to have an
|
||||
early transition or another quality-dependent input.
|
||||
Do not apply the provisional late-level weapon offset at level 1. An Exotic
|
||||
sample's level-1 and level-50 displays fit a `2.91` endpoint ratio for most
|
||||
stats, which conflicts with extending the level-40-to-level-50 offset
|
||||
backward. Weapons appear to have an early transition or quality-dependent input.
|
||||
|
||||
Until more intermediate samples are available:
|
||||
|
||||
|
||||
+43
-37
@@ -1,6 +1,6 @@
|
||||
# SHD.build architecture
|
||||
|
||||
This project is intentionally being built in vertical slices.
|
||||
The project is built in vertical slices.
|
||||
|
||||
## Planned stack
|
||||
|
||||
@@ -18,7 +18,8 @@ This project is intentionally being built in vertical slices.
|
||||
- Editor: manage builder source data.
|
||||
- Admin: manage users, roles, source data, and audit history.
|
||||
|
||||
Authorization must be checked on the server for every mutation. Hiding an admin link is not authorization.
|
||||
Server code checks authorization for each mutation. Hiding an admin link does
|
||||
not grant authorization.
|
||||
|
||||
Weapon and gear range calculations must preserve unrounded decimal endpoints
|
||||
and follow the empirically validated progression rules in
|
||||
@@ -26,16 +27,15 @@ and follow the empirically validated progression rules in
|
||||
linear rather than compounded and game-matching integer values are truncated
|
||||
only at the presentation boundary.
|
||||
|
||||
Local credentials are stored as Argon2id PHC strings with a unique random salt
|
||||
per password. Current work factors follow OWASP's 19 MiB / 2 iteration /
|
||||
single-lane minimum profile and can be increased over time; successful login
|
||||
will eventually rehash credentials whose parameters are outdated.
|
||||
Local credentials use Argon2id PHC strings with a unique random salt per
|
||||
password. Current work factors meet OWASP's 19 MiB / 2 iteration / single-lane
|
||||
minimum profile. Successful login rehashes credentials with outdated parameters.
|
||||
|
||||
## Database runtime
|
||||
|
||||
The application uses Turso Database in-process through its native Node package.
|
||||
It opens a local SQLite-compatible database file and does not use Turso Cloud,
|
||||
libSQL HTTP, remote replicas, auth tokens, or synchronization.
|
||||
It opens a local SQLite-compatible database file. The application does not use
|
||||
Turso Cloud, libSQL HTTP, remote replicas, auth tokens, or synchronization.
|
||||
|
||||
The database defaults to `./data/shd-build.db`. Production deployments must
|
||||
provide a persistent writable filesystem and can override the location with
|
||||
@@ -92,13 +92,12 @@ not locked by another process.
|
||||
|
||||
## Development database maintenance
|
||||
|
||||
The development database remains valuable persistent data, but it is no longer
|
||||
served to outside contributors and does not require a low-downtime migration
|
||||
window. Stop only this project's development server before backup, export,
|
||||
restore, migration, or other work requiring exclusive access. Create and verify
|
||||
a JSON backup, perform the prepared operation, verify schema state and critical
|
||||
record counts, then restart and health-check the application. Prefer additive,
|
||||
repeatable migrations and preserve the verified backup if maintenance fails.
|
||||
Treat the development database as persistent data. Stop this project's
|
||||
development server before backup, export, restore, migration, or other work
|
||||
requiring exclusive access. Create and verify a JSON backup, perform the
|
||||
prepared operation, verify schema state and critical record counts, then
|
||||
restart and health-check the application. Prefer additive, repeatable
|
||||
migrations and retain the verified backup if maintenance fails.
|
||||
|
||||
Production and staging remain shared persistent environments and retain the
|
||||
deployment, backup, synchronization, and maintenance safeguards documented in
|
||||
@@ -120,13 +119,14 @@ mutation rather than deletion or modification of the original event:
|
||||
before-snapshot.
|
||||
3. The UI requires a second, explicit confirmation.
|
||||
4. The server reloads the target and compares it with the original
|
||||
after-snapshot, ignoring bookkeeping timestamps. A mismatch means newer work
|
||||
exists and blocks automatic compensation.
|
||||
after-snapshot, ignoring bookkeeping timestamps. A mismatch indicates newer
|
||||
work and blocks automatic compensation.
|
||||
5. Referenced roles, permissions, and relationships are revalidated before
|
||||
restoration. Restoring administrator authority additionally requires full
|
||||
administrative access.
|
||||
6. The server restores the previous state and appends an `audit.reverted` event
|
||||
whose target is the original audit-event ID.
|
||||
7. A reverted event cannot be reverted again.
|
||||
|
||||
# Moderated game-data contributions
|
||||
|
||||
@@ -156,8 +156,6 @@ assignment, so it applies consistently to existing users and every account
|
||||
creation method. Registered agents can inspect live source data and submit
|
||||
moderated proposals, but cannot directly mutate canonical records, review the
|
||||
queue, or access unrelated ISAC operations without additional permissions.
|
||||
7. A successfully reverted event cannot be reverted again.
|
||||
|
||||
Unsupported destructive identity operations, account deletion, incomplete
|
||||
snapshots, stale targets, and events whose dependencies no longer exist are
|
||||
marked non-reversible or rejected. Recovery for those cases requires a
|
||||
@@ -256,7 +254,7 @@ from Firepower, Toughness, Engineering, Health, or Armor.
|
||||
The two secondary slots draw distinct attributes from one shared option pool.
|
||||
Each available secondary attribute stores its own minimum and maximum for all
|
||||
four qualities. Gear tier determines capacity without per-item configuration:
|
||||
T0 gear has one Gear Mod slot, while T1 and T2 gear have two.
|
||||
T0 gear has one Gear Mod slot; T1 and T2 gear have two.
|
||||
|
||||
Canonical data is stored in `gear_mods`, `gear_mod_quality_values`,
|
||||
`gear_mod_secondary_options`, and `gear_mod_secondary_quality_values`.
|
||||
@@ -274,37 +272,45 @@ and secondary options reference active attributes that define Skill Chip
|
||||
minimum and maximum roll values; a configured chip rolls one primary and two
|
||||
distinct secondary stats from those separate pools.
|
||||
|
||||
Every active Skill Chip definition belongs exclusively to one active
|
||||
specialization ability through `skill_chip_sets.specialization_ability_id`.
|
||||
An ability has capacity for exactly three active definitions. The save and
|
||||
restore actions enforce that capacity, while legacy rows may remain nullable
|
||||
until they are edited. A specialization focus is builder-ready only when each
|
||||
of its three assigned abilities has all three active Skill Chip definitions.
|
||||
Skill Chip mutations refresh readiness for focuses that reference both the
|
||||
previous and resulting ability.
|
||||
|
||||
Both set-bonus progression systems use the number of equipped High-end chips as
|
||||
the interpolation step. For `h` High-end chips, where `h` is zero through
|
||||
three, a configured minimum and maximum resolve as
|
||||
`minimum + (maximum − minimum) × h / 3`. Zero High-end chips therefore produce
|
||||
the minimum, while three produce the maximum. The editor stores only endpoints
|
||||
and previews all four states, preventing duplicated tier data from diverging.
|
||||
the minimum, while three produce the maximum. The editor stores endpoints and
|
||||
previews all four states, preventing duplicated tier data from diverging.
|
||||
Skill Chip mutations, owned relationships, and safe icon metadata are captured
|
||||
in the `game_data` audit stream.
|
||||
|
||||
# Game-data icon storage
|
||||
|
||||
Talents, brand sets, OS Protocols, and Skill Chips reference uploaded artwork through the
|
||||
polymorphic `game_data_icons` metadata table. Original uploads are never
|
||||
retained. The shared server pipeline accepts transparent SVG, PNG, WebP, and
|
||||
AVIF sources, rejects active or externally referenced SVG content, and uses
|
||||
Sharp’s asynchronous native worker pool to rasterize and normalize every
|
||||
accepted icon to a transparent 256 × 256 WebP.
|
||||
Talents, brand sets, OS Protocols, and Skill Chips reference uploaded artwork
|
||||
through the polymorphic `game_data_icons` metadata table. The shared server
|
||||
pipeline accepts transparent SVG, PNG, WebP, and AVIF sources, rejects active
|
||||
or externally referenced SVG content, and uses Sharp’s native worker pool to
|
||||
rasterize and normalize each accepted icon to a transparent 256 × 256 WebP.
|
||||
|
||||
Normalized files live beneath the ignored `data/game-icons/` directory and are
|
||||
served by the validated `/icons/game-data/[type]/[id]` endpoint with immutable
|
||||
cache headers and MIME sniffing disabled. ISAC save actions process files
|
||||
before mutation, apply upload or removal after record validation, and include
|
||||
only safe icon metadata—not image bytes—in their audit snapshots.
|
||||
safe icon metadata, rather than image bytes, in audit snapshots.
|
||||
|
||||
Icon metadata is optional. Canonical records and contributor proposals may be
|
||||
created without an upload, allowing values and artwork to be contributed in
|
||||
separate dependency-safe changes. Missing artwork is represented at render
|
||||
time by the static `/missing-item-icon.svg` asset through the shared
|
||||
`MISSING_ITEM_ICON_URL` constant and `ItemIcon` component; the placeholder is never inserted into
|
||||
`game_data_icons`. A later edit or proposal can attach the real icon without
|
||||
recreating or replacing the canonical item.
|
||||
`MISSING_ITEM_ICON_URL` constant and `ItemIcon` component. The placeholder does
|
||||
not enter `game_data_icons`. A later edit or proposal can attach the real icon
|
||||
without recreating the canonical item.
|
||||
# Oversight notification dispatch
|
||||
|
||||
New Data Change and Avatar Oversight review items are routed only to agents who
|
||||
@@ -321,11 +327,11 @@ scheduler instance:
|
||||
```
|
||||
|
||||
The dispatcher groups due rows by reviewer, queue, cadence, and delivery
|
||||
window. Daily rows are assigned to the next 10:00 AM wall-clock boundary in
|
||||
`America/New_York`, including daylight-saving transitions. Successfully
|
||||
dispatched rows retain `delivered_at` evidence and are never selected again.
|
||||
The existing ISAC notification delivery preferences still determine optional
|
||||
browser and email fan-out after the consolidated inbox message is created.
|
||||
window. Daily rows use the next 10:00 AM wall-clock boundary in
|
||||
`America/New_York`, including daylight-saving transitions. Dispatched rows
|
||||
retain `delivered_at` evidence and are excluded from later selection. Existing
|
||||
ISAC notification delivery preferences determine optional browser and email
|
||||
fan-out after the consolidated inbox message is created.
|
||||
|
||||
## Automated ISAC system messages
|
||||
|
||||
|
||||
Reference in New Issue
Block a user