# SHD.build architecture The team builds the project in vertical slices. ## Application stack - SvelteKit 2 / Svelte 5 with TypeScript - Embedded Turso Database via `@tursodatabase/database` for 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 `localStorage` only 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`](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 `GET` and `HEAD` requests 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`. `GET` and `HEAD` requests redirect to `/coming-soon` with 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. ## Community content moderation Comments, profile biographies, custom avatars, guides, and build descriptions share the moderation lifecycle documented in [`CONTENT_MODERATION.md`](CONTENT_MODERATION.md). Local rules and the configured provider classify each submission before publication. Clean submissions can publish without human work. Flagged submissions and fail-closed provider errors enter the appropriate Oversight queue. Agents may report visible content they do not own. Reports enter Oversight without hiding the content. Human decisions require a reason and append an audit event. Long-form edits that need review use immutable pending revisions so the last approved biography, guide, or description remains public. ISAC Site Controls owns provider behavior, content scopes, thresholds, link and PII checks, and allowed or banned terms. API credentials remain in server environment configuration. ## Increment plan 1. Product shell and anonymous local-draft builder. 2. Embedded Turso migrations, repositories, and seed data. 3. Credentials and Discord authentication. 4. Full equipment/stat builder driven by database definitions. 5. Publishing, discovery, ratings, comments, favorites, and copy flow. 6. Rich build guides and moderation. 7. 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: ```sh npm run db:backup ``` Create a production backup: ```sh 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: ```sh npm run db:restore -- data/backups/.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: 1. The operator must hold `audit.revert` in addition to any other access. 2. The reversion registry must support the original action and the event must contain a usable 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 indicates newer work and blocks automatic compensation. 5. The server revalidates referenced roles, permissions, and relationships before restoration. Restoring administrator authority also 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. 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 category bonuses `weapon_category_attribute_bonuses` stores three ordered fixed Attribute contributions for each Shotgun, Rifle, MMR, AR, LMG, and SMG category. Each profile grants 5% Weapon Critical Hit Chance and 50% Weapon Critical Hit Damage. Shotgun, Rifle, AR, LMG, and SMG grant 40% Headshot Damage; MMR grants 70%. Weapon records do not own copies of these rows. Both builder catalog loaders attach the matching category profile when they hydrate a weapon. Exotic weapons retain an underlying category and inherit the same profile as a standard weapon of that type. The combat summary preserves the existing equipped-weapon semantics: each equipped weapon contributes its inherited profile to the Agent's accumulated Attributes. ISAC exposes one category-profile editor above the weapon registry. Operators may change the three positive values, while the Attribute identities and order remain fixed. Each successful save appends a `game_data/weapon_bonus_profile.updated` audit event with the complete previous and resulting profile. # 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. The editor captures one shared set for T1/T2 and the single shared slot omitted at T0, then expands that input into the normalized per-tier rows. Operators may save partial rows when the in-game configuration is unavailable. ISAC derives `CONFIG REQUIRED` until every available tier contains four unique slots at T0 or five at T1/T2. 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 event registry publishes authoritative field metadata where branches need a bounded contract. For example, `weapon_hit.values.headshot` is a boolean that distinguishes headshots from body shots. 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. A Talent may save an empty `captured_only` program while an author records its name, category, image, and source description. The registry marks that Talent `CONFIG REQUIRED`, and calculations exclude it. Changing readiness to `ready` or `requires_assumptions` restores strict branch, numbered-outcome, and description-placeholder validation. The program graph uses three child tables: - `conditional_effect_state_pools` defines 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_clauses` listens 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. A mutation may bind a target-scoped pool to the current event target, event actor, or another typed entity selector. Each target owns an independent value and expiry timer. - `conditional_effect_outcomes` applies 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`, or `while_active`. One typed value or expression exists for every variant required by the owning source. The common value models are static values, live Attribute values, percentage-normalized Attribute values, event-field values, reducer output, source-defined scenario values, and OS Protocol upgrade scaling. Restoration outcomes may deliver their total immediately or continuously over an explicit duration. 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. Each write to an expiring pool refreshes its timer. When a pool expires, `on_exit` outcomes evaluate stack thresholds and per-stack expressions against the final value before reset. The runtime records the bound target on emitted outcomes. A one-second target debuff with a one-second damage cadence can therefore resolve once at its refreshed shared expiry without inventing intermediate ticks. Live Attribute references use a stable Attribute ID and read the authoritative loadout total that the builder captures for the evaluation pass. Percentage Attributes retain their displayed units, so an expression that applies a coefficient to a percentage divides the Attribute value by 100. Evaluation fails when the loadout does not supply a referenced Attribute; the runtime does not substitute zero. Continuous restoration resolves the outcome's total when the branch activates, then delivers that total at a constant rate over the configured duration. Activations create independent streams, overlapping streams add, and restoration above the resource maximum is lost when it occurs. The runtime integrates delivery at event and expiry boundaries before processing the event at that timestamp, which keeps results independent of an arbitrary tick interval. 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 deterministic draft parser owns the canonical outcome-capability manifest. The AI context serializes that manifest instead of maintaining a second outcome grammar. The parser rejects outcome fields absent from the manifest. Contract tests build a draft from each advertised capability example and pass it through the parser, so CI blocks a prompt that advertises unsupported fields. A mechanic grammar change must update the manifest and example, pass the round-trip test, and increment the prompt contract version before release. 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_required` contains bounded questions whose answers affect the calculation model. - `draft_ready` contains the typed mechanic draft, assumptions, warnings, readiness state, and a short explanation. - `capability_gap` names 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. For a mechanic capability gap, the client builds a developer prompt from the exact analyzed wording, clarification transcript, source profile, and structured gap response. The author can copy that prompt without another model request. The author can also submit the gap to active full administrators. The submission endpoint validates bounded fields, confirms that the request ID belongs to the author's audited `capability_gap` generation, suppresses repeat submissions, sends mandatory ISAC notifications, and appends a `mechanic_gap.submitted` event to the `ai_assistance` audit category. The notification links to the filtered audit evidence. Submission does not mutate the mechanic draft or parent game-data record. 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_ASSUMPTIONS` rather 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: 1. Its proximity clause records the current enemy count within 10 meters. 2. 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. 3. Its `shot_fired` clause consumes 2% of maximum Health, then deals direct damage equal to `0.6% × (1 + upgrades / 5)` of current Health. The `resource_cost` outcome 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: ```text * * * * * 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: ```text * * * * * 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.