Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
8ab92b7
docs: add IMPROVEMENT-024 design spec (daily objective announcement +…
Sellafield May 23, 2026
b9e6178
docs: add IMPROVEMENT-024 implementation plan
Sellafield May 23, 2026
ba0a577
feat: announce daily objective pool on server cold boot (IMPROVEMENT-…
Sellafield May 23, 2026
9a280a3
feat: add TodaysDailyObjectiveRow record (IMPROVEMENT-024)
Sellafield May 23, 2026
0804d11
feat: add LoadTodaysDailyObjectivesAsync to Admin Tool SeasonReposito…
Sellafield May 23, 2026
e14efdb
feat: add TodaysDailyObjectives collection to SeasonStatisticsViewMod…
Sellafield May 23, 2026
7227c6b
feat: add Today's Daily Objectives section to Statistics tab (IMPROVE…
Sellafield May 23, 2026
6bbc96f
fix: align GetObjectives sort order and clarify isFirstLoad scope (IM…
Sellafield May 23, 2026
90cd610
docs: mark IMPROVEMENT-024 as DONE, move to completed backlog
Sellafield May 23, 2026
9816221
docs: remove completed DONE issues from issues.md backlog
Sellafield May 23, 2026
478576d
docs: add design spec for IMPROVEMENT-025 equipment set synergy bonuses
Sellafield May 23, 2026
88682b5
docs: fix spec gaps in IMPROVEMENT-025 design (effectcategory, active…
Sellafield May 23, 2026
cd4cbe2
docs: add implementation plan for IMPROVEMENT-025 equipment set bonuses
Sellafield May 23, 2026
19f2dec
feat: add equipment set DB schema (IMPROVEMENT-025)
Sellafield May 23, 2026
cc6b8b4
feat: add effect_equipment_set_bonus effect type (IMPROVEMENT-025)
Sellafield May 23, 2026
5ae4930
feat: add equipment set data types and repository interface (IMPROVEM…
Sellafield May 23, 2026
85210f9
feat: implement EquipmentSetRepository with in-memory cache (IMPROVEM…
Sellafield May 23, 2026
5c0f2ce
feat: add EquipmentSetBonusCalculator (IMPROVEMENT-025)
Sellafield May 23, 2026
13afce5
chore: remove unused System.Linq using in EquipmentSetBonusCalculator
Sellafield May 23, 2026
2396dfe
feat: integrate equipment set bonuses into Robot stat pipeline (IMPRO…
Sellafield May 23, 2026
a937ac3
chore: fix using order and improve Robot set bonus field comments (IM…
Sellafield May 23, 2026
a418d6b
feat: add SetBonusEffectApplicator for zone effect display (IMPROVEME…
Sellafield May 24, 2026
0932276
fix: materialize Except and add InZone guard in SetBonusEffectApplica…
Sellafield May 24, 2026
b30c2f3
feat: call SetBonusEffectApplicator in Robot.OnUpdate (IMPROVEMENT-025)
Sellafield May 24, 2026
0a671fd
feat: register EquipmentSetRepository and EquipmentSetBonusCalculator…
Sellafield May 24, 2026
b6d334f
feat: add pilot set content SQL for set_striker (IMPROVEMENT-025)
Sellafield May 24, 2026
012e37e
Extended daily objectives announcement format
Sellafield May 24, 2026
a9b6394
fix: add NicSpent/NicEarned season tracking to MarketBuyItem (ISSUE-020)
Sellafield May 24, 2026
feed137
backlog
Sellafield May 24, 2026
29c29f2
docs: add design spec for IMPROVEMENT-027 equipment set bonus effect …
Sellafield May 24, 2026
d832752
docs: add implementation plan for IMPROVEMENT-027
Sellafield May 24, 2026
0e165ba
feat: embed set bonus modifier values in effect display (IMPROVEMENT-…
Sellafield May 24, 2026
66d3274
docs: mark IMPROVEMENT-027 as DONE
Sellafield May 24, 2026
9a33510
docs: add design spec for IMPROVEMENT-028 equipment set admin tool
Sellafield May 24, 2026
19f8bb1
docs: update IMPROVEMENT-028 spec - cf_robot_equipment filter and agg…
Sellafield May 24, 2026
6ccbd29
docs: add implementation plan for IMPROVEMENT-028
Sellafield May 24, 2026
7003d14
feat: add equipment set row types and pick items (IMPROVEMENT-028)
Sellafield May 24, 2026
e621fde
refactor: make SetId a plain property in EquipmentSetRow
Sellafield May 24, 2026
cf1248a
feat: add EquipmentSetRepository for Admin Tool (IMPROVEMENT-028)
Sellafield May 24, 2026
96a4962
feat: add EquipmentSetChanges SQL builder (IMPROVEMENT-028)
Sellafield May 24, 2026
9d413c1
refactor: use SqlLiteral.Of for bonusValue in BuildUpsertThreshold
Sellafield May 24, 2026
e79bc0a
feat: add AddSetMemberViewModel and picker dialog (IMPROVEMENT-028)
Sellafield May 24, 2026
bbdec01
feat: add EquipmentSetsViewModel (IMPROVEMENT-028)
Sellafield May 24, 2026
e252eb9
fix: guard AddThreshold against null SelectedSet
Sellafield May 24, 2026
e822726
feat: add EquipmentSetsView XAML and code-behind (IMPROVEMENT-028)
Sellafield May 24, 2026
4569373
refactor: use direct Command bindings and remove RenameBox x:Name
Sellafield May 24, 2026
ef3fdbd
feat: wire Equipment Sets tab into MainViewModel and MainWindow (IMPR…
Sellafield May 24, 2026
938dabe
docs: mark IMPROVEMENT-028 as DONE
Sellafield May 24, 2026
7744c86
feat: replace inline threshold ComboBox with modal picker dialog (IMP…
Sellafield May 24, 2026
a1aefa6
Max speed for NPC in fleeing state reduced to 50%
Sellafield May 24, 2026
6e3299c
docs: spec for codebase graph workflow integration
Sellafield May 24, 2026
5efc20a
docs: implementation plan for codebase graph workflow integration
Sellafield May 24, 2026
080ed87
feat: add graph query script tools/query-graph.ps1
Sellafield May 24, 2026
8268ed9
fix: filter class-level nodes and add hashtable index for O(1) edge l…
Sellafield May 24, 2026
ae2e2f0
docs: update codebase-graph.md with GRAPH_REPORT, query script, and e…
Sellafield May 24, 2026
b47453c
fix: correct contains edge count and add namespace node query caveat
Sellafield May 24, 2026
f5ce8c8
docs: wire codebase graph into CLAUDE.md workflow and knowledge table
Sellafield May 24, 2026
2725755
fix: improve graph workflow step — unconditional check, correct flags…
Sellafield May 24, 2026
eacb217
fix: accurate community filter, doc corrections for sparsity and comm…
Sellafield May 24, 2026
cf254e0
backlog
Sellafield May 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 27 additions & 16 deletions .claude/knowledge/codebase-graph.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,46 @@
# Codebase Dependency Graph

Generated by graphify-dotnet from the `src/` directory. Regenerates automatically before
each build of `Perpetuum.Server` (requires .NET 10 SDK for the graphify tool — `dotnet tool restore` from the
each build of `Perpetuum.Server` (requires .NET 10 SDK and `dotnet tool restore` from the
solution root).

## Artifacts

- **`docs/graph/graph.json`** — machine-readable graph; gitignored,
present after any local `Perpetuum.Server` build
- **`docs/graph/GRAPH_REPORT.md`** — Markdown architecture report; gitignored, same condition
- **`docs/graph/GRAPH_REPORT.md`** — Markdown summary: god nodes, community clusters, stats.
**Read this first.** Token-efficient starting point for any architectural question.
- **`docs/graph/graph.json`** — Full machine-readable graph (19,926 nodes, 33,689 edges).
Use via `tools/query-graph.ps1` — do not read the raw file.
- **GitHub Wiki** — latest report published by CI:
`https://github.com/OpenPerpetuum/PerpetuumServer2/wiki/Codebase-Graph`

## Graph Structure

Nodes represent C# classes, methods, and namespaces (type: `Entity` or `File`).
Edges represent inheritance, composition, and namespace imports.
Communities (Louvain clustering, clusters) group related symbols.
Edges have two relationship types:
- **`contains`** (33,623 edges) — within-file hierarchy: file → namespace → class → method
- **`imports`** (66 edges) — cross-file namespace dependencies (the meaningful ones for impact analysis)

Communities (Louvain clustering) group related symbols into 2,616 clusters.

## How to Use

**Impact analysis** — when a type is modified, query `graph.json` for edges pointing to that
node's `id` to find all dependents before assessing blast radius.
| Goal | Action |
|---|---|
| Architectural overview or god-node check | Read `docs/graph/GRAPH_REPORT.md` |
| Blast radius before modifying a class | `.\tools\query-graph.ps1 <ClassName> -Direction in` |
| What a class depends on | `.\tools\query-graph.ps1 <ClassName> -Direction out` |
| Full dependency picture | `.\tools\query-graph.ps1 <ClassName>` (default: both) |
| Find other `.cs` files in the same Louvain cluster | `.\tools\query-graph.ps1 <ClassName> -Direction community` |

## God-Node Awareness

**God-node awareness** — the top 10 most-connected symbols are listed in `GRAPH_REPORT.md`
under "God Nodes". These are the highest-risk symbols to change: `RelocateItems`,
`LootItemRepository`, `PackItems`, `ChangeAmmo`, `UnstackAmount`, `EquipModule`,
`ListContainer`, `IWeatherService`, `SetItemName`, `ILootItemRepository` — see `GRAPH_REPORT.md` for the full list.
The top 10 most-connected symbols are listed in `docs/graph/GRAPH_REPORT.md` under "God Nodes".
These are the highest-risk symbols to change — check the report before modifying any of them.
The list regenerates on each build; do not rely on hardcoded names from prior sessions.

**Subsystem navigation** — look up a class node in `graph.json` to find its `community` ID,
then find other nodes with the same `community` to discover related types in the same cluster.
Note: `tools/query-graph.ps1` matches `.cs` file nodes only — namespace-level entries in the God Nodes list (e.g. `Perpetuum.RequestHandlers`) cannot be queried by name and must be explored via `GRAPH_REPORT.md` directly.

**Dependency verification** — check for edge paths between two namespaces to confirm whether
an unintended cross-subsystem dependency would be introduced by a change.
> **Note on import sparsity:** Only 66 `imports` edges exist across the entire codebase (AST-only
> analysis). Only ~7 classes have meaningful inbound edges — for all others, `-Direction in` returns
> only the parent file's `contains` edge, not actual callers. Absence of inbound edges is the norm,
> not a signal that a class is safe to change.
10 changes: 6 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,11 @@ For any non-trivial task:
1. Identify affected subsystems
2. Identify relevant documentation
3. Locate similar implementations
4. Understand existing patterns
5. Evaluate runtime implications
6. Produce a short implementation plan
7. Then implement
4. Check `docs/graph/GRAPH_REPORT.md` for God Nodes (high-risk symbols); run `.\tools\query-graph.ps1 <ClassName> -Direction in` to enumerate direct dependents — a null result is normal (most classes have no detected importers) and does not mean the change is safe (if `graph.json` is absent, skip and continue to step 5)
5. Understand existing patterns
6. Evaluate runtime implications
7. Produce a short implementation plan
8. Then implement

---

Expand Down Expand Up @@ -328,6 +329,7 @@ Avoid parallel abstractions unless justified.
|---|---|
| Main AI instructions | `CLAUDE.md` |
| Architecture deep-dive | `.claude/knowledge/architecture.md` |
| Codebase graph & impact analysis | `.claude/knowledge/codebase-graph.md` |
| Specialist agents | `.claude/agents/<name>.md` |


Expand Down
26 changes: 26 additions & 0 deletions docs/Patches/p36.2/Features/EquipmentSets/effects_row.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-- Insert effects table row for effect_equipment_set_bonus (ID 139)
-- (IMPROVEMENT-025)
-- Idempotent: safe to run multiple times.
-- The effects.id column is IDENTITY(1,1), so IDENTITY_INSERT is required
-- when inserting a specific ID value.

SET IDENTITY_INSERT effects ON;

IF NOT EXISTS (SELECT 1 FROM effects WHERE id = 139)
BEGIN
INSERT INTO effects
(id, name, description, effectcategory, duration, isaura, auraradius, ispositive, display, saveable)
VALUES
(139,
N'Set Bonus Active',
N'An equipment set bonus is active on this robot.',
0,
0,
0,
0,
1,
1,
0);
END;

SET IDENTITY_INSERT effects OFF;
30 changes: 30 additions & 0 deletions docs/Patches/p36.2/Features/EquipmentSets/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-- Equipment Set Synergy Bonuses Migration (IMPROVEMENT-025)
-- Run once against the game database before deploying the updated server binary.

CREATE TABLE equipment_sets (
set_id INT NOT NULL IDENTITY(1,1),
name NVARCHAR(64) NOT NULL,
CONSTRAINT PK_equipment_sets PRIMARY KEY (set_id),
CONSTRAINT UQ_equipment_sets_name UNIQUE (name)
);

CREATE TABLE equipment_set_members (
set_id INT NOT NULL,
definition INT NOT NULL,
CONSTRAINT PK_equipment_set_members PRIMARY KEY (set_id, definition),
CONSTRAINT FK_equipment_set_members_set FOREIGN KEY (set_id)
REFERENCES equipment_sets (set_id),
CONSTRAINT FK_equipment_set_members_def FOREIGN KEY (definition)
REFERENCES entitydefaults (definition)
);

CREATE TABLE equipment_set_bonus_thresholds (
set_id INT NOT NULL,
required_pieces INT NOT NULL,
aggregate_field INT NOT NULL,
bonus_value FLOAT NOT NULL,
CONSTRAINT PK_equipment_set_bonus_thresholds
PRIMARY KEY (set_id, required_pieces, aggregate_field),
CONSTRAINT FK_equipment_set_bonus_thresholds_set FOREIGN KEY (set_id)
REFERENCES equipment_sets (set_id)
);
50 changes: 50 additions & 0 deletions docs/Patches/p36.2/Features/EquipmentSets/set_striker_pilot.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-- Pilot set: set_striker (IMPROVEMENT-025 validation content)
-- Idempotent: safe to run multiple times.
--
-- AggregateField IDs used:
-- 17 = armor_max_modifier (formula: Modifier — 1.05 = +5% max armor)
-- 310 = resist_kinetic_modifier (formula: Modifier — 1.08 = +8% kinetic resist)
--
-- Member modules: all four tiers of medium armor plates.
-- Definition names are resolved dynamically via entitydefaults — no hardcoded IDs.

-- 1. Create the set (idempotent)
IF NOT EXISTS (SELECT 1 FROM equipment_sets WHERE name = N'set_striker')
INSERT INTO equipment_sets (name) VALUES (N'set_striker');

DECLARE @set_id INT;
SELECT @set_id = set_id FROM equipment_sets WHERE name = N'set_striker';

-- 2. Assign member modules — all four medium armor plate tiers
-- Skips any member already present to remain idempotent.
INSERT INTO equipment_set_members (set_id, definition)
SELECT @set_id, ed.definition
FROM entitydefaults ed
WHERE ed.definitionname IN (
N'def_standard_medium_armor_plate',
N'def_named1_medium_armor_plate',
N'def_named2_medium_armor_plate',
N'def_named3_medium_armor_plate'
)
AND NOT EXISTS (
SELECT 1
FROM equipment_set_members esm
WHERE esm.set_id = @set_id
AND esm.definition = ed.definition
);

-- 3. 2-piece threshold: +5% max armor (aggregate_field = 17, bonus_value = 1.05)
IF NOT EXISTS (
SELECT 1 FROM equipment_set_bonus_thresholds
WHERE set_id = @set_id AND required_pieces = 2 AND aggregate_field = 17
)
INSERT INTO equipment_set_bonus_thresholds (set_id, required_pieces, aggregate_field, bonus_value)
VALUES (@set_id, 2, 17, 1.05);

-- 4. 4-piece threshold: +8% kinetic resist (aggregate_field = 310, bonus_value = 1.08)
IF NOT EXISTS (
SELECT 1 FROM equipment_set_bonus_thresholds
WHERE set_id = @set_id AND required_pieces = 4 AND aggregate_field = 310
)
INSERT INTO equipment_set_bonus_thresholds (set_id, required_pieces, aggregate_field, bonus_value)
VALUES (@set_id, 4, 310, 1.08);
Loading
Loading