38 KiB
SHD.build architecture
The team builds the project in vertical slices.
Application stack
- SvelteKit 2 / Svelte 5 with TypeScript
- Embedded Turso Database via
@tursodatabase/databasefor durable application data - Auth.js-compatible server authentication with Discord OAuth and credentials providers
- Argon2id password hashing, server-side sessions, CSRF protection, and role checks
- Browser
localStorageonly for anonymous build drafts
Product boundaries
- Public: browse, sort, and view published builds and guides.
- Anonymous: create and edit local drafts.
- Authenticated: publish, rate, comment, favorite, copy, and manage owned builds.
- Editor: manage builder source data.
- Admin: manage users, roles, source data, and audit history.
Server code checks authorization for each mutation. UI visibility has no effect on server authorization.
Weapon and gear range calculations must preserve decimal endpoints
and follow the tested progression rules in
STAT_LEVEL_SCALING.md. Leveling is linear rather than
compounded. The presentation layer truncates game-matching integer values.
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. 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
TDR_DATABASE_PATH. This rules out ephemeral serverless filesystems unless a
deployment attaches a persistent volume.
Site launch controls
The singleton site_launch_settings row stores the public launch gate, account
creation state, and availability for News, Database, Builds, Guides, and
Builder. Schema migration creates the row with Coming Soon enabled, account
creation enabled, and all five sections enabled. The application caches the row
for five seconds and clears the process cache after an operator saves changes.
Agents with site.settings.manage edit the row from ISAC → System
Configuration → Site Controls. The save action requires an explicit value for
all seven controls and a confirmation token. A successful update records
system_configuration / site_access.updated with the operator, target row, and
complete before/after snapshots.
The request hook applies the launch gate to anonymous traffic and section availability to every request:
- With Coming Soon enabled, anonymous
GETandHEADrequests redirect to/coming-soon. Anonymous mutations outside exempt routes receive a JSON 404. - Authenticated agents bypass the Coming Soon gate and enter the application.
- A disabled public section blocks its route family for every user, including
authenticated agents:
/news,/database,/builds,/guides, or/builder.GETandHEADrequests redirect to/coming-soonwith the unavailable section identified. Mutations receive a JSON 404. - Authentication callbacks, sign-in, contact, legal pages, health endpoints, scheduler endpoints, application assets, and the prelaunch page remain reachable without a session.
The global navigation omits disabled sections for every user. Server checks
remain authoritative for direct URLs and mutations. An authenticated operator
may open /coming-soon to preview the promotional page, but the preview does
not override section availability.
Account creation uses a separate control. Closing registration blocks password registration, a magic-link request for an unknown email, and Discord OAuth for an unknown Discord identity. Existing password, email, and Discord identities can still sign in. The sign-in page hides the password registration form and explains registration closure. This guard runs on the server, so a crafted form submission or OAuth callback cannot create an account while registration is closed.
Increment plan
- Product shell and anonymous local-draft builder.
- Embedded Turso migrations, repositories, and seed data.
- Credentials and Discord authentication.
- Full equipment/stat builder driven by database definitions.
- Publishing, discovery, ratings, comments, favorites, and copy flow.
- Rich build guides and moderation.
- Admin editors, validation, audit log, and data import/export.
Administrator bootstrap
The authentication service promotes the first authenticated user to admin if the
database does not contain an administrator. Promotion uses one conditional
database update so concurrent first sign-ins cannot both become administrators.
Once an administrator exists, later sign-ins never alter roles.
Database backup and restore
Embedded Turso databases contain contributor-entered source data and must be preserved across development changes.
Create a timestamped development backup:
npm run db:backup
Create a production backup:
npm run db:backup:production
The scripts write versioned JSON documents beneath data/backups/ by default.
Git ignores that directory because backups contain private user data,
including password hashes.
To restore, first create an empty database and apply the schema. Then run:
npm run db:restore -- data/backups/<backup-file>.json
The restore operation refuses to import into any database containing records. This guard prevents accidental merging or overwriting of contributor data. Stop the application before backup or restore so the embedded database file is not locked by another process.
Development database maintenance
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
docs/DEPLOYMENT.md.
ISAC audit and compensating reversals
Every successful ISAC mutation appends an event to audit_log through the
shared server audit utility. Events identify the operator, operational category,
specific action, target entity, timestamp, and useful before/after evidence.
The audit registry is read-only and requires audit.view; the seeded Auditor
role grants that permission without granting mutation capabilities.
Audit history is immutable. The server implements “Revert” as a new compensating mutation rather than deletion or modification of the original event:
- The operator must hold
audit.revertin addition to any other access. - The reversion registry must support the original action and the event must contain a usable before-snapshot.
- The UI requires a second, explicit confirmation.
- The server reloads the target and compares it with the original after-snapshot, ignoring bookkeeping timestamps. A mismatch indicates newer work and blocks automatic compensation.
- The server revalidates referenced roles, permissions, and relationships before restoration. Restoring administrator authority also requires full administrative access.
- The server restores the previous state and appends an
audit.revertedevent whose target is the original audit-event ID. - The server rejects a second reversion of the same event.
Moderated game-data contributions
ISAC data access uses data.view, data.propose, data.manage, and
data.approve. Data Viewers can inspect registries. Data Contributors can submit
the same editor forms, but the server serializes each request into
data_change_proposals instead of mutating canonical tables. Data Oversight
reviews the queue and must confirm approval or rejection.
Approval replays the stored form through its original SvelteKit server action.
This preserves canonical validation, relational updates, icon processing, and the
normal game-data audit event. Proposal creation and review decisions also produce
separate data_moderation audit events. Failed canonical validation marks a
proposal as failed without changing live data. Pending counts appear alongside
each data registry in ISAC so collaborators can see concurrent suggestions.
The protected seeded roles are Data Viewer, Data Contributor, Data Editor, and Data Oversight. Data Editor retains direct-write access for trusted operators; Data Oversight combines direct-write access with queue review. New ISAC data modules must use the shared moderation interception path and audit both the final mutation and proposal lifecycle.
Mechanic programs remain part of their owning source record. Talent, OS Protocol, Skill Chip, and Specialization proposals carry the complete typed program with the rest of the source payload. Oversight reviews and approves that program through the source module's canonical validation path. There is no standalone Trigger proposal module or pending Trigger dependency.
Each authenticated, active registered agent receives isac.access,
data.view, and data.propose as baseline permissions in the authorization
layer. The authorization layer grants this baseline without a removable role
assignment, so existing users and each account-creation method receive the same
access. Registered agents can inspect live source data and submit
moderated proposals, but cannot mutate canonical records, review the
queue, or access unrelated ISAC operations without additional permissions.
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
specialized workflow rather than inferred database writes.
System metrics
ISAC uses /isac as its canonical route namespace. The server
redirects legacy /admin URLs to their matching /isac location so old
notifications and external bookmarks remain usable.
The read-only /isac/metrics route requires metrics.view and collects a
point-in-time process-local health snapshot. Node and operating-system APIs
provide process CPU and memory, event-loop utilization and delay, host memory,
load average, core count, platform, and uptime. Embedded Turso pragmas provide
page allocation, page size, and free-page information; aggregate SQL queries
provide application record volumes.
The interface refreshes on demand and may poll every ten seconds while the operator keeps auto-refresh enabled. CPU and event-loop values are deltas from the previous server-side sample, so the first sample and samples separated by long gaps provide direction rather than a monitoring-grade time series. Production alerting and historical analysis should use a dedicated external telemetry system. This surface omits secrets, environment-variable values, and full database paths.
The same surface reports remove.bg paid credits and free API calls as separate
capacity pools. The scheduler reconciles the account balance no more often than
the configured interval, while successful image processing applies the
provider's X-Credits-Charged value to the persisted estimate. Warning and
critical transitions create one alert episode with a unique delivery for each
active Administrator. A later critical transition may alert again; repeated
scheduler runs within the same episode do not. Provider errors preserve the
last known balance and never expose the API key or raw response body.
Weapon-owned exotic talents
Standard weapon talent pools remain reusable across weapons and use Talent 1 /
Top or Talent 2 / Bottom. One exotic weapon owns each exotic talent through
weapons.exotic_talent_id.
The weapon editor creates and updates the underlying normalized talents,
talent_tiers, description template, and typed conditional rule together with
the weapon. Server validation rejects an exotic talent ID owned by another
weapon and rejects any shared Talent. Converting a weapon back to standard
clears the relationship and archives its now-unowned exotic Talent. The shared
Talent Registry rejects exotic Talent creation and editing so contributors
cannot bypass weapon ownership.
OS Protocol progression
OS Protocols use dedicated normalized tables because their talent progression
has six calculated states (base plus five upgrades) rather than the shared
two-tier talent model. Each protocol owns one os_protocol_primary_effects
record and one os_protocol_talents record. Each protocol Talent owns its
mechanic program through the shared conditional-effect tables.
Dedicated profile tables normalize the three secondary attributes. One
os_protocol_secondary_profiles record exists for each configured quality +
attribute-type combination, with its three fixed stat/value pairs in
os_protocol_secondary_effects. Every matching protocol inherits that profile;
secondary values are not copied onto individual protocol records.
The unique primary value uses base × (1 + (level − 1) / 49) for levels 1–50, while
talent effects use base × (1 + upgrades / 5) for zero to five upgrades.
Server actions validate all relationships and append the full owned graph to
the existing game_data audit history.
Weapon Mods and tiered weapon slots
weapon_mod_slots records each weapon's available slot vocabulary per tier.
Canonical validation requires four unique slots for T0 and five for T1/T2 for
every tier at or above the weapon's native tier. Regular Weapon Mods declare a
single slot type and one or more compatible base weapon categories through
weapon_mod_compatible_categories; eligibility requires both a category match
and the slot to exist on the equipped weapon at its selected tier.
Exotic Weapon Mods instead reference one exotic_weapon_id. They do not carry
regular category compatibility. The builder treats them as permanent equipment
and lets the user change only their level. They do not use quality profiles,
random primary rolls, secondary pools, or regular Mod Effects. Their canonical
contributions are fixed attribute references with explicit level-1 and level-20
values in weapon_mod_exotic_attributes; intermediate values use linear
interpolation.
All regular Weapon Mods permit Firepower, Toughness, or Engineering as a
builder-time primary choice. weapon_mod_quality_values stores the shared
level-1 and level-20 primary roll endpoints for each quality; primary values
use linear interpolation from levels 1–20.
weapon_mod_secondary_options is the shared pool from which a build selects
two distinct secondary attributes. Every option owns a minimum and maximum at
each quality through weapon_mod_secondary_quality_values; secondary
availability is not tied to a numbered slot in canonical data. Mod Effects are
triggerless, hold one or two attribute references, and store fixed per-quality
values in weapon_mod_effect_quality_values.
Gear Mods and tier slot capacity
Gear Mod definitions are standard-quality records only; there is no exotic Gear Mod branch. Every definition supplies primary roll ranges for Standard, Specialized, Superior, and High-end quality at Levels 1 and 20. Values between those endpoints use linear interpolation. A build instance selects one primary attribute 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; T1 and T2 gear have two.
The gear_mods, gear_mod_quality_values, gear_mod_secondary_options, and
gear_mod_secondary_quality_values tables store canonical data.
Successful mutations use gear_mod.created, gear_mod.updated,
gear_mod.archived, and gear_mod.unarchived audit actions. Contributor
submissions use the gearmods proposal module and remain non-canonical until
approved by Oversight.
Skill Chip quality scaling
Skill Chip sets support Superior and High-end chip instances. Each set owns one 2-piece bonus, distinct primary and secondary stat-option pools, and one 3-piece Talent with a typed conditional rule. Primary 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 pools.
Each active Skill Chip definition belongs to one active specialization ability
through skill_chip_sets.specialization_ability_id. An ability has capacity for
three active definitions. The save and restore actions enforce that capacity,
and the schema requires the foreign key for each row. 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 endpoints and
previews all four states, preventing duplicated tier data from diverging.
The game_data audit stream captures Skill Chip mutations, owned relationships,
and safe icon metadata.
Mechanic programs and loadout state
Mechanic programs belong to the record whose behavior they describe. One
conditional_effect_rules row owns the program for one standard or exotic
Talent, OS Protocol Talent, Skill Chip Talent, or Specialization tree rank. The
source_type and source_id pair identifies that owner. No standalone Trigger
record or shared Trigger relationship participates in execution.
Each source supplies a fixed value-variant contract:
- Standard and exotic Talents require Tier I and Tier II values.
- OS Protocol Talents store a base value. An outcome may use an expression that
reads
equipment.protocolTalentUpgrades, from zero through five, so selected values can scale to twice the base at upgrade five. - Skill Chip Talents store minimum and maximum values for quality composition interpolation.
- Specialization tree-rank effects store the rank's base value.
The rule row stores the script version, source-defined scenario-input controls, configuration state, and one of three simulation-readiness states:
ready: the runtime has enough typed data to calculate the mechanic.requires_assumptions: the mechanic needs one or more scenario inputs before the runtime can include it.captured_only: ISAC preserves the authored mechanic, but the runtime cannot calculate it.
The code-defined MECHANIC_EVENT_REGISTRY is the event authority. Its 19
normalized events are shot_fired, weapon_hit, skill_deployed,
skill_hit, agent_hit, health_damage_taken, damage_dealt,
enemy_killed, assist, healing_applied, extra_health_granted,
movement_snapshot, proximity_snapshot, firing_started, firing_stopped,
reload_started, reload_completed, weapon_swapped, and tick. Event
details such as target, distance, critical state, source, movement, and nearby
counts travel as typed event values.
The optional Action Family catalog provides authoring recipes for common mechanics. Applying a recipe copies its typed scenario inputs, state pools, and clauses into the source-owned program. Those branches become editable authoritative data; the recipe key remains provenance, not a runtime dependency. Adding another recipe appends branches. Replacing a program with a recipe requires confirmation because replacement discards the current branch graph.
The program graph uses three child tables:
conditional_effect_state_poolsdefines agent- or target-scoped flags, counts, stacks, durations, scalar values, marks, and resources. Each pool owns its bounds, expiry policy, refresh policy, and best-case value.conditional_effect_clauseslistens for one of the 19 events. A clause owns a typed predicate, reducer, optional cooldown, optional weight expression, state mutations, and optional recipe provenance. Reducers convert event streams into counts, distinct or consecutive matches, sliding windows, durations, intervals, or derived values. Mutations can set, add, consume, clear, derive, or transfer state.conditional_effect_outcomesapplies Attribute modifiers, direct damage, restoration, resource costs, or markers. Each outcome names its target scope, value basis, optional resource or Attribute, combat timing, optional state band, and phase:on_event,after_event,on_enter,on_exit, orwhile_active. One typed value or expression exists for every variant required by the owning source. The common value models are static values, event-field values, reducer output, source-defined scenario values, and OS Protocol upgrade scaling.
The generic runtime in src/lib/calculations/mechanic.ts processes combat events
in timestamp order. Predicates decide whether an event enters a clause. Reducers
convert event streams into activations or derived values. The runtime applies
state mutations, enforces pool bounds and expiry, emits outcomes in phase order,
and records telemetry for each activation.
Periodic behavior requires an interval in the reducer. The runtime does not
infer a one-second cadence from missing data. A mechanic with an unknown cadence
uses requires_assumptions, identifies the missing input, and stays out of the
calculation until an operator or builder scenario supplies it.
The runtime derives a read-only pseudo-language listing from the typed program. ISAC and the builder use that listing to explain events, predicates, reducers, state changes, phases, and formulas. No route accepts the generated text as input, and no server process executes it. Operators edit structured fields; the formatter regenerates the listing after each change.
AI-assisted mechanic drafting
Manual mechanic authoring remains the complete authoring path. The AI assistant produces an optional draft for the existing structured editor. It cannot save, publish, approve, or submit game data, and it cannot alter application code. An author may ignore, revise, or replace any generated field through the manual controls.
The shared mechanic editor exposes the mechanic assistant for standard and exotic Talents, OS Protocol Talents, Skill Chip Talents, and Specialization tree-rank effects. Calculation revisions use a separate assistant contract because their typed stage schema and publication authority differ from source-owned mechanic programs.
The server owns a versioned mechanic-authoring prompt and context builder. Each request receives the source type, progression contract, submitted game text, current draft when supplied, code-defined mechanic registries, and the active ISAC Attribute catalog. Attribute context includes stable IDs, names, units, descriptions, and calculation metadata. The client cannot replace the system instructions, choose arbitrary models, attach files, add tools, or add external URLs.
The OpenAI integration uses the Responses API with Structured Outputs, no
tools, and store: false. The application stores the bounded clarification
history needed for the current drafting session. Requests use a fixed
server-configured model, a privacy-preserving user safety identifier, input and
output limits, timeouts, duplicate-request protection, and per-agent rate
limits. The API key remains on the server.
The model returns one structured result:
clarification_requiredcontains bounded questions whose answers affect the calculation model.draft_readycontains the typed mechanic draft, assumptions, warnings, readiness state, and a short explanation.capability_gapnames unsupported runtime behavior, the nearest supported representation, an optional workaround, and the schema, runtime, editor, reference, and test changes a developer would need to add support.
The server validates draft_ready output against the response schema and the
same deterministic mechanic parser used by manual authoring. Unknown events,
Attributes, value models, scopes, operators, reducers, or outcomes fail
validation. The client receives no draft when local validation fails.
The author reviews a validated draft and chooses whether to apply it to the unsaved editor. Applying a draft changes client form state only. The existing module action performs canonical validation, contribution interception, approval, persistence, and game-data audit logging after the author initiates the normal save flow.
The ai.mechanic.generate permission authorizes generation and clarification
requests. The server checks it for every request. Agents without the permission
may see the disabled assistant entry point, but they retain all manual controls
allowed by their data role.
Successful generation operations append sanitized events under the
ai_assistance audit category. Events identify the operator, source type,
prompt-context version, model, result type, token usage, request ID, and
validation result. Audit snapshots exclude the API key,
environment configuration, hidden model reasoning, authentication data, and
unbounded prompt content. Applying a draft changes client state and creates no
server event. The owning module's game-data audit event remains the record of
any saved mechanic change.
AI-assisted calculation-revision drafting
The calculation assistant converts an operator's plain-language combat-model request into an optional draft revision. It receives the current active or edited revision, the calculation-stage vocabulary, weapon and Skill profile contracts, and the active ISAC Attribute catalog. Its versioned prompt, structured response schema, validation, and application adapter remain separate from mechanic drafting.
The assistant returns clarification questions, a validated revision draft, or a calculation capability gap. A capability gap identifies the missing stage, operator, curve, binding, runtime behavior, or validation rule and lists the application and test changes needed to support it. The server rejects unknown stage keys, locked-position changes, duplicate bindings, invalid Attribute references, unsupported operators, and malformed curve or cap values through the deterministic calculation-revision parser.
The ai.calculation.generate permission authorizes calculation drafting and
clarification requests. It does not grant revision mutation or publication.
Applying a result changes the open revision form in the client. The operator
must review and save that draft through the existing revision action. The
separate calculation.publish permission and activation confirmation remain
mandatory before the saved draft affects loadout results.
Successful calculation-generation requests append sanitized events under
ai_assistance. These events use calculation-specific actions, identify the
base revision and generated contract version, and follow the same request,
token-usage, result, and secret-exclusion rules as mechanic-assistance events.
Applying a draft creates no server event. Existing calculation_configuration
events remain the evidence for revision saves and activation.
Source authors define the builder controls required by their mechanic as typed
toggle, slider, or numeric scenario inputs. Each input records its label,
guidance, bounds when applicable, default value, and best-case value. These
controls appear only in advanced scenario configuration. Maximum mode supplies
best-case values. Custom mode uses the values saved with the loadout. A
mechanic with an undefined required assumption remains excluded and reports
REQUIRES_ASSUMPTIONS.
Loadout schema version 4 stores one focus, selected tree ranks, nine Skill
Chips, a unified conditional-effect state map, encounter assumptions,
per-ability activation controls, and calculationMode. Runtime readers accept
the exact version 4 structure. A backed-up prototype migration removes drafts,
saved builds, and calibration snapshots that use incompatible payloads.
Calculation modes
The builder offers maximum and custom modes.
- Maximum mode supplies each scenario input's best-case value, selects peak conditional-effect state, and runs selected abilities on cooldown against their configured target caps. The interface hides granular assumptions in this mode.
- Custom mode uses the agent's encounter inputs, effect states, activation
rates, target counts, and other supported assumptions. A missing required
input produces
REQUIRES_ASSUMPTIONSrather than a fabricated value.
Saved and shared loadouts preserve the selected mode. Calculation results label the mode so readers can distinguish a best-case ceiling from a scenario-based estimate.
Specialization ability attacks
specialization_ability_attacks stores intrinsic base damage, Blast, Pierce, or
Shredding damage, direct or periodic delivery, hit or tick counts, cast time,
target cap, and charge behavior. skill_attribute_calculation_profiles binds
Attributes to the versioned Skill damage and cadence stages. Cooldown Recovery
changes charge and activation readiness. Duration changes the number of
periodic ticks.
Periodic delivery waits one configured tick interval before its first tick and excludes ticks at or after the encounter boundary. The Skill engine reevaluates active modifier windows at each delivered hit or tick. A missing tick interval marks the attack as requiring configuration; the engine does not supply a default cadence.
Versioned combat calculations
calculation_revisions supplies one active rule set. Operators can change draft
revisions, while active and retired revisions stay immutable.
calculation_stages stores the ordered typed pipeline. The base-damage stage
holds the first position, and the firing-cycle stage holds the last.
attribute_calculation_profiles maps an Attribute to a weapon stage with its
weight, optional caps, curve, and weapon-category scope.
The shared combat engine consumes weapon facts, accumulated Attributes, calculation profiles, and mechanic outcomes. It produces four hit values, burst DPS, reload-aware sustained DPS, encounter DPS, and stack telemetry. Custom mode walks the expected shot timeline from RPM, magazine size, reload time, hit rate, configured burst size, pauses between bursts, and encounter duration. It resolves the active Attribute state at each landed shot before calculating that shot's expected damage.
The builder scales a weapon definition's level-1 damage range to the equipped
item level, then the agent selects one damage value inside that valid range.
Combat receives equipped_weapon.damage as its base damage and consumes that
number once. The combat pipeline has no level_scaled_damage value and must not
apply item-level scaling a second time.
The generated calculation listing names each typed pipeline stage and its bound
Attributes. The listing explains the active revision with statements such as
TAKE equipped_weapon.damage AS base_damage, ADD, APPLY, WEIGHT, and
RETURN. The typed stages and profiles remain authoritative. The application
neither parses nor executes the listing.
The Headshot Kill Share assumption marks the matching portion of generated kill events for headshot-kill triggers. Rate of Fire, Magazine Size, and Reload Speed bindings change the firing-cycle calculation. Maximum mode applies maximum state. Custom mode can set an effect to Peak, Encounter, or Off.
The Skill engine uses a dedicated intrinsic ability-damage model. It supports direct and periodic delivery, critical and multi-shot expectation, target caps, per-ability activation rates, and charge recovery. Skills do not headshot. Dynamic Talent and tree-effect windows apply to each Skill hit at its delivery time. Encounter-triggered Skill modifiers use the primary weapon timeline until the builder supports a combined weapon-rotation model.
The engine returns INSUFFICIENT_DATA with missing requirement keys when a
calculation lacks required inputs or an Attribute binding. Callers must render
that state instead of a partial result. Saved and shared builds load the current
active revision for each request.
calculation_calibration_cases stores a versioned loadout snapshot, observed
hit values, timing values, optional encounter DPS, and the model prediction.
Ready cases require the four hit values plus magazine and reload times. The
assisted suggestion action compares ready observations with their predictions
and creates a draft revision for operator review.
Rule activation requires calculation.publish and a confirmation.
ISAC records draft creation, draft edits, publication, calibration changes, and
assisted suggestions under the calculation_configuration audit category.
ISAC data export and environment synchronization include calculation rules,
typed mechanic programs, ability attacks, calibration cases, and their
relationships in dependency order.
OS Protocol upgrade expressions and health timing
OS Protocol talents expose zero through five upgrade states. Value expressions
reference equipment.protocolTalentUpgrades and use the linear multiplier
1 + upgrades / 5. Base values resolve at upgrade zero, and scalable values
reach twice their base at upgrade five. Each outcome chooses whether to use this
multiplier, so a fixed resource cost can remain unchanged while restoration and
damage increase.
Happy Survival uses three clauses:
- Its proximity clause records the current enemy count within 10 meters.
- Its periodic clause restores
min(enemyCount × 0.5%, 2.5%) × (1 + upgrades / 5)of maximum Health. The reducer reads the configured interval from an explicit scenario input. The runtime caps applied restoration at missing Health. - Its
shot_firedclause consumes 2% of maximum Health, then deals direct damage equal to0.6% × (1 + upgrades / 5)of current Health. Theresource_costoutcome runs first. The damage outcome uses the remaining current Health after that cost, rather than the pre-event Health value.
At upgrade five, the restoration becomes 1% of maximum Health per nearby enemy up to 5%, and the shot damage becomes 1.2% of current Health. The 2% Health cost does not scale.
Game-data icon storage
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
SVG content and external references, 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. The
validated /icons/game-data/[type]/[id] endpoint serves them with immutable
cache headers and disables MIME sniffing. ISAC save actions process files
before mutation, apply upload or removal after record validation, and include
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, which lets contributors submit values and artwork in
separate dependency-safe changes. The shared MISSING_ITEM_ICON_URL constant
and ItemIcon component render /missing-item-icon.svg for missing artwork.
The placeholder does not enter game_data_icons. A later edit or proposal can
attach the real icon without recreating the canonical item.
source_entity_type and source_entity_id link each game_data_icons row to a
managed_images record. Both records share one storage key and one WebP file.
The managed record adds the searchable library name, category, tags, creator,
dimensions, and assignment state. Game-data icon saves upsert this record, keep
operator-edited metadata, and merge current entity-context tags. Removing a
source icon removes the linked library record and shared file.
The additive database migration registers older game-data icons and selects an administrator or the earliest active account as the migration actor. ISAC and managed-image exports accept linked storage keys. Environment synchronization keeps the library metadata and one copy of each file.
Oversight notification dispatch
The notification router sends Data Change review items to agents who hold
data.approve and Avatar Oversight review items to agents who hold
avatars.review. Each reviewer chooses Immediate, 15-minute, one-hour, or
daily delivery for each authorized queue. Submission creates Immediate alerts;
oversight_notification_queue stores consolidated alerts.
Production must run the idempotent dispatcher once per minute from one scheduler instance:
* * * * * cd /path/to/shd.build && npm run notifications:oversight:production
The dispatcher groups due rows by reviewer, queue, cadence, and delivery
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 later queries exclude them. Existing
ISAC notification delivery preferences determine optional browser and email
fan-out after the dispatcher creates the consolidated inbox message.
Automated ISAC system messages
Automated message definitions live in automated_system_messages and are
managed through ISAC → System Configuration → Automated Messages by agents with
system.notifications.manage. The audit log records edits under the
system_configuration audit category. Definitions identify a lifecycle event,
enabled state, delay in minutes, message content, primary destination, and
structured action callouts.
Password, magic-link, and Discord registration emit account.created.
The delivery table has a unique message/user/event/event-ID constraint, so the
same account creation cannot deliver the Welcome Agent message more than once.
The default welcome introduces News, Database, Builds, Guides, Builder, ISAC
Contributor Mode, and the SHD Discord community.
The registration request delivers Immediate definitions.
automated_system_message_deliveries stores definitions with a delay. Production
must run the delayed dispatcher once per minute:
* * * * * cd /path/to/shd.build && npm run notifications:system:production
Only one dispatcher instance should run for the embedded database. Delivered rows retain both their delivery timestamp and generated notification ID.