feat: add community publishing and news syndication
This commit is contained in:
+289
-207
@@ -5,24 +5,28 @@ 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
|
||||
- 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.
|
||||
- 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
|
||||
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.
|
||||
|
||||
@@ -49,9 +53,9 @@ 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
|
||||
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.
|
||||
|
||||
@@ -84,22 +88,102 @@ 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.
|
||||
Comments, profile biographies, custom avatars, and Guides 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.
|
||||
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 or Guide 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.
|
||||
|
||||
## Guides and News articles
|
||||
|
||||
`articles` stores the stable identity, kind, owner, immutable slug, visibility,
|
||||
and published or pending revision pointers for Guides and News. Authors edit one
|
||||
mutable `article_drafts` row with optimistic version checks. Publication creates
|
||||
an immutable `article_revisions` snapshot. Public readers and search load only
|
||||
the revision named by `articles.published_revision_id`.
|
||||
|
||||
Guides require OpenAI moderation before an unlisted or public revision can
|
||||
publish. The moderation request includes the title, deck, document text, image
|
||||
alternative text and captions, cover image, and inline images. A failed provider
|
||||
request stays in Guide Oversight. Clean revisions may publish automatically;
|
||||
flagged revisions preserve the previous public revision while Oversight decides.
|
||||
News bypasses this lifecycle because `news.manage` is the editorial gate. News
|
||||
comments still use community moderation.
|
||||
|
||||
### Ubisoft News syndication
|
||||
|
||||
The Ubisoft News importer reads the English-language Division Resurgence feed
|
||||
from Ubisoft Nimbus. It stores each Nimbus item ID as the stable external ID and
|
||||
publishes only the source title, excerpt, and canonical Ubisoft link. It does
|
||||
not copy the source article body or images. Imported articles use Ubisoft's
|
||||
original publication timestamp rather than the import time.
|
||||
|
||||
`external_news_sources` maps each Nimbus ID to one SHD.build article. A SHA-256
|
||||
content hash covers the normalized title, excerpt, canonical URL, and source
|
||||
publication timestamp. An unchanged hash updates `last_seen_at` without creating
|
||||
a revision. A changed hash creates and publishes an immutable revision. The
|
||||
importer does not delete or unpublish an article when Nimbus omits it from a
|
||||
later feed, and it does not restore an article that an editor deleted. The
|
||||
Newsroom treats source-linked articles as read-only so local edits cannot retain
|
||||
Ubisoft attribution. The importer defers future-dated items until their source
|
||||
publication time.
|
||||
|
||||
The seeded, non-public `Ubisoft Official News` service identity owns imported
|
||||
articles and acts on their audit events. A first import records
|
||||
`editorial_content / news.imported`; a later content sync records
|
||||
`editorial_content / news.import_synced`. Audit snapshots identify the provider,
|
||||
Nimbus ID, canonical URL, source timestamp, and resulting revision.
|
||||
|
||||
The existing bearer-authenticated scheduler endpoint invokes the importer. A
|
||||
successful run sets the next check one hour later. The importer stores the last
|
||||
attempt, last success, next check, consecutive failure count, and bounded last
|
||||
error in provider state. Failures retry after 15 minutes with exponential
|
||||
backoff capped at six hours. A successful run clears the error and failure
|
||||
count.
|
||||
|
||||
The article document is a versioned, strictly validated Tiptap JSON envelope.
|
||||
The application stores no author-supplied HTML and renders accepted nodes with
|
||||
Svelte components. `article_revision_build_embeds` supports several Builds per
|
||||
Guide and several Guides per Build. Each relation records a bounded publication
|
||||
snapshot. Authors may embed any Build they own; another Agent's Build must be
|
||||
public and becomes unavailable in the Guide if its source stops being public.
|
||||
|
||||
Guide taxonomy combines fixed activity and difficulty terms with active
|
||||
Specializations, Focuses, and weapon categories. `article_search` stores the
|
||||
published title, deck, body text, and tags. Weighted token scoring prioritizes
|
||||
title, deck, tags, then body because the embedded Turso engine does not ship the
|
||||
FTS5 module. Search rows exist only for current public revisions.
|
||||
|
||||
Registered and anonymous visitors may rate a Guide from 1 through 5. Anonymous
|
||||
ratings and Guide reports use a random HTTP-only browser token; the database
|
||||
stores only its domain-separated hash. Repeated anonymous activity requires a
|
||||
Turnstile result. Guide reports reference the exact published revision so a
|
||||
report against an older revision cannot hide newer work.
|
||||
|
||||
The Builder stores loadout data only. It has no summary or Guide body. An owned
|
||||
Build page links to `/guides/new?build=<id>`, where the server verifies
|
||||
ownership and initializes a removable Build block in a new private Guide.
|
||||
|
||||
## Public data corrections and discussion
|
||||
|
||||
Every public database card links to its canonical ISAC record with an exact
|
||||
`item` ID and offers a moderated Community Discussion. Authenticated Agents can
|
||||
post notes and replies; Community Oversight may pin one visible comment as the
|
||||
record's Community Note. A public correction opens the same ISAC editor used by
|
||||
data operators. Contributor-mode submissions create a proposal rather than
|
||||
changing canonical data. Contributors can attach an optional bounded rationale;
|
||||
the rationale is retained with the proposal and visible to Data Oversight, but
|
||||
does not become part of the canonical action replay.
|
||||
|
||||
## Increment plan
|
||||
|
||||
1. Product shell and anonymous local-draft builder.
|
||||
@@ -109,10 +193,11 @@ environment configuration.
|
||||
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
|
||||
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.
|
||||
|
||||
@@ -134,8 +219,8 @@ 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.
|
||||
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:
|
||||
|
||||
@@ -144,18 +229,18 @@ 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.
|
||||
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.
|
||||
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
|
||||
@@ -165,22 +250,22 @@ deployment, backup, synchronization, and maintenance safeguards documented in
|
||||
|
||||
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.
|
||||
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.
|
||||
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
|
||||
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.
|
||||
@@ -189,47 +274,48 @@ mutation rather than deletion or modification of the original 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.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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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
|
||||
@@ -241,10 +327,10 @@ 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.
|
||||
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
|
||||
@@ -252,8 +338,8 @@ 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.
|
||||
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
|
||||
|
||||
@@ -266,8 +352,8 @@ 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.
|
||||
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
|
||||
@@ -286,14 +372,14 @@ An exotic weapon references one active exotic talent through
|
||||
`weapons.exotic_talent_id`. The weapon editor lists registered exotic talents
|
||||
and identifies talents assigned to another weapon. Server validation rejects
|
||||
standard talents, inactive talents, and talents already assigned elsewhere.
|
||||
Changing or clearing a weapon relationship leaves the previous talent active
|
||||
and available for another assignment.
|
||||
Changing or clearing a weapon relationship leaves the previous talent active and
|
||||
available for another assignment.
|
||||
|
||||
Tier I and its modeled mechanic are required when a contributor saves an
|
||||
exotic talent. Tier II may remain undocumented. An undocumented Tier II has no
|
||||
Tier I and its modeled mechanic are required when a contributor saves an exotic
|
||||
talent. Tier II may remain undocumented. An undocumented Tier II has no
|
||||
`talent_tiers` row or conditional-effect value variant. A later Talent Registry
|
||||
edit adds the Tier II projection and mechanic values without changing the
|
||||
weapon reference.
|
||||
edit adds the Tier II projection and mechanic values without changing the weapon
|
||||
reference.
|
||||
|
||||
# OS Protocol progression
|
||||
|
||||
@@ -309,22 +395,22 @@ 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.
|
||||
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
|
||||
`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.
|
||||
`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
|
||||
@@ -336,62 +422,60 @@ 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.
|
||||
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`.
|
||||
`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,
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
`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.
|
||||
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.
|
||||
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
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -413,8 +497,8 @@ Each source supplies a fixed value-variant contract:
|
||||
|
||||
- Standard Talents require Tier I and Tier II values. Exotic Talents require
|
||||
Tier I and may omit Tier II until contributors document its game values.
|
||||
Builder calculations use Tier I while an equipped exotic Talent has no Tier
|
||||
II mechanic variant.
|
||||
Builder calculations use Tier I while an equipped exotic Talent has no Tier II
|
||||
mechanic variant.
|
||||
- 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.
|
||||
@@ -457,24 +541,23 @@ separate event from changes to the Agent's current Extra Health state.
|
||||
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.
|
||||
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
|
||||
`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 event from the registry. A
|
||||
clause owns a typed predicate, reducer, optional cooldown, optional weight
|
||||
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 event from the registry. 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,
|
||||
@@ -486,17 +569,17 @@ The program graph uses three child tables:
|
||||
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.
|
||||
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.
|
||||
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
|
||||
@@ -508,9 +591,9 @@ 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.
|
||||
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.
|
||||
|
||||
The read-only `combat.currentHealthPercent` reference divides resolved current
|
||||
Health by resolved maximum Health and returns a value from 0 through 100. The
|
||||
@@ -543,8 +626,8 @@ formatter regenerates the listing after each change.
|
||||
|
||||
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
|
||||
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
|
||||
@@ -569,12 +652,12 @@ 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 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:
|
||||
|
||||
@@ -587,15 +670,15 @@ The model returns one structured result:
|
||||
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.
|
||||
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,
|
||||
@@ -616,11 +699,11 @@ 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.
|
||||
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
|
||||
|
||||
@@ -628,16 +711,16 @@ 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.
|
||||
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,
|
||||
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.
|
||||
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.
|
||||
@@ -657,8 +740,8 @@ 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
|
||||
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
|
||||
@@ -673,8 +756,8 @@ 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.
|
||||
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.
|
||||
@@ -689,8 +772,8 @@ estimate.
|
||||
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.
|
||||
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
|
||||
@@ -734,11 +817,10 @@ 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.
|
||||
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
|
||||
@@ -751,12 +833,12 @@ 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.
|
||||
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
|
||||
|
||||
@@ -793,9 +875,9 @@ 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.
|
||||
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
|
||||
@@ -820,8 +902,8 @@ keeps the library metadata and one copy of each file.
|
||||
|
||||
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;
|
||||
`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
|
||||
@@ -831,12 +913,12 @@ 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.
|
||||
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
|
||||
|
||||
@@ -847,15 +929,15 @@ managed through ISAC → System Configuration → Automated Messages by agents w
|
||||
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
|
||||
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:
|
||||
`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
|
||||
|
||||
Reference in New Issue
Block a user