Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
904cb073a2 | ||
|
|
fe06b90117 | ||
|
|
e5213248a8 |
+1
-2
@@ -44,5 +44,4 @@ __pycache__/
|
||||
*.py[cod]
|
||||
|
||||
.pi
|
||||
.mesh
|
||||
.worktrees/
|
||||
.mesh
|
||||
@@ -69,27 +69,11 @@ The `.agents/skills/` directory contains repo-specific skill files. Key skills:
|
||||
|
||||
Use `skill` tool to load one when the task matches.
|
||||
|
||||
## Agent skills
|
||||
|
||||
### Issue tracker
|
||||
|
||||
Track issues in this repository's Gitea Issues using `tea`. See
|
||||
`docs/agents/issue-tracker.md`.
|
||||
|
||||
### Triage labels
|
||||
|
||||
Use the five canonical triage labels without overrides. See
|
||||
`docs/agents/triage-labels.md`.
|
||||
|
||||
### Domain docs
|
||||
|
||||
Use the single-context domain documentation layout. See `docs/agents/domain.md`.
|
||||
|
||||
## Verify commands
|
||||
|
||||
Run in order after source changes:
|
||||
|
||||
```bash
|
||||
```
|
||||
npm run check # svelte-kit sync + svelte-check (typecheck)
|
||||
npm run build # secretless Vite production build
|
||||
```
|
||||
|
||||
-271
@@ -1,271 +0,0 @@
|
||||
# SHD.build Domain
|
||||
|
||||
SHD.build is an independent community intelligence platform for _The Division
|
||||
Resurgence_. This glossary defines the language used for player planning, shared
|
||||
content, Game Data, governance, equipment, progression, and modeled mechanics.
|
||||
|
||||
## Product and people
|
||||
|
||||
**SHD.build**: The community platform described by this glossary. Always
|
||||
capitalize `SHD` and write the product name as `SHD.build`. _Avoid_: SHD Build,
|
||||
SHD Builder
|
||||
|
||||
**Agent**: Any person using SHD.build, whether anonymous or authenticated.
|
||||
_Avoid_: User, visitor, player when referring to product access
|
||||
|
||||
**Anonymous Agent**: An Agent who is not signed in to SHD.build. _Avoid_:
|
||||
Visitor, guest
|
||||
|
||||
**Registered Agent**: An Agent with an SHD.build account. _Avoid_: Registered
|
||||
user, member
|
||||
|
||||
**Modeling Session**: An interactive agentic work session that prepares one
|
||||
Mechanic Program at a time for a Registered Agent to confirm in chat before the
|
||||
session submits it through an existing ISAC action. _Avoid_: Agent, Modeling
|
||||
Client, bulk job
|
||||
|
||||
**Contributor**: A Registered Agent who has accepted the contributor guidelines
|
||||
and may submit proposed Game Data changes for review. _Avoid_: Editor when the
|
||||
Agent cannot change Canonical Game Data directly
|
||||
|
||||
**Operator**: A Registered Agent performing an authorized task in ISAC.
|
||||
Contributor, reviewer, moderator, and administrator authority remain distinct
|
||||
from this situational term. _Avoid_: Admin as a generic name for anyone using
|
||||
ISAC
|
||||
|
||||
**Oversight**: The human review function for proposed Game Data, moderated
|
||||
Community Content, and identity assets. Use the qualified names Data Oversight,
|
||||
Community Oversight, Guide Oversight, and Avatar Oversight when the review area
|
||||
matters. _Avoid_: Moderation as a name for every review function
|
||||
|
||||
## Planning and shared content
|
||||
|
||||
**Builder**: The planning tool an Agent uses to configure a Loadout and inspect
|
||||
supported calculations. _Avoid_: Build when referring to the tool
|
||||
|
||||
**Loadout**: A complete planned character configuration, including selected
|
||||
Equipment, progression choices, and combat assumptions. A Loadout may exist
|
||||
without becoming a saved Build. _Avoid_: Build when persistence, ownership, or
|
||||
sharing matters
|
||||
|
||||
**Loadout Draft**: An editable Loadout that has not become a saved Build.
|
||||
Anonymous and Registered Agents may both work with drafts. _Avoid_: Build draft
|
||||
|
||||
**Build**: A saved, owned, and shareable planning artifact whose central content
|
||||
is a Loadout. A Build may be Private, Unlisted, or Public and may derive from
|
||||
another Build. _Avoid_: Loadout when persistence, ownership, or sharing matters
|
||||
|
||||
**Guide**: A revisioned long-form community article that may include snapshots
|
||||
of one or more Builds. A Guide is prose-led content rather than a Loadout
|
||||
container. _Avoid_: Build guide when referring to every Guide
|
||||
|
||||
**News Article**: A revisioned editorial or syndicated article published through
|
||||
the News channel. _Avoid_: Guide, post
|
||||
|
||||
**Article Revision**: An immutable edition of a Guide or News Article. Editors
|
||||
select one revision for publication while later revisions may await review.
|
||||
_Avoid_: Version when referring to published article history
|
||||
|
||||
**Community Content**: Agent-authored comments, profile biographies, custom
|
||||
avatars, and Guides governed by the shared moderation lifecycle. _Avoid_:
|
||||
User-generated content, UGC
|
||||
|
||||
**Community Note**: A visible comment that Community Oversight pins to a Public
|
||||
Database discussion. It remains Community Content and follows the same removal
|
||||
rules as other comments. _Avoid_: Official correction, staff note
|
||||
|
||||
**Content Report**: An Agent's request for Oversight to review visible Community
|
||||
Content, with no immediate change to the content's visibility. _Avoid_: Takedown
|
||||
request
|
||||
|
||||
**Public Database**: The Agent-facing reference view of Canonical Game Data.
|
||||
_Avoid_: Database when the term could mean application storage
|
||||
|
||||
## Game Data and governance
|
||||
|
||||
**Game Data**: Community-maintained structured facts about _The Division
|
||||
Resurgence_ used by the Public Database, Builder, Builds, and contribution
|
||||
workflow. _Avoid_: Source data, builder data, reference data
|
||||
|
||||
**Canonical Game Data**: The authoritative, approved body of Game Data. _Avoid_:
|
||||
Live data, production data
|
||||
|
||||
**Canonical Record**: One authoritative record within Canonical Game Data.
|
||||
_Avoid_: Approved proposal
|
||||
|
||||
**Contribution Proposal**: A pending request from a Contributor to create or
|
||||
change Canonical Game Data. Approval still requires the proposed change to
|
||||
satisfy the canonical rules for its record family. _Avoid_: Draft record,
|
||||
pending record
|
||||
|
||||
**ISAC**: The operational console for Game Data, contribution review, community
|
||||
governance, identity and access management, audit history, and system
|
||||
operations. _Avoid_: Admin panel, calculation engine
|
||||
|
||||
**Registry**: An ISAC workspace containing one family of related records and its
|
||||
management controls. _Avoid_: Table, list page
|
||||
|
||||
**Audit Event**: An immutable account of a consequential operation or review
|
||||
decision, including the actor, target, and change evidence. _Avoid_: Audit-log
|
||||
row, activity log
|
||||
|
||||
**Compensating Reversion**: A new operation that restores an earlier state while
|
||||
preserving the original Audit Event and recording the reversal. _Avoid_:
|
||||
Rollback, undo when either implies rewriting history
|
||||
|
||||
## Equipment and progression
|
||||
|
||||
**Equipment**: The items and enhancements selected for a Loadout, including
|
||||
Weapons, Gear, Weapon Mods, Gear Mods, Skill Chips, and an OS Protocol. _Avoid_:
|
||||
Item when the equipment relationship matters
|
||||
|
||||
**Gear**: Equippable protective equipment assigned to a character slot. Gear may
|
||||
provide core values, Attributes, a Brand Set, a Talent, and augmentation
|
||||
capacity. _Avoid_: Armor as a collective name for all Gear
|
||||
|
||||
**Body Armor**: The canonical name for the equippable torso Gear slot. _Avoid_:
|
||||
Vest
|
||||
|
||||
**Weapon**: An equippable firearm with a Weapon Category, damage and handling
|
||||
properties, progression values, and applicable Talents and Mods. _Avoid_: Gun
|
||||
when referring to the modeled record
|
||||
|
||||
**Weapon Category**: The classification that groups Weapons as Shotgun, Rifle,
|
||||
MMR, AR, LMG, or SMG. _Avoid_: Weapon type, weapon family
|
||||
|
||||
**Weapon Mod**: An enhancement fitted to a compatible Weapon slot. Regular and
|
||||
Exotic Weapon Mods follow different selection rules. _Avoid_: Attachment
|
||||
|
||||
**Gear Mod**: An enhancement fitted to Gear according to the Gear's available
|
||||
mod capacity. _Avoid_: Tactical Augment
|
||||
|
||||
**Tactical Augment**: A numbered augmentation position recorded on selected Gear
|
||||
in a Loadout. _Avoid_: Gear Mod
|
||||
|
||||
**Attribute**: A named measurable game property with defined units and
|
||||
calculation meaning. _Avoid_: Stat when referring to the canonical Attribute
|
||||
catalog
|
||||
|
||||
**Core Attribute**: One of the three primary character alignments: Firepower,
|
||||
Toughness, or Engineering. _Avoid_: Main stat, primary stat
|
||||
|
||||
**Brand Set**: A Gear identity whose bonuses depend on how many pieces from that
|
||||
set the Loadout equips. _Avoid_: Gear set
|
||||
|
||||
**Quality**: The item classification Standard, Specialized, Superior, or
|
||||
High-end. Quality governs available or scaled properties for applicable
|
||||
Equipment. _Avoid_: Rarity
|
||||
|
||||
**Exotic**: A special scope used by certain Weapons, Talents, and Weapon Mods.
|
||||
Exotic is not a fifth Quality. _Avoid_: Exotic quality
|
||||
|
||||
**Item Level**: An Equipment progression coordinate used to resolve
|
||||
level-dependent values. _Avoid_: Tier, Quality
|
||||
|
||||
**Equipment Tier**: The T0, T1, or T2 progression band that controls Equipment
|
||||
capabilities and values. _Avoid_: Talent Tier, Item Level
|
||||
|
||||
**Talent Tier**: The Tier I or Tier II value variant of a Talent. _Avoid_:
|
||||
Equipment Tier, T1, T2 when the distinction could be unclear
|
||||
|
||||
**Reference Level**: The Item Level at which an observed value or range applies.
|
||||
_Avoid_: Base level
|
||||
|
||||
**Hidden Base**: A level-independent value inferred from observed Equipment
|
||||
values and the game's progression rules. _Avoid_: Displayed Value, Raw Value
|
||||
|
||||
**Raw Value**: A calculated value before the presentation rules that reproduce
|
||||
the game's displayed number. _Avoid_: Hidden Base, Displayed Value
|
||||
|
||||
**Displayed Value**: The player-visible number produced after applying the
|
||||
game's presentation rules to a Raw Value. _Avoid_: Raw Value
|
||||
|
||||
**OS Protocol**: A single equipped progression item with a Quality, Core
|
||||
Attribute alignment, primary effect, inherited secondary profile, and
|
||||
upgradeable Talent. _Avoid_: Protocol when the operating-system meaning could be
|
||||
inferred
|
||||
|
||||
**Skill**: A selectable combat capability associated with a Specialization.
|
||||
Skills may own Skill Chip Sets and may have explicit compatibility relationships
|
||||
with another Skill. _Avoid_: Ability or Specialization Ability for the
|
||||
selectable game concept
|
||||
|
||||
**Signature Skill**: A Specialization's defining Skill, distinguished from its
|
||||
other selectable Skills. _Avoid_: Signature ability
|
||||
|
||||
**Skill Chip**: An Equipment selection that modifies the Skill occupying its
|
||||
Loadout position. Skill Chips belong to a Skill Chip Set and may provide
|
||||
primary, secondary, and set effects. _Avoid_: Chip when the equipment family is
|
||||
unclear
|
||||
|
||||
**Skill Chip Set**: A reusable Skill Chip definition associated with a Skill,
|
||||
including its set bonus and optional Talent. An explicit compatibility
|
||||
relationship may let another Skill equip the set. _Avoid_: Skill set
|
||||
|
||||
**Specialization**: A character progression identity containing Focuses, Skills,
|
||||
and progression choices. _Avoid_: Class
|
||||
|
||||
**Focus**: A path within a Specialization aligned to one Core Attribute and
|
||||
defining a selection of Skills and progression choices. _Avoid_: Subclass,
|
||||
specialization path
|
||||
|
||||
**Talent**: A named conditional behavior owned by its source, such as a Weapon,
|
||||
Gear item, OS Protocol, or Skill Chip Set. Different Talent sources may use
|
||||
different progression variants. _Avoid_: Perk, passive
|
||||
|
||||
## Modeled mechanics
|
||||
|
||||
**Mechanic Program**: The structured, source-owned description of a Talent or
|
||||
other conditional behavior. It captures the conditions, state changes, and
|
||||
outcomes needed to evaluate that behavior. _Avoid_: Script, Trigger record
|
||||
|
||||
**Event**: An observed occurrence on a combat timeline that a Mechanic Program
|
||||
may inspect. _Avoid_: Trigger when referring to the occurrence itself
|
||||
|
||||
**Trigger Condition**: The criteria that an Event must satisfy before a mechanic
|
||||
can proceed toward Activation. A Trigger Condition is part of a Mechanic
|
||||
Program, not a standalone Game Data record. _Avoid_: Trigger as an entity
|
||||
|
||||
**Activation**: A successful mechanic occurrence after its Event, conditions,
|
||||
chance, and Cooldown requirements resolve. _Avoid_: Proc when precision matters
|
||||
|
||||
**Cooldown**: The period after a successful Activation during which the same
|
||||
mechanic cannot activate again. A failed chance does not begin the Cooldown.
|
||||
_Avoid_: Delay
|
||||
|
||||
**Mechanic State**: A value retained across Events for one mechanic, such as a
|
||||
flag, count, stack, duration, mark, or resource. _Avoid_: Runtime state when
|
||||
discussing game behavior
|
||||
|
||||
**Outcome**: The effect emitted by an activated mechanic, such as an Attribute
|
||||
change, damage, restoration, resource change, or marker. _Avoid_: Effect when
|
||||
the source Talent and emitted result could be confused
|
||||
|
||||
**Combat Profile**: The encounter assumptions and observed rates used to project
|
||||
a Loadout's combat performance. _Avoid_: Simulation when no event simulation
|
||||
occurs
|
||||
|
||||
**Scenario Input**: An explicit encounter assumption required to evaluate a
|
||||
mechanic whose result cannot be determined from the Loadout alone. _Avoid_:
|
||||
Guess, default assumption
|
||||
|
||||
**Ready**: The mechanic readiness state indicating that typed Game Data can
|
||||
evaluate the mechanic without additional Scenario Inputs. _Avoid_: Complete
|
||||
|
||||
**Requires Assumptions**: The mechanic readiness state indicating that
|
||||
evaluation needs one or more explicit Scenario Inputs. _Avoid_: Ready,
|
||||
unsupported
|
||||
|
||||
**Captured Only**: The mechanic readiness state indicating that SHD.build
|
||||
preserves the authored behavior but cannot calculate it. _Avoid_: Unsupported
|
||||
when the behavior has still been recorded
|
||||
|
||||
**Extra Health**: Temporary Health granted in addition to an Agent's normal
|
||||
Health. Each grant remains distinct from later changes to the current Extra
|
||||
Health total. _Avoid_: Health, healing, bonus armor
|
||||
|
||||
**Hit Taken**: A hit received by an Agent, including one absorbed by Extra
|
||||
Health. A Hit Taken does not require normal Health loss. _Avoid_: Taking Damage
|
||||
|
||||
**Taking Damage**: A loss of an Agent's normal Health. A hit absorbed by Extra
|
||||
Health does not count as Taking Damage. _Avoid_: Hit Taken
|
||||
@@ -1,60 +0,0 @@
|
||||
# Domain docs
|
||||
|
||||
Engineering skills must consult this repository's domain documentation before
|
||||
exploring code.
|
||||
|
||||
## Read before exploring
|
||||
|
||||
- Read `CONTEXT.md` at the repository root.
|
||||
- If `CONTEXT-MAP.md` exists, use it to find the `CONTEXT.md` files relevant to
|
||||
the task.
|
||||
- Read ADRs under `docs/adr/` that affect the area you will change. In a
|
||||
multi-context repository, also check `src/<context>/docs/adr/`.
|
||||
|
||||
If these files do not exist, proceed without comment. Do not suggest creating
|
||||
them before the project resolves domain terms or architectural decisions. The
|
||||
`/domain-modeling` skill, reached through `/grill-with-docs` and
|
||||
`/improve-codebase-architecture`, creates them when needed.
|
||||
|
||||
## File structure
|
||||
|
||||
This repository uses the single-context layout:
|
||||
|
||||
```text
|
||||
/
|
||||
├── CONTEXT.md
|
||||
├── docs/adr/
|
||||
│ ├── 0001-event-sourced-orders.md
|
||||
│ └── 0002-postgres-for-write-model.md
|
||||
└── src/
|
||||
```
|
||||
|
||||
A repository with multiple bounded contexts can use this layout:
|
||||
|
||||
```text
|
||||
/
|
||||
├── CONTEXT-MAP.md
|
||||
├── docs/adr/
|
||||
└── src/
|
||||
├── ordering/
|
||||
│ ├── CONTEXT.md
|
||||
│ └── docs/adr/
|
||||
└── billing/
|
||||
├── CONTEXT.md
|
||||
└── docs/adr/
|
||||
```
|
||||
|
||||
## Use glossary terms
|
||||
|
||||
Use the terms defined in `CONTEXT.md` in issue titles, refactor proposals,
|
||||
hypotheses, and test names. Do not substitute synonyms that the glossary
|
||||
rejects.
|
||||
|
||||
If a needed concept is absent, reconsider whether the project uses that concept.
|
||||
If it represents a real gap, record it for `/domain-modeling`.
|
||||
|
||||
## Flag ADR conflicts
|
||||
|
||||
Call out any proposal that conflicts with an ADR:
|
||||
|
||||
> _Contradicts ADR-0007 (event-sourced orders), but worth reopening because…_
|
||||
@@ -1,106 +0,0 @@
|
||||
# Issue tracker: Gitea
|
||||
|
||||
Issues and specs for this repo live as Gitea issues. Use the
|
||||
[`tea`](https://gitea.com/gitea/tea) CLI for all operations.
|
||||
|
||||
## Repository context
|
||||
|
||||
Run commands inside the clone so `tea` can infer the repository and login from
|
||||
its Git remote. If the clone has multiple Gitea remotes or logins, pass
|
||||
`--remote <name>` or `--login <name>`. Use `--repo <owner>/<repo>` only outside
|
||||
the clone.
|
||||
|
||||
Before the first operation, verify the instance with
|
||||
`tea logins list --output json`. If it is missing, ask the user to run
|
||||
`tea login add`; never ask for or handle their access token.
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Create an issue**: `tea issues create --title "..." --description "..."`.
|
||||
For a multi-line body, use shell command substitution around a quoted heredoc:
|
||||
|
||||
```bash
|
||||
tea issues create --title "..." --description "$(cat <<'EOF'
|
||||
Multi-line issue body.
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
- **Read an issue with comments**:
|
||||
`tea issues <number> --comments --fields index,state,author,url,title,body,assignees,labels,comments --output json`.
|
||||
- **List issues**:
|
||||
`tea issues list --state open --fields index,state,author,url,title,body,assignees,labels,comments --output json`,
|
||||
adding `--labels "label-one,label-two"`, `--assignee <user>`, or another
|
||||
supported filter as needed. Paginate with `--page` and `--limit`; do not
|
||||
assume the default first 30 results are complete.
|
||||
- **Comment on an issue**: `tea comments add <number> --description "..."`.
|
||||
- **Apply or remove labels**: `tea issues edit <number> --add-labels "..."` or
|
||||
`--remove-labels "..."`. Separate multiple labels with commas.
|
||||
- **Assign or unassign**: `tea issues edit <number> --add-assignees "..."` or
|
||||
`--remove-assignees "..."`.
|
||||
- **Close**: post the closing explanation with
|
||||
`tea comments add <number> --description "..."`, then run
|
||||
`tea issues close <number>`.
|
||||
- **Reopen**: `tea issues reopen <number>`.
|
||||
- **Raw API fallback**: use `tea api '<endpoint>'` only for a Gitea feature
|
||||
without a first-class `tea` command. Prefer `{owner}` and `{repo}`
|
||||
placeholders so repository context remains explicit.
|
||||
|
||||
Use `--output json` for machine-readable reads. Do not scrape table output.
|
||||
|
||||
## Pull requests as a triage surface
|
||||
|
||||
**PRs as a request surface: no.** _(Set this to `yes` if this repo treats
|
||||
external pull requests as feature requests; `/triage` reads this flag.)_
|
||||
|
||||
When set to `yes`, PRs use the same labels and states as issues:
|
||||
|
||||
- **Read a PR with comments and diff**:
|
||||
`tea pulls <number> --comments --fields index,state,author,url,title,body,diff,assignees,labels,comments --output json`.
|
||||
- **List open PRs**:
|
||||
`tea pulls list --state open --fields index,state,author,author-id,url,title,body,assignees,labels,comments --output json`.
|
||||
Determine whether an author is external from the repository's Gitea membership
|
||||
data; the PR listing does not classify author membership.
|
||||
- **Comment**: `tea comments add <number> --description "..."`.
|
||||
- **Apply or remove labels**: `tea pulls edit <number> --add-labels "..."` or
|
||||
`--remove-labels "..."`.
|
||||
- **Close**: `tea pulls close <number>`.
|
||||
|
||||
Gitea pull requests use the repository's issue index space, so `#42` may refer
|
||||
to either surface. If the context is unclear, inspect it with
|
||||
`tea pulls 42 --output json`; if that command reports no PR, read it with
|
||||
`tea issues 42 --output json`.
|
||||
|
||||
## When a skill says "publish to the issue tracker"
|
||||
|
||||
Create a Gitea issue with `tea issues create`.
|
||||
|
||||
## When a skill says "fetch the relevant ticket"
|
||||
|
||||
Run
|
||||
`tea issues <number> --comments --fields index,state,author,url,title,body,assignees,labels,comments --output json`.
|
||||
|
||||
## Wayfinding operations
|
||||
|
||||
`/wayfinder` uses one **map** issue with linked **child** issues. These
|
||||
conventions store relationships in issue bodies, so they work across Gitea
|
||||
versions.
|
||||
|
||||
- **Map**: create one issue labelled `wayfinder:map` containing the Notes,
|
||||
Decisions-so-far, and Fog sections. Use
|
||||
`tea issues create --labels "wayfinder:map"` with its title and description.
|
||||
- **Child ticket**: start the body with `Part of #<map>` and apply a
|
||||
`wayfinder:<type>` label: `research`, `prototype`, `grilling`, or `task`. Add
|
||||
each child as a task-list item in the map body to preserve frontier order.
|
||||
- **Blocking**: put `Blocked by: #<n>, #<n>` near the top of the child body. A
|
||||
ticket becomes unblocked after every referenced blocker closes.
|
||||
- **Frontier query**: list all open children, order them by the map's task list,
|
||||
and discard tickets with an open blocker or an assignee. Select the first
|
||||
remaining ticket. Fetch each referenced blocker; do not infer its state from
|
||||
map text.
|
||||
- **Claim**: resolve the active username with `tea whoami`, then run
|
||||
`tea issues edit <n> --add-assignees "<username>"` as the session's first
|
||||
write. Do not assume `@me` works.
|
||||
- **Resolve**: run `tea comments add <n> --description "<answer>"`, close the
|
||||
child with `tea issues close <n>`, then append a context pointer and link to
|
||||
the map's Decisions-so-far section.
|
||||
@@ -1,14 +0,0 @@
|
||||
# Triage labels
|
||||
|
||||
Engineering skills use five canonical triage roles. This table maps each role to
|
||||
the label string in this repository's issue tracker.
|
||||
|
||||
| Canonical role | Tracker label | Meaning |
|
||||
| ----------------- | ----------------- | -------------------------------------- |
|
||||
| `needs-triage` | `needs-triage` | A maintainer must evaluate the issue |
|
||||
| `needs-info` | `needs-info` | Waiting for more information |
|
||||
| `ready-for-agent` | `ready-for-agent` | Fully specified and ready for an agent |
|
||||
| `ready-for-human` | `ready-for-human` | Requires human implementation |
|
||||
| `wontfix` | `wontfix` | Maintainers will not act on the issue |
|
||||
|
||||
When a skill names a canonical role, use the corresponding tracker label.
|
||||
@@ -1,3 +1,4 @@
|
||||
import { dev } from '$app/environment';
|
||||
import { json, type Handle, type HandleServerError } from '@sveltejs/kit';
|
||||
import { stringify } from 'devalue';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
@@ -15,6 +16,15 @@ import {
|
||||
} from '$lib/server/data-moderation';
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
// THROWAWAY PROTOTYPE: fixture-only review must not open the shared database.
|
||||
if (
|
||||
dev &&
|
||||
event.url.pathname === '/database' &&
|
||||
event.url.searchParams.get('prototype') === 'model-status'
|
||||
) {
|
||||
return resolve(event);
|
||||
}
|
||||
|
||||
if (
|
||||
event.url.pathname === '/admin' ||
|
||||
event.url.pathname.startsWith('/admin/')
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
import { dev } from '$app/environment';
|
||||
import { getSessionUser } from '$lib/server/auth/session';
|
||||
import { hasPermission } from '$lib/server/auth/authorization';
|
||||
import type { LayoutServerLoad } from './$types';
|
||||
import { notificationSummary } from '$lib/server/notifications';
|
||||
import { getSiteAccessSettings } from '$lib/server/site-access';
|
||||
|
||||
export const load: LayoutServerLoad = async ({ cookies }) => {
|
||||
export const load: LayoutServerLoad = async ({ cookies, url }) => {
|
||||
// THROWAWAY PROTOTYPE: fixture-only review must not open the shared database.
|
||||
if (dev && url.pathname === '/database' && url.searchParams.get('prototype') === 'model-status') {
|
||||
return {
|
||||
user: null,
|
||||
isacAccess: false,
|
||||
notifications: { unread: 0 },
|
||||
siteAccess: {
|
||||
comingSoonEnabled: false,
|
||||
accountCreationEnabled: true,
|
||||
sections: { news: true, database: true, builds: true, guides: true, builder: true },
|
||||
updatedAt: 'prototype'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const user = await getSessionUser(cookies);
|
||||
return {
|
||||
user,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { dev } from '$app/environment';
|
||||
import { getDatabase } from '$lib/server/database';
|
||||
import { getRegistrySearch } from '$lib/server/pagination';
|
||||
import { databaseCategories } from '$lib/database/categories';
|
||||
@@ -16,6 +17,11 @@ const tables = {
|
||||
} as const;
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
// THROWAWAY PROTOTYPE: avoid opening the shared database for fixture-only review.
|
||||
if (dev && url.searchParams.get('prototype') === 'model-status') {
|
||||
return { registries: [], total: 0, search: '', searchResults: [] };
|
||||
}
|
||||
|
||||
const database = await getDatabase();
|
||||
const { search, pattern } = getRegistrySearch(url);
|
||||
const counts = await Promise.all(
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { dev } from '$app/environment';
|
||||
import { page } from '$app/state';
|
||||
import ModelStatusPrototype from './ModelStatusPrototype.prototype.svelte';
|
||||
import type { PrototypeVariant } from './model-status.prototype';
|
||||
|
||||
let { data } = $props();
|
||||
const onlineRegistries = $derived(data.registries.filter((registry) => registry.count > 0).length);
|
||||
const prototypeVariant = $derived.by<PrototypeVariant | null>(() => {
|
||||
if (!dev || page.url.searchParams.get('prototype') !== 'model-status') return null;
|
||||
const variant = page.url.searchParams.get('variant');
|
||||
return variant === 'A' || variant === 'B' || variant === 'C' ? variant : null;
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -8,6 +18,9 @@
|
||||
<meta name="description" content="Community-maintained The Division Resurgence attributes, talents, brands, gear, weapons, mods, protocols, skill chips, and specializations data."/>
|
||||
</svelte:head>
|
||||
|
||||
{#if prototypeVariant}
|
||||
<ModelStatusPrototype variant={prototypeVariant} />
|
||||
{:else}
|
||||
<main class="page community-index-page">
|
||||
<header class="catalogue-hero community-index-hero">
|
||||
<div><p class="eyebrow"><span></span> ISAC PUBLIC ARCHIVE</p><h1>RESURGENCE <em>DATABASE.</em></h1><p>Explore equipment, talents, modifications, and gameplay effects from The Division Resurgence.</p></div>
|
||||
@@ -54,3 +67,4 @@
|
||||
{/each}
|
||||
</section>
|
||||
</main>
|
||||
{/if}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
import ModelStatusPrototypeSwitcher from './ModelStatusPrototypeSwitcher.prototype.svelte';
|
||||
import ModelStatusVariantA from './ModelStatusVariantA.prototype.svelte';
|
||||
import ModelStatusVariantB from './ModelStatusVariantB.prototype.svelte';
|
||||
import ModelStatusVariantC from './ModelStatusVariantC.prototype.svelte';
|
||||
import type { PrototypeVariant } from './model-status.prototype';
|
||||
|
||||
let { variant }: { variant: PrototypeVariant } = $props();
|
||||
</script>
|
||||
|
||||
<!-- THROWAWAY PROTOTYPE: three issue #5 variants on the existing /database route. -->
|
||||
<main class="page model-status-prototype">
|
||||
{#if variant === 'A'}
|
||||
<ModelStatusVariantA />
|
||||
{:else if variant === 'B'}
|
||||
<ModelStatusVariantB />
|
||||
{:else}
|
||||
<ModelStatusVariantC />
|
||||
{/if}
|
||||
</main>
|
||||
<ModelStatusPrototypeSwitcher current={variant} />
|
||||
@@ -0,0 +1,45 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import { onMount } from 'svelte';
|
||||
import { prototypeVariantNames, type PrototypeVariant } from './model-status.prototype';
|
||||
|
||||
let { current }: { current: PrototypeVariant } = $props();
|
||||
const variants: PrototypeVariant[] = ['A', 'B', 'C'];
|
||||
|
||||
const cycle = (direction: -1 | 1) => {
|
||||
const currentIndex = variants.indexOf(current);
|
||||
const next = variants[(currentIndex + direction + variants.length) % variants.length];
|
||||
const params = new URLSearchParams(page.url.searchParams);
|
||||
params.set('prototype', 'model-status');
|
||||
params.set('variant', next);
|
||||
void goto(`${page.url.pathname}?${params.toString()}${page.url.hash}`, {
|
||||
keepFocus: true,
|
||||
noScroll: true,
|
||||
replaceState: true
|
||||
});
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
const handleKeydown = (event: KeyboardEvent) => {
|
||||
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
|
||||
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
|
||||
if (event.target instanceof HTMLElement && event.target.closest('input, textarea, select, [contenteditable]:not([contenteditable="false"])')) return;
|
||||
event.preventDefault();
|
||||
cycle(event.key === 'ArrowLeft' ? -1 : 1);
|
||||
};
|
||||
window.addEventListener('keydown', handleKeydown);
|
||||
return () => window.removeEventListener('keydown', handleKeydown);
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- THROWAWAY PROTOTYPE: development-only variant switcher for issue #5. -->
|
||||
<nav class="prototype-switcher" aria-label="Model status prototype variants">
|
||||
<button type="button" onclick={() => cycle(-1)} aria-label="Show previous prototype variant">←</button>
|
||||
<strong aria-live="polite" title={prototypeVariantNames[current]} aria-label={`Variant ${current}: ${prototypeVariantNames[current]}`}>{current}</strong>
|
||||
<button type="button" onclick={() => cycle(1)} aria-label="Show next prototype variant">→</button>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
.prototype-switcher{position:fixed;z-index:1000;right:14px;bottom:14px;display:grid;grid-template-columns:36px 34px 36px;height:36px;border:1px solid var(--shd-border-strong);box-shadow:0 7px 20px #000a;background:var(--shd-bg);color:var(--shd-text)}.prototype-switcher button{display:grid;place-items:center;border:0;background:var(--shd-surface-raised);color:var(--shd-orange);font:800 17px/1 var(--shd-font-mono);cursor:pointer}.prototype-switcher button:first-child{border-right:1px solid var(--shd-border)}.prototype-switcher button:last-child{border-left:1px solid var(--shd-border)}.prototype-switcher button:hover{background:var(--shd-surface-active);color:var(--shd-orange-hover)}.prototype-switcher button:active{transform:translateY(1px);filter:brightness(.82)}.prototype-switcher button:focus-visible{position:relative;z-index:1;outline:2px solid var(--shd-focus-ring);outline-offset:2px}.prototype-switcher>strong{display:grid;place-items:center;color:var(--shd-text);font:900 12px/1 var(--shd-font-mono);letter-spacing:.6px}@media(max-width:520px){.prototype-switcher{right:8px;bottom:8px}}@media(prefers-reduced-motion:reduce){.prototype-switcher button{transition:none}}
|
||||
</style>
|
||||
@@ -0,0 +1,78 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
modelStatusFixtures,
|
||||
publicBehavior,
|
||||
publicStatus
|
||||
} from './model-status.prototype';
|
||||
|
||||
const quickViewFixtures = [modelStatusFixtures[0], modelStatusFixtures[3]];
|
||||
</script>
|
||||
|
||||
<!-- THROWAWAY PROTOTYPE: issue #5, variant A. -->
|
||||
<div class="prototype-a">
|
||||
<header class="prototype-heading">
|
||||
<div>
|
||||
<p><span></span> DEVELOPMENT PROTOTYPE · ISSUE #5 · VARIANT A</p>
|
||||
<h1>MODELING <em>STATUS.</em></h1>
|
||||
</div>
|
||||
<p>Integrated card hierarchy keeps the public state in the identity header and gives behavior one primary reading block.</p>
|
||||
</header>
|
||||
|
||||
<section aria-labelledby="a-card-heading">
|
||||
<header class="surface-heading">
|
||||
<div><span>01 / CARD PRESENTATION</span><h2 id="a-card-heading">DATABASE CARDS</h2></div>
|
||||
<small>ALL MECHANIC OWNERS · PUBLIC STATE ONLY</small>
|
||||
</header>
|
||||
<div class="card-grid">
|
||||
{#each modelStatusFixtures as fixture}
|
||||
<article class="model-card">
|
||||
<header>
|
||||
<div><small>{fixture.owner}</small><h3>{fixture.name}</h3><span>{fixture.context}</span></div>
|
||||
<strong class:not-modeled={publicStatus(fixture) === 'NOT MODELED'}>{publicStatus(fixture)}</strong>
|
||||
</header>
|
||||
<div class="behavior-block">
|
||||
<small>{publicStatus(fixture) === 'MODELED' ? 'MODELED BEHAVIOR' : 'AUTHORED DESCRIPTION'}</small>
|
||||
<p>{publicBehavior(fixture)}</p>
|
||||
</div>
|
||||
<footer><span>READ-ONLY FIXTURE</span><b>VIEW DETAILS →</b></footer>
|
||||
</article>
|
||||
{/each}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section aria-labelledby="a-detail-heading">
|
||||
<header class="surface-heading">
|
||||
<div><span>02 / DETAIL PRESENTATION</span><h2 id="a-detail-heading">QUICK VIEW</h2></div>
|
||||
<small>MODELED TEXT FIRST · AUTHORED FALLBACK WHEN NOT MODELED</small>
|
||||
</header>
|
||||
<div class="quick-view">
|
||||
{#each quickViewFixtures as fixture}
|
||||
<article>
|
||||
<header><div><small>{fixture.owner}</small><h3>{fixture.name}</h3></div><strong class:not-modeled={publicStatus(fixture) === 'NOT MODELED'}>{publicStatus(fixture)}</strong></header>
|
||||
<div><span>BEHAVIOR</span><p>{publicBehavior(fixture)}</p></div>
|
||||
<dl><div><dt>RECORD</dt><dd>{fixture.context}</dd></div><div><dt>DISPLAY SOURCE</dt><dd>{publicStatus(fixture) === 'MODELED' ? 'Modeled behavior' : 'Authored description'}</dd></div></dl>
|
||||
</article>
|
||||
{/each}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section aria-labelledby="a-search-heading">
|
||||
<header class="surface-heading">
|
||||
<div><span>03 / GLOBAL SEARCH PRESENTATION</span><h2 id="a-search-heading">SEARCH RESULTS</h2></div>
|
||||
<small>{modelStatusFixtures.length} FIXTURE MATCHES</small>
|
||||
</header>
|
||||
<div class="search-results">
|
||||
{#each modelStatusFixtures as fixture}
|
||||
<div class="search-result">
|
||||
<small>{fixture.owner}</small><strong>{fixture.name}</strong><span>{publicBehavior(fixture)}</span><b class:not-modeled={publicStatus(fixture) === 'NOT MODELED'}>{publicStatus(fixture)}</b>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.prototype-a{display:grid;gap:28px;padding:20px 0 110px}.prototype-heading{min-height:150px;display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,420px);align-items:end;gap:24px;padding:24px;border:1px solid var(--shd-border);border-top:2px solid var(--shd-orange);background:var(--shd-surface)}.prototype-heading p{margin:0;color:var(--shd-text-muted);font:var(--shd-type-supporting)/1.5 var(--shd-font-editorial)}.prototype-heading>div>p{color:var(--shd-orange);font:900 var(--shd-type-telemetry)/1 var(--shd-font-sans);letter-spacing:1px}.prototype-heading>div>p span{display:inline-block;width:22px;border-top:2px solid currentColor;vertical-align:middle}.prototype-heading h1{margin:10px 0 0;font-size:clamp(34px,5vw,64px);line-height:.9;letter-spacing:-1px}.prototype-heading h1 em{color:var(--shd-orange);font-style:normal}.prototype-a>section{display:grid;gap:10px}.surface-heading{min-height:46px;display:flex;align-items:end;justify-content:space-between;gap:16px;padding-bottom:9px;border-bottom:1px solid var(--shd-border)}.surface-heading>div{display:flex;align-items:baseline;gap:12px}.surface-heading span,.surface-heading small{color:var(--shd-text-muted);font-size:var(--shd-type-telemetry);font-weight:800;letter-spacing:.8px}.surface-heading h2{margin:0;font-size:20px;letter-spacing:.6px}.card-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.model-card{min-width:0;display:flex;flex-direction:column;border:1px solid var(--shd-border);border-top:2px solid var(--shd-border-strong);background:var(--shd-surface)}.model-card>header{min-height:92px;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:start;gap:10px;padding:14px;border-bottom:1px solid var(--shd-border)}.model-card header small,.quick-view header small{display:block;margin-bottom:6px;color:var(--shd-text-muted);font-size:var(--shd-type-telemetry);font-weight:900;letter-spacing:.8px}.model-card h3,.quick-view h3{margin:0;font-size:18px;line-height:1.05}.model-card header span{display:block;margin-top:7px;color:var(--shd-text-dim);font-size:var(--shd-type-telemetry)}.model-card header strong,.quick-view header strong,.search-result b{padding:5px 7px;border:1px solid var(--shd-success);color:var(--shd-success);font-size:10px;line-height:1;letter-spacing:.7px;white-space:nowrap}.model-card header strong.not-modeled,.quick-view header strong.not-modeled,.search-result b.not-modeled{border-color:var(--shd-border-strong);color:var(--shd-text-muted)}.behavior-block{flex:1;margin:12px;padding:12px;border-left:2px solid var(--shd-success);background:var(--shd-model-surface)}.model-card:has(.not-modeled) .behavior-block{border-left-color:var(--shd-text-dim)}.behavior-block small{color:var(--shd-text-dim);font-size:10px;font-weight:900;letter-spacing:.9px}.behavior-block p{margin:7px 0 0;color:var(--shd-text);font:var(--shd-type-supporting)/1.5 var(--shd-font-editorial)}.model-card>footer{display:flex;justify-content:space-between;gap:10px;padding:10px 12px;border-top:1px solid var(--shd-border-soft);color:var(--shd-text-dim);font-size:10px;font-weight:900;letter-spacing:.6px}.model-card>footer b{color:var(--shd-orange)}.quick-view{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.quick-view>article{border:1px solid var(--shd-border);background:var(--shd-surface-raised)}.quick-view article>header{display:flex;align-items:start;justify-content:space-between;gap:16px;padding:16px;border-bottom:1px solid var(--shd-border)}.quick-view article>div{padding:18px 16px}.quick-view article>div span{color:var(--shd-text-muted);font-size:10px;font-weight:900;letter-spacing:1px}.quick-view article>div p{max-width:62ch;margin:8px 0 0;font:18px/1.55 var(--shd-font-editorial)}.quick-view dl{display:grid;grid-template-columns:1fr 1fr;margin:0;border-top:1px solid var(--shd-border)}.quick-view dl>div{padding:10px 16px;border-left:1px solid var(--shd-border)}.quick-view dl>div:first-child{border-left:0}.quick-view dt{color:var(--shd-text-dim);font-size:10px;font-weight:900;letter-spacing:.8px}.quick-view dd{margin:4px 0 0;color:var(--shd-text-muted);font-size:var(--shd-type-telemetry)}.search-results{border:1px solid var(--shd-border);background:var(--shd-model-surface)}.search-result{display:grid;grid-template-columns:190px minmax(150px,.7fr) minmax(260px,1.6fr) auto;align-items:center;gap:12px;min-height:58px;padding:8px 12px;border-top:1px solid var(--shd-border-soft)}.search-result:first-child{border-top:0}.search-result>small{color:var(--shd-orange);font-size:var(--shd-type-telemetry);font-weight:900;letter-spacing:.5px}.search-result>strong{font-size:var(--shd-type-supporting)}.search-result>span{color:var(--shd-text-muted);font:var(--shd-type-telemetry)/1.4 var(--shd-font-editorial)}
|
||||
@media(max-width:1000px){.card-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.search-result{grid-template-columns:minmax(140px,.7fr) minmax(0,1fr) auto}.search-result>small{grid-column:1/-1}.search-result>span{grid-column:2}}
|
||||
@media(max-width:700px){.prototype-heading,.quick-view,.card-grid{grid-template-columns:1fr}.prototype-heading{align-items:start}.surface-heading{align-items:start}.surface-heading>div{display:grid;gap:4px}.surface-heading>small{display:none}.search-result{grid-template-columns:1fr auto}.search-result>small,.search-result>span{grid-column:1/-1}}
|
||||
</style>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,93 @@
|
||||
// THROWAWAY PROTOTYPE for issue #5. Fixture lifecycle data must never become a public API.
|
||||
export type PrototypeVariant = 'A' | 'B' | 'C';
|
||||
export type PublicModelStatus = 'MODELED' | 'NOT MODELED';
|
||||
|
||||
type MechanicLifecycle =
|
||||
'ready' | 'requires_assumptions' | 'captured_only' | 'absent';
|
||||
|
||||
export type ModelStatusFixture = {
|
||||
id: string;
|
||||
owner:
|
||||
| 'Talent'
|
||||
| 'OS Protocol Talent'
|
||||
| 'Skill Chip Talent'
|
||||
| 'Specialization tree-rank effect';
|
||||
name: string;
|
||||
context: string;
|
||||
internal: {
|
||||
lifecycle: MechanicLifecycle;
|
||||
current: boolean;
|
||||
};
|
||||
modeledText?: string;
|
||||
authoredText: string;
|
||||
};
|
||||
|
||||
export const modelStatusFixtures: ModelStatusFixture[] = [
|
||||
{
|
||||
id: 'talent-ready',
|
||||
owner: 'Talent',
|
||||
name: 'Predatory Advance',
|
||||
context: 'Weapon Talent · Tier I',
|
||||
internal: { lifecycle: 'ready', current: true },
|
||||
modeledText:
|
||||
'On enemy kill: gain +15% Weapon Damage for 10 s. Refreshes and stacks up to 3 times.',
|
||||
authoredText:
|
||||
'Killing an enemy grants 15% weapon damage for 10s. Stacks up to 3 times.',
|
||||
},
|
||||
{
|
||||
id: 'protocol-assumptions',
|
||||
owner: 'OS Protocol Talent',
|
||||
name: 'Emergency Weave',
|
||||
context: 'Superior OS Protocol · Talent',
|
||||
internal: { lifecycle: 'requires_assumptions', current: true },
|
||||
modeledText:
|
||||
'When armor breaks: restore 20% Armor over 5 s. Cooldown: 20 s.',
|
||||
authoredText:
|
||||
'When armor is broken, restore 20% armor over 5s. Cooldown: 20s.',
|
||||
},
|
||||
{
|
||||
id: 'chip-captured',
|
||||
owner: 'Skill Chip Talent',
|
||||
name: 'Rapid Instruction',
|
||||
context: 'Tactical Skill Chip Set · 3-piece talent',
|
||||
internal: { lifecycle: 'captured_only', current: true },
|
||||
modeledText: 'After skill use: gain +8% Skill Haste for 12 s.',
|
||||
authoredText: 'Using a skill grants 8% Skill Haste for 12s.',
|
||||
},
|
||||
{
|
||||
id: 'specialization-absent',
|
||||
owner: 'Specialization tree-rank effect',
|
||||
name: 'Perfect Cadence · Rank 3',
|
||||
context: 'Sharpshooter · Precision branch',
|
||||
internal: { lifecycle: 'absent', current: true },
|
||||
authoredText:
|
||||
'After reaching 5 hits without missing, your next shot deals 25% increased damage.',
|
||||
},
|
||||
{
|
||||
id: 'talent-stale',
|
||||
owner: 'Talent',
|
||||
name: 'Measured Focus',
|
||||
context: 'Gear Talent · Tier II',
|
||||
internal: { lifecycle: 'ready', current: false },
|
||||
modeledText:
|
||||
'After a headshot: gain +8% Critical Hit Damage for 8 s, stacking up to 3 times.',
|
||||
authoredText:
|
||||
'Headshots grant 10% Critical Hit Damage for 8s. Stacks up to 4 times.',
|
||||
},
|
||||
];
|
||||
|
||||
export const publicStatus = (fixture: ModelStatusFixture): PublicModelStatus =>
|
||||
fixture.internal.current && fixture.internal.lifecycle !== 'absent'
|
||||
? 'MODELED'
|
||||
: 'NOT MODELED';
|
||||
|
||||
export const publicBehavior = (fixture: ModelStatusFixture): string =>
|
||||
publicStatus(fixture) === 'MODELED' && fixture.modeledText
|
||||
? fixture.modeledText
|
||||
: fixture.authoredText;
|
||||
|
||||
export const prototypeVariantNames = {
|
||||
A: 'Integrated card hierarchy',
|
||||
B: 'Evidence rail',
|
||||
C: 'Registry rows',
|
||||
} satisfies Record<PrototypeVariant, string>;
|
||||
Reference in New Issue
Block a user