From b9e2336a6307f26577ee2c8060c86e1d59b72304 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 26 Aug 2026 22:59:11 +0200 Subject: [PATCH 01/20] Add typed app control-flow map --- .gitignore | 2 + AGENTS.md | 7 + Delvehold.sln | 9 + Delvehold/Documentation/Control Flow Map.md | 31 + Delvehold/Documentation/Implementation Map.md | 3 +- Delvehold/Documentation/index.md | 1 + README.md | 7 + modeling/control-flow/graph.ts | 292 +++ modeling/control-flow/index.html | 12 + modeling/control-flow/main.tsx | 31 + modeling/control-flow/package-lock.json | 1944 +++++++++++++++++ modeling/control-flow/package.json | 25 + modeling/control-flow/style.css | 18 + modeling/control-flow/tsconfig.json | 20 + modeling/control-flow/verify.ts | 155 ++ modeling/control-flow/vite.config.ts | 8 + scripts/control-flow.ps1 | 84 + scripts/verify-runtime.ps1 | 3 + 18 files changed, 2651 insertions(+), 1 deletion(-) create mode 100644 Delvehold/Documentation/Control Flow Map.md create mode 100644 modeling/control-flow/graph.ts create mode 100644 modeling/control-flow/index.html create mode 100644 modeling/control-flow/main.tsx create mode 100644 modeling/control-flow/package-lock.json create mode 100644 modeling/control-flow/package.json create mode 100644 modeling/control-flow/style.css create mode 100644 modeling/control-flow/tsconfig.json create mode 100644 modeling/control-flow/verify.ts create mode 100644 modeling/control-flow/vite.config.ts create mode 100644 scripts/control-flow.ps1 diff --git a/.gitignore b/.gitignore index 6cebb1b..25f8f6f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ Thumbs.db client/.godot/ *.user *.suo +modeling/control-flow/node_modules/ +modeling/control-flow/dist/ diff --git a/AGENTS.md b/AGENTS.md index 6afcd52..42898f5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,6 +27,13 @@ - Shared Quartz machinery remains owned by `GameCult/GameCult-Quartz`; do not vendor it here. - Generated output under `quartz-site/public/` is disposable and must not become source truth. +## Control-flow modeling + +- `modeling/control-flow/graph.ts` owns the typed, source-addressed map of the current executable body. Delvehold owns its semantics and provenance; Norn owns rendering behavior only. +- Any change to client, host, protocol, solution/build, or launcher control flow requires review of the affected nodes, edges, owners, and source witnesses. Admit the resulting normalized source hash only after that review. +- Run `scripts/control-flow.ps1 validate` before accepting runtime work. New executable files within the inventoried game-runtime surfaces (`client/`, `src/`, root solution/MSBuild files, and top-level runtime launchers), stale witnesses, broken graph references, and changed inventoried sources must fail this check rather than disappearing from the map. Quartz publishing belongs to the documentation body; the Norn viewer belongs to the modeling tool body. +- Graph inspection is non-mutating. A future control surface must still cross the typed Delvehold command-and-receipt boundary; selection in Norn is never gameplay authority. + ## Lore prose - Use `F:\Projects\Kalsa\workshop\review-council\critics\AI-Isms and Prose.md` as the review brief for Lore prose. Its named patterns are prompts to reread, not forbidden-token rules or quality metrics. diff --git a/Delvehold.sln b/Delvehold.sln index a191be1..d37728a 100644 --- a/Delvehold.sln +++ b/Delvehold.sln @@ -8,6 +8,15 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Delvehold.WorldHost", "src\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Delvehold.Client", "client\Delvehold.Client.csproj", "{F764A271-81B2-42ED-8D07-BB6C00281029}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Control Flow", "Control Flow", "{612EA672-270E-476C-9B76-B29A1CC6327B}" + ProjectSection(SolutionItems) = preProject + modeling\control-flow\graph.ts = modeling\control-flow\graph.ts + modeling\control-flow\main.tsx = modeling\control-flow\main.tsx + modeling\control-flow\package.json = modeling\control-flow\package.json + modeling\control-flow\verify.ts = modeling\control-flow\verify.ts + scripts\control-flow.ps1 = scripts\control-flow.ps1 + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/Delvehold/Documentation/Control Flow Map.md b/Delvehold/Documentation/Control Flow Map.md new file mode 100644 index 0000000..1228395 --- /dev/null +++ b/Delvehold/Documentation/Control Flow Map.md @@ -0,0 +1,31 @@ +--- +title: Control Flow Map +description: Typed, source-addressed architecture and execution graph rendered through Norn. +aliases: + - /documentation/control-flow +--- + +# Control Flow Map + +The maintained graph lives in `modeling/control-flow/graph.ts`. It separates two views: + +- **Authority body** names the runtime organs, their owners, and which edges may read, write, construct, dispatch, or render. +- **Control flow** follows executable paths through Godot startup, local projection selection, CultMesh discovery and session establishment, host startup, state seeding, entry admission branches, receipt persistence, response projection, shutdown, and verification. + +Every node and edge carries source witnesses. The source inventory covers the executable game client, host, protocol, root solution and MSBuild files, and top-level runtime launchers with normalized SHA-256 digests. Changing one of those files makes validation fail until the graph is reviewed and the new digest is explicitly admitted. New executable files inside those surfaces also fail the inventory check. Quartz publishing remains part of the documentation body; this Norn view remains part of the modeling-tool body. + +Norn owns graph layout, motion, selection, inspection, and rendering. It does not own the graph's meaning, execute control flow, or issue game commands. + +Run the integrity check and build the viewer: + +```powershell +.\scripts\control-flow.ps1 validate +``` + +Open the interactive Norn view: + +```powershell +.\scripts\control-flow.ps1 view +``` + +The viewer runs at `http://127.0.0.1:4176`. Its process ID and logs live under `artifacts/control-flow/`. diff --git a/Delvehold/Documentation/Implementation Map.md b/Delvehold/Documentation/Implementation Map.md index 17a96c2..9f64aeb 100644 --- a/Delvehold/Documentation/Implementation Map.md +++ b/Delvehold/Documentation/Implementation Map.md @@ -15,6 +15,7 @@ This page describes the body that exists in source. The [[Game Design/index|Game - `Delvehold.Client` is one Godot 4.7.2 C# application. DELVE and HOLD are views inside that application, not separate clients or authorities. - `Delvehold.Protocol` contains the typed CultMesh operation payloads shared by the client and host. It owns no state and performs no simulation. - CultLib revision `334e60f1928b4212a29dd8b0d19b2c099fe6365e` supplies CultCache, CultNet, and CultMesh. Builds reject another revision or a dirty CultLib worktree. The host targets the installed .NET 10 runtime; Godot's C# client targets its supported .NET 8 surface. +- Delvehold owns the semantic [[Control Flow Map|architecture and control-flow graph]] derived from its current source. Norn renders that typed projection and owns presentation behavior only. Selection and inspection are non-mutating; gameplay actions still cross the existing command-and-receipt boundary. ## First live path @@ -31,6 +32,6 @@ The seed state names one workshop, one commons, one dungeon core, one contract, ## Verification layer -`scripts/verify-runtime.ps1` builds the three projects, starts the host, connects the headless Godot client, rejects an idempotency collision and an invalid projection, restarts the host against the same CultCache, and replays the original enter intent. The smoke compares the complete accepted receipt summary, including its original effective time, across both accepted calls, requires the CultCache state file to remain present, and rejects known canonical-write APIs in client source. +`scripts/verify-runtime.ps1` first requires the typed control-flow graph and its Norn viewer to validate, then builds the three projects, starts the host, connects the headless Godot client, rejects an idempotency collision and an invalid projection, restarts the host against the same CultCache, and replays the original enter intent. The smoke compares the complete accepted receipt summary, including its original effective time, across both accepted calls, requires the CultCache state file to remain present, and rejects known canonical-write APIs in client source. Dungeon generation, CultMath randomness, CultGeometry isosurface extraction, gameplay mutation, and the Ghostlight boundary remain subsequent cuts. diff --git a/Delvehold/Documentation/index.md b/Delvehold/Documentation/index.md index a02992d..f58858f 100644 --- a/Delvehold/Documentation/index.md +++ b/Delvehold/Documentation/index.md @@ -7,3 +7,4 @@ The machinery beneath DELVE/HOLD: typed world state, ownership boundaries, clien - [[Authority Map|Authority map]] - [[Implementation Map|Implementation map]] +- [[Control Flow Map|Control-flow map]] diff --git a/README.md b/README.md index 6cd6596..fcc1873 100644 --- a/README.md +++ b/README.md @@ -38,3 +38,10 @@ Open the project in the Godot editor with: ```powershell .\scripts\open-godot.ps1 ``` + +Validate the source-addressed control-flow map or open its interactive Norn view: + +```powershell +.\scripts\control-flow.ps1 validate +.\scripts\control-flow.ps1 view +``` diff --git a/modeling/control-flow/graph.ts b/modeling/control-flow/graph.ts new file mode 100644 index 0000000..3c65872 --- /dev/null +++ b/modeling/control-flow/graph.ts @@ -0,0 +1,292 @@ +import type { + EpiphanyCodeRef, + NornGraphEdge, + NornGraphLink, + NornGraphNode, + NornGraphsState, +} from "@gamecult/norn-viewer"; + +export type AuthorityOwner = + | "godot-client" + | "protocol" + | "world-host" + | "cultlib" + | "verification" + | "developer-tooling" + | "modeling" + | "norn"; + +export type ControlEdgeKind = + | "calls" + | "constructs" + | "signals" + | "discovers" + | "opens-session" + | "sends-intent" + | "dispatches" + | "validates" + | "reads" + | "writes" + | "flushes" + | "returns-receipt" + | "branches" + | "replays" + | "rejects" + | "waits-for" + | "terminates" + | "verifies" + | "renders"; + +export interface OwnedGraphNode extends NornGraphNode { + owner: AuthorityOwner; + runtime: "godot" | "world-host" | "shared" | "tooling" | "external-library"; + code_refs: EpiphanyCodeRef[]; +} + +export interface TypedControlEdge extends NornGraphEdge { + id: string; + kind: ControlEdgeKind; + code_refs: EpiphanyCodeRef[]; +} + +export interface SourceSurface { + path: string; + normalizedSha256: string; + responsibility: string; +} + +const refs = { + client: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "client/Main.cs", symbol, note }), + scene: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "client/Main.tscn", symbol, note }), + project: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "client/project.godot", symbol, note }), + protocol: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "src/Delvehold.Protocol/WorldContracts.cs", symbol, note }), + host: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "src/Delvehold.WorldHost/Program.cs", symbol, note }), + world: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "src/Delvehold.WorldHost/WorldState.cs", symbol, note }), + meshClient: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "@cultlib/src/GameCult.Mesh/CultMeshClient.cs", symbol, note }), + operationServer: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "@cultlib/src/GameCult.Networking/CultNetOperationServer.cs", symbol, note }), + tcp: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "@cultlib/src/GameCult.Networking/CultNetTcpSchemaTransport.cs", symbol, note }), + cache: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "@cultlib/src/GameCult.Mesh/CultMesh.cs", symbol, note }), + verify: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "scripts/verify-runtime.ps1", symbol, note }), + controlFlow: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "scripts/control-flow.ps1", symbol, note }), + openGodot: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "scripts/open-godot.ps1", symbol, note }), + solution: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "Delvehold.sln", symbol, note }), + buildProps: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "Directory.Build.props", symbol, note }), + buildTargets: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "Directory.Build.targets", symbol, note }), + clientProject: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "client/Delvehold.Client.csproj", symbol, note }), + protocolProject: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "src/Delvehold.Protocol/Delvehold.Protocol.csproj", symbol, note }), + hostProject: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "src/Delvehold.WorldHost/Delvehold.WorldHost.csproj", symbol, note }), + graph: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "modeling/control-flow/graph.ts", symbol, note }), + graphVerify: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "modeling/control-flow/verify.ts", symbol, note }), + norn: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "@norn/web/norn-viewer/src/lib/NornViewer.tsx", symbol, note }), +}; + +const architectureNodes: OwnedGraphNode[] = [ + { id: "arch-godot-project", title: "Godot application", purpose: "Own the one DELVE/HOLD presentation process and scene lifetime.", mechanism: "Godot loads Main.tscn and attaches Main.cs to the root Control.", status: "verified", owner: "godot-client", runtime: "godot", code_refs: [refs.project("run/main_scene"), refs.scene("script = ExtResource")] }, + { id: "arch-main-control", title: "Main projection controller", purpose: "Build the current interface, select DELVE or HOLD presentation, and coordinate world entry.", mechanism: "One Godot Control owns local mode widgets and one application-lifetime CultMesh client.", status: "verified", owner: "godot-client", runtime: "godot", code_refs: [refs.client("public partial class Main")] }, + { id: "arch-cultmesh-client", title: "CultMesh client session owner", purpose: "Discover the stable world authority and reuse its typed operation session.", mechanism: "CultMeshClient owns discovery, transport connectors, reconnection, correlation, and response decoding.", status: "verified", owner: "cultlib", runtime: "external-library", code_refs: [refs.meshClient("public sealed class CultMeshClient")] }, + { id: "arch-world-contract", title: "World-entry wire contract", purpose: "Name the Verse, authority runtime, operation, and typed intent/receipt schemas without owning world state.", mechanism: "Protocol DTOs cross CultMesh; they contain no persistence or simulation authority.", status: "verified", owner: "protocol", runtime: "shared", code_refs: [refs.protocol("public static class DelveholdWorldContract"), refs.protocol("public sealed class WorldEnterIntent"), refs.protocol("public sealed class WorldEnterReceipt")] }, + { id: "arch-world-host", title: "Central world host", purpose: "Own the canonical Delvehold world process, admission boundary, and local persistence lifetime.", mechanism: "A single C# process composes CultMesh storage, TCP ingress, discovery, identity, and typed operations.", status: "verified", owner: "world-host", runtime: "world-host", code_refs: [refs.host("var settings = HostSettings.Parse(args)")] }, + { id: "arch-cultmesh-ingress", title: "CultMesh ingress", purpose: "Bind loopback transport, stable authority identity, Verse catalog, and typed operation dispatch.", mechanism: "TCP schema server carries discovery, session identity, and operation envelopes without owning domain admission.", status: "verified", owner: "cultlib", runtime: "world-host", code_refs: [refs.host("TcpFramedCultNetSchemaServer"), refs.host("CultMeshSessionIdentityServer"), refs.host("CultNetOperationServer")] }, + { id: "arch-entry-admission", title: "World-entry admission", purpose: "Validate projection, serialize idempotency decisions, and author accepted or rejected operation results.", mechanism: "One semaphore protects receipt lookup and creation; the host admits only DELVE or HOLD.", status: "verified", owner: "world-host", runtime: "world-host", code_refs: [refs.host("operationGate"), refs.host("invalid-world-entry"), refs.host("idempotency-key-collision")] }, + { id: "arch-world-state", title: "Canonical first-world state", purpose: "Own the first world identity, revision, and initial fixture identifiers.", mechanism: "WorldHost alone defines and seeds DelveholdWorldState when the canonical record is absent.", status: "verified", owner: "world-host", runtime: "world-host", code_refs: [refs.world("public sealed class DelveholdWorldState"), refs.world("FirstWorld()")] }, + { id: "arch-cultcache", title: "CultCache persistence", purpose: "Persist canonical world state and entry receipts across host lifetimes.", mechanism: "CultMeshNode exposes the typed database/cache; explicit flushes make admitted records durable.", status: "verified", owner: "cultlib", runtime: "world-host", code_refs: [refs.cache("CreateNodeAsync"), refs.host("node.Database.PutAsync"), refs.host("node.FlushAsync")] }, + { id: "arch-runtime-verifier", title: "Verification harness", purpose: "Falsify source-map integrity, connection, rejection, forbidden-writer, and restart-persistence claims.", mechanism: "A bounded PowerShell harness validates the typed map, builds three projects, and drives headless Godot against disposable host state.", status: "verified", owner: "verification", runtime: "tooling", code_refs: [refs.verify("control-flow.ps1"), refs.verify("Invoke-GodotSmoke"), refs.verify("forbiddenClientWriters"), refs.controlFlow("run build"), refs.graphVerify("DELVEHOLD_CONTROL_FLOW_INVALID")] }, + { id: "arch-developer-tooling", title: "Build and launch tooling", purpose: "Give Rider, command-line verification, and the Godot editor one admitted project graph and dependency path.", mechanism: "The conventional solution selects three projects; shared MSBuild files pin CultLib; bounded launchers start the editor and focused checks.", status: "verified", owner: "developer-tooling", runtime: "tooling", code_refs: [refs.solution("Delvehold.Client"), refs.buildProps("CultLibRevision"), refs.buildTargets("VerifyCultLibRevision"), refs.openGodot("Start-Process")] }, + { id: "arch-control-flow-map", title: "Typed control-flow map", purpose: "Publish the admitted, source-addressed semantic graph for review and rendering.", mechanism: "TypeScript narrows owners and edge kinds while carrying source witnesses and the admitted executable-source inventory.", status: "active", owner: "modeling", runtime: "tooling", code_refs: [refs.graph("export const controlFlowMap"), refs.graph("sourceInventory")] }, + { id: "arch-norn-viewer", title: "Norn inspection view", purpose: "Render authority and execution graphs for human inspection without gaining command authority.", mechanism: "The React NornViewer consumes the typed projection and owns layout, motion, selection, and detail presentation only.", status: "verified", owner: "norn", runtime: "tooling", code_refs: [refs.norn("export function NornViewer"), refs.graph("nornState")] }, +]; + +const architectureEdges: TypedControlEdge[] = [ + { id: "arch-edge-project-main", source_id: "arch-godot-project", target_id: "arch-main-control", kind: "constructs", label: "root scene", code_refs: [refs.scene("script = ExtResource")] }, + { id: "arch-edge-main-client", source_id: "arch-main-control", target_id: "arch-cultmesh-client", kind: "constructs", label: "application lifetime", code_refs: [refs.client("new CultMeshClient")] }, + { id: "arch-edge-main-contract", source_id: "arch-main-control", target_id: "arch-world-contract", kind: "sends-intent", label: "typed entry", code_refs: [refs.client("InvokeAsync")] }, + { id: "arch-edge-client-ingress", source_id: "arch-cultmesh-client", target_id: "arch-cultmesh-ingress", kind: "opens-session", label: "CultNet TCP", code_refs: [refs.meshClient("ConnectAsync"), refs.tcp("TcpFramedCultNetSchemaServer")] }, + { id: "arch-edge-ingress-admission", source_id: "arch-cultmesh-ingress", target_id: "arch-entry-admission", kind: "dispatches", label: "delvehold.world/enter", code_refs: [refs.host("Register")] }, + { id: "arch-edge-admission-world", source_id: "arch-entry-admission", target_id: "arch-world-state", kind: "reads", label: "world identity/revision", code_refs: [refs.host("node.Cache.Get")] }, + { id: "arch-edge-admission-cache", source_id: "arch-entry-admission", target_id: "arch-cultcache", kind: "writes", label: "accepted receipts only", code_refs: [refs.host("node.Database.PutAsync(receiptKey, receipt)")] }, + { id: "arch-edge-host-ingress", source_id: "arch-world-host", target_id: "arch-cultmesh-ingress", kind: "constructs", label: "one host lifetime", code_refs: [refs.host("using var server") ] }, + { id: "arch-edge-host-world", source_id: "arch-world-host", target_id: "arch-world-state", kind: "constructs", label: "seed if absent", code_refs: [refs.host("DelveholdWorldState.FirstWorld()")] }, + { id: "arch-edge-host-cache", source_id: "arch-world-host", target_id: "arch-cultcache", kind: "constructs", label: "typed store", code_refs: [refs.host("CultMesh.CreateNodeAsync")] }, + { id: "arch-edge-verifier-app", source_id: "arch-runtime-verifier", target_id: "arch-godot-project", kind: "verifies", label: "headless path", code_refs: [refs.verify("$godot --headless")] }, + { id: "arch-edge-verifier-host", source_id: "arch-runtime-verifier", target_id: "arch-world-host", kind: "verifies", label: "restart and replay", code_refs: [refs.verify("Start-WorldHost 'restart'")] }, + { id: "arch-edge-tooling-client", source_id: "arch-developer-tooling", target_id: "arch-godot-project", kind: "constructs", label: "Godot build and editor", code_refs: [refs.clientProject("Godot.NET.Sdk/4.7.2"), refs.openGodot("--editor")] }, + { id: "arch-edge-tooling-host", source_id: "arch-developer-tooling", target_id: "arch-world-host", kind: "constructs", label: "focused .NET build", code_refs: [refs.hostProject("Delvehold.Protocol"), refs.solution("Delvehold.WorldHost")] }, + { id: "arch-edge-tooling-contract", source_id: "arch-developer-tooling", target_id: "arch-world-contract", kind: "constructs", label: "shared protocol build", code_refs: [refs.protocolProject("GameCult.Caching"), refs.solution("Delvehold.Protocol")] }, + { id: "arch-edge-verifier-map", source_id: "arch-runtime-verifier", target_id: "arch-control-flow-map", kind: "verifies", label: "typed graph and source-inventory integrity", code_refs: [refs.verify("control-flow.ps1') validate"), refs.graphVerify("normalizedSha256")] }, + { id: "arch-edge-norn-map", source_id: "arch-norn-viewer", target_id: "arch-control-flow-map", kind: "renders", label: "non-mutating projection", code_refs: [refs.graph("nornState")] }, +]; + +const dataflowNodes: OwnedGraphNode[] = [ + { id: "flow-client-start", title: "Godot loads Main", purpose: "Instantiate the one client root and begin its scene lifetime.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.scene("[node name=\"Delvehold\"")] }, + { id: "flow-ready", title: "Ready", purpose: "Build local presentation and begin asynchronous world entry.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("public override void _Ready()")] }, + { id: "flow-build-ui", title: "Build interface", purpose: "Construct title, mode controls, and connection status without touching world truth.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("private void BuildInterface()")] }, + { id: "flow-mode-select", title: "Select local projection", purpose: "Switch visible DELVE/HOLD mode labels and buttons only.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("private void SelectProjection") ] }, + { id: "flow-enter", title: "Begin world entry", purpose: "Parse launch arguments, construct the application client, and invoke typed entry.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("private async Task EnterWorldAsync()")] }, + { id: "flow-discovery", title: "Discover authority", purpose: "Resolve the stable Verse and world-host runtime to the current physical route.", owner: "cultlib", runtime: "external-library", status: "verified", code_refs: [refs.meshClient("RendezvousEndpoints"), refs.meshClient("ConnectOnlineAsync")] }, + { id: "flow-catalog-response", title: "Return Verse catalog", purpose: "Publish the host route and supported document protocol to discovery callers.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("CreateCatalogResponse")] }, + { id: "flow-session-open", title: "Open identity-bound session", purpose: "Establish the reusable CultMesh documents session to the world host.", owner: "cultlib", runtime: "shared", status: "verified", code_refs: [refs.meshClient("ConnectAsync(target, CultMeshProtocols.Documents") ] }, + { id: "flow-send-entry", title: "Send entry intent", purpose: "Correlate one typed WorldEnterIntent under the caller idempotency key.", owner: "cultlib", runtime: "godot", status: "verified", code_refs: [refs.client("InvokeAsync"), refs.meshClient("CultNetOperationRequestMessage")] }, + { id: "flow-host-start", title: "Start world host", purpose: "Parse state/port settings and assemble the authoritative process.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("HostSettings.Parse(args)")] }, + { id: "flow-open-state", title: "Open typed state", purpose: "Create the CultMesh node over the configured CultCache path and registries.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("CreateCultCacheDocumentRegistry"), refs.host("CultMesh.CreateNodeAsync")] }, + { id: "flow-seed-world", title: "Seed world if absent", purpose: "Create the explicit first-world record once, then flush it.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("if (node.Cache.Get(worldKey) is null)"), refs.world("FirstWorld()")] }, + { id: "flow-bind-ingress", title: "Bind CultMesh ingress", purpose: "Start TCP, identity, catalog, and operation handlers before readiness.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("DELVEHOLD_HOST_READY")] }, + { id: "flow-dispatch-entry", title: "Dispatch entry operation", purpose: "Decode the typed payload and route it to host-owned admission.", owner: "cultlib", runtime: "world-host", status: "verified", code_refs: [refs.operationServer("Register"), refs.host("Register")] }, + { id: "flow-acquire-gate", title: "Serialize admission", purpose: "Prevent concurrent receipt lookup and creation from splitting idempotency authority.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("await operationGate.WaitAsync()")] }, + { id: "flow-validate-entry", title: "Validate entry", purpose: "Normalize the caller identity and admit only DELVE or HOLD projections.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("projection != \"delve\" && projection != \"hold\"")] }, + { id: "flow-invalid-reject", title: "Reject invalid entry", purpose: "Return typed rejection without persisting a receipt.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("invalid-world-entry")] }, + { id: "flow-receipt-lookup", title: "Lookup receipt", purpose: "Read the canonical receipt record for this idempotency key.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("node.Cache.Get(receiptKey)")] }, + { id: "flow-replay-accept", title: "Accept exact replay", purpose: "Return the original receipt when caller and projection match.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("Accepted(existing)")] }, + { id: "flow-collision-reject", title: "Reject key collision", purpose: "Return rejection evidence without replacing the original receipt.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("idempotency-key-collision")] }, + { id: "flow-create-receipt", title: "Create entry receipt", purpose: "Author the first accepted receipt from host-owned world identity and revision.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("var receipt = new WorldEnterReceipt")] }, + { id: "flow-persist-receipt", title: "Persist and flush receipt", purpose: "Commit the accepted receipt before reporting acceptance.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("node.Database.PutAsync(receiptKey, receipt)"), refs.host("await node.FlushAsync()")] }, + { id: "flow-return-response", title: "Return operation response", purpose: "Carry provider-authored status and typed receipt back over the reusable session.", owner: "cultlib", runtime: "shared", status: "verified", code_refs: [refs.operationServer("CultNetOperationReply"), refs.meshClient("CultMeshOperationResult")] }, + { id: "flow-show-result", title: "Show result", purpose: "Project the expected provider status into UI text or smoke exit code.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("private void ShowResult")] }, + { id: "flow-client-error", title: "Project client failure", purpose: "Convert discovery, session, or invocation exceptions into a failed local result without inventing world state.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("catch (Exception error)"), refs.client("error.Message, false")] }, + { id: "flow-client-exit", title: "Dispose client", purpose: "Release the application-lifetime CultMesh client with the scene.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("public override void _ExitTree()")] }, + { id: "flow-host-wait", title: "Wait for stop or failure", purpose: "Keep authority alive until Ctrl+C or ingress failure terminates the process.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("Task.WhenAny(stopped.Task, server.BackgroundFailure)")] }, + { id: "flow-host-cancel", title: "Handle operator cancellation", purpose: "Convert Ctrl+C into orderly completion of the host lifetime.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("Console.CancelKeyPress"), refs.host("stopped.TrySetResult()")] }, + { id: "flow-host-failure", title: "Propagate ingress failure", purpose: "Terminate the host by throwing the captured background listener failure.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("if (server.BackgroundFailure.IsCompleted)"), refs.host("throw await server.BackgroundFailure")] }, + { id: "flow-host-terminated", title: "Host process terminates", purpose: "Dispose host-owned resources after orderly cancellation or propagate a fatal ingress error to the process boundary.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("using var node"), refs.host("throw await server.BackgroundFailure")] }, + { id: "flow-smoke", title: "Runtime smoke sequence", purpose: "Drive accepted entry, collision rejection, invalid rejection, restart, and exact receipt replay.", owner: "verification", runtime: "tooling", status: "verified", code_refs: [refs.verify("Invoke-GodotSmoke 'first'"), refs.verify("Invoke-GodotSmoke 'collision'"), refs.verify("Invoke-GodotSmoke 'restart'")] }, + { id: "flow-map-validation", title: "Validate typed map", purpose: "Reject graph, witness, dependency-pin, inventory, or source-digest drift before compiling the runtime.", owner: "verification", runtime: "tooling", status: "verified", code_refs: [refs.verify("control-flow.ps1') validate"), refs.graphVerify("DELVEHOLD_CONTROL_FLOW_INVALID")] }, + { id: "flow-focused-build", title: "Build focused solution", purpose: "Compile the Godot client, protocol, and world host under the admitted CultLib revision.", owner: "developer-tooling", runtime: "tooling", status: "verified", code_refs: [refs.verify("dotnet build"), refs.solution("Delvehold.Client"), refs.buildProps("CultLibRoot"), refs.buildTargets("CultLib must be revision")] }, + { id: "flow-cultlib-admission", title: "Admit CultLib dependency", purpose: "Require the exact CultLib revision and a clean worktree before project references resolve.", owner: "developer-tooling", runtime: "tooling", status: "verified", code_refs: [refs.buildTargets("VerifyCultLibRevision"), refs.buildProps("CultLibRevision")] }, + { id: "flow-revision-reject", title: "Reject CultLib revision", purpose: "Fail the build when the selected CultLib checkout is not the admitted revision.", owner: "developer-tooling", runtime: "tooling", status: "verified", code_refs: [refs.buildTargets("CultLib must be revision")] }, + { id: "flow-dirty-reject", title: "Reject dirty CultLib", purpose: "Fail the build when the selected CultLib worktree contains unadmitted changes.", owner: "developer-tooling", runtime: "tooling", status: "verified", code_refs: [refs.buildTargets("CultLibRoot must be clean")] }, + { id: "flow-open-editor", title: "Open Godot editor", purpose: "Launch the admitted Godot executable against the client project with the selected CultLib worktree.", owner: "developer-tooling", runtime: "tooling", status: "verified", code_refs: [refs.openGodot("Godot_v4.7.2-stable_mono_win64.exe"), refs.openGodot("Start-Process")] }, +]; + +const e = (id: string, source_id: string, target_id: string, kind: ControlEdgeKind, code_refs: EpiphanyCodeRef[], label?: string): TypedControlEdge => ({ id, source_id, target_id, kind, code_refs, label }); + +const dataflowEdges: TypedControlEdge[] = [ + e("flow-edge-start-ready", "flow-client-start", "flow-ready", "calls", [refs.client("_Ready")]), + e("flow-edge-ready-ui", "flow-ready", "flow-build-ui", "calls", [refs.client("BuildInterface();")]), + e("flow-edge-ready-enter", "flow-ready", "flow-enter", "calls", [refs.client("_ = EnterWorldAsync();")]), + e("flow-edge-ui-mode", "flow-build-ui", "flow-mode-select", "signals", [refs.client("_delve.Pressed"), refs.client("_hold.Pressed")]), + e("flow-edge-enter-mode", "flow-enter", "flow-mode-select", "calls", [refs.client("SelectProjection(options.Projection)")]), + e("flow-edge-enter-discovery", "flow-enter", "flow-discovery", "discovers", [refs.client("RendezvousEndpoints")]), + e("flow-edge-discovery-catalog", "flow-discovery", "flow-catalog-response", "calls", [refs.host("CultMeshVerseCatalogRequestMessage")]), + e("flow-edge-catalog-session", "flow-catalog-response", "flow-session-open", "opens-session", [refs.host("authorityRoutes"), refs.meshClient("ConnectAsync")]), + e("flow-edge-session-send", "flow-session-open", "flow-send-entry", "sends-intent", [refs.meshClient("session.SendCultNet(message)")]), + e("flow-edge-host-open", "flow-host-start", "flow-open-state", "calls", [refs.host("CultMesh.CreateNodeAsync")]), + e("flow-edge-open-seed", "flow-open-state", "flow-seed-world", "branches", [refs.host("is null")]), + e("flow-edge-seed-bind", "flow-seed-world", "flow-bind-ingress", "calls", [refs.host("using var server")]), + e("flow-edge-bind-catalog", "flow-bind-ingress", "flow-catalog-response", "calls", [refs.host("CreateCatalogResponse")]), + e("flow-edge-send-dispatch", "flow-send-entry", "flow-dispatch-entry", "dispatches", [refs.operationServer("HandleAsync")]), + e("flow-edge-dispatch-gate", "flow-dispatch-entry", "flow-acquire-gate", "calls", [refs.host("operationGate.WaitAsync")]), + e("flow-edge-gate-validate", "flow-acquire-gate", "flow-validate-entry", "validates", [refs.host("sourceRuntimeId"), refs.host("projection")]), + e("flow-edge-validate-invalid", "flow-validate-entry", "flow-invalid-reject", "rejects", [refs.host("invalid-world-entry")], "invalid caller or projection"), + e("flow-edge-validate-lookup", "flow-validate-entry", "flow-receipt-lookup", "reads", [refs.host("receiptKey")], "valid entry"), + e("flow-edge-lookup-replay", "flow-receipt-lookup", "flow-replay-accept", "replays", [refs.host("Accepted(existing)")], "exact match"), + e("flow-edge-lookup-collision", "flow-receipt-lookup", "flow-collision-reject", "rejects", [refs.host("idempotency-key-collision")], "same key, different input"), + e("flow-edge-lookup-create", "flow-receipt-lookup", "flow-create-receipt", "branches", [refs.host("var receipt = new WorldEnterReceipt")], "missing receipt"), + e("flow-edge-create-persist", "flow-create-receipt", "flow-persist-receipt", "writes", [refs.host("PutAsync(receiptKey, receipt)")]), + e("flow-edge-persist-response", "flow-persist-receipt", "flow-return-response", "returns-receipt", [refs.host("Accepted(receipt)")]), + e("flow-edge-replay-response", "flow-replay-accept", "flow-return-response", "returns-receipt", [refs.host("Accepted(existing)")]), + e("flow-edge-collision-response", "flow-collision-reject", "flow-return-response", "returns-receipt", [refs.host("Rejected(")]), + e("flow-edge-invalid-response", "flow-invalid-reject", "flow-return-response", "returns-receipt", [refs.host("invalid-world-entry")]), + e("flow-edge-response-show", "flow-return-response", "flow-show-result", "calls", [refs.client("CallDeferred(MethodName.ShowResult")]), + e("flow-edge-enter-error", "flow-enter", "flow-client-error", "branches", [refs.client("catch (Exception error)")], "discovery, session, or invocation failure"), + e("flow-edge-error-show", "flow-client-error", "flow-show-result", "calls", [refs.client("error.Message, false")]), + e("flow-edge-show-exit", "flow-show-result", "flow-client-exit", "terminates", [refs.client("GetTree().Quit")], "headless smoke only"), + e("flow-edge-scene-exit", "flow-client-start", "flow-client-exit", "terminates", [refs.client("_ExitTree")], "ordinary scene teardown"), + e("flow-edge-bind-wait", "flow-bind-ingress", "flow-host-wait", "waits-for", [refs.host("Task.WhenAny")]), + e("flow-edge-wait-cancel", "flow-host-wait", "flow-host-cancel", "branches", [refs.host("stopped.Task")], "Ctrl+C"), + e("flow-edge-wait-failure", "flow-host-wait", "flow-host-failure", "branches", [refs.host("server.BackgroundFailure")], "listener failure"), + e("flow-edge-cancel-stop", "flow-host-cancel", "flow-host-terminated", "terminates", [refs.host("stopped.TrySetResult()")], "complete wait and dispose"), + e("flow-edge-failure-stop", "flow-host-failure", "flow-host-terminated", "terminates", [refs.host("throw await server.BackgroundFailure")], "fatal process exit"), + e("flow-edge-smoke-start", "flow-smoke", "flow-host-start", "verifies", [refs.verify("Start-WorldHost")]), + e("flow-edge-smoke-map", "flow-smoke", "flow-map-validation", "calls", [refs.verify("control-flow.ps1') validate")]), + e("flow-edge-map-build", "flow-map-validation", "flow-focused-build", "calls", [refs.verify("dotnet build")]), + e("flow-edge-smoke-client", "flow-smoke", "flow-client-start", "verifies", [refs.verify("$godot --headless")]), + e("flow-edge-smoke-replay", "flow-smoke", "flow-replay-accept", "verifies", [refs.verify("$firstReceipt -ne $restartReceipt")]), + e("flow-edge-smoke-invalid", "flow-smoke", "flow-invalid-reject", "verifies", [refs.verify("invalid-projection")]), + e("flow-edge-smoke-collision", "flow-smoke", "flow-collision-reject", "verifies", [refs.verify("collision")]), + e("flow-edge-build-admission", "flow-focused-build", "flow-cultlib-admission", "validates", [refs.buildTargets("BeforeTargets=\"ResolveProjectReferences\"")]), + e("flow-edge-admission-revision", "flow-cultlib-admission", "flow-revision-reject", "rejects", [refs.buildTargets("found $([System.String]")], "wrong revision"), + e("flow-edge-admission-dirty", "flow-cultlib-admission", "flow-dirty-reject", "rejects", [refs.buildTargets("CultLibStatus")], "dirty worktree"), + e("flow-edge-admission-client", "flow-cultlib-admission", "flow-client-start", "constructs", [refs.clientProject("Godot.NET.Sdk/4.7.2")], "admitted dependency"), + e("flow-edge-admission-host", "flow-cultlib-admission", "flow-host-start", "constructs", [refs.hostProject("OutputType>Exe")], "admitted dependency"), + e("flow-edge-editor-client", "flow-open-editor", "flow-client-start", "constructs", [refs.openGodot("--editor")], "interactive editor projection"), +]; + +const architectureByFlowId: Record = { + "flow-client-start": "arch-godot-project", + "flow-ready": "arch-main-control", + "flow-build-ui": "arch-main-control", + "flow-mode-select": "arch-main-control", + "flow-enter": "arch-main-control", + "flow-discovery": "arch-cultmesh-client", + "flow-catalog-response": "arch-cultmesh-ingress", + "flow-session-open": "arch-cultmesh-client", + "flow-send-entry": "arch-world-contract", + "flow-host-start": "arch-world-host", + "flow-open-state": "arch-cultcache", + "flow-seed-world": "arch-world-state", + "flow-bind-ingress": "arch-cultmesh-ingress", + "flow-dispatch-entry": "arch-cultmesh-ingress", + "flow-acquire-gate": "arch-entry-admission", + "flow-validate-entry": "arch-entry-admission", + "flow-invalid-reject": "arch-entry-admission", + "flow-receipt-lookup": "arch-cultcache", + "flow-replay-accept": "arch-entry-admission", + "flow-collision-reject": "arch-entry-admission", + "flow-create-receipt": "arch-entry-admission", + "flow-persist-receipt": "arch-cultcache", + "flow-return-response": "arch-cultmesh-ingress", + "flow-show-result": "arch-main-control", + "flow-client-error": "arch-main-control", + "flow-client-exit": "arch-main-control", + "flow-host-wait": "arch-world-host", + "flow-host-cancel": "arch-world-host", + "flow-host-failure": "arch-world-host", + "flow-host-terminated": "arch-world-host", + "flow-smoke": "arch-runtime-verifier", + "flow-map-validation": "arch-runtime-verifier", + "flow-focused-build": "arch-developer-tooling", + "flow-cultlib-admission": "arch-developer-tooling", + "flow-revision-reject": "arch-developer-tooling", + "flow-dirty-reject": "arch-developer-tooling", + "flow-open-editor": "arch-developer-tooling", +}; + +const links: NornGraphLink[] = dataflowNodes.map((node) => ({ + dataflow_node_id: node.id, + architecture_node_id: architectureByFlowId[node.id] ?? `unmapped:${node.id}`, + relationship: "owned-by", + code_refs: node.code_refs, +})); + +export const sourceInventory: SourceSurface[] = [ + { path: "Delvehold.sln", normalizedSha256: "19ce0e87a3b8c7d34b957db8fa1c50f25230a849c39de6872afb81746cb1ca65", responsibility: "Rider and focused build project membership" }, + { path: "Directory.Build.props", normalizedSha256: "898df5c1ff8b0bf7110ac2e27aa585d8d241af85b718e53aaefecd9c1fc8098f", responsibility: "shared target and dependency admission defaults" }, + { path: "Directory.Build.targets", normalizedSha256: "fb703044f822d690ff3afdfa82219d43951f740834f533b7988c18e446da41a6", responsibility: "CultLib revision and cleanliness gate" }, + { path: "client/Delvehold.Client.csproj", normalizedSha256: "0a9a92a87761f2a2525a19ac73bad22c621e2640ab91127c17660e111497cf26", responsibility: "Godot client build dependencies" }, + { path: "client/Main.cs", normalizedSha256: "ea636fd09961d93991e28653f237bc1a3f52661101a4311d697b4865ba4ff23f", responsibility: "Godot client control flow" }, + { path: "client/Main.tscn", normalizedSha256: "a1c1d81dfe77e1921a0f42f0c17d7205ca5d0a0e9ec9373382a568a1885c2f55", responsibility: "Godot root scene construction" }, + { path: "client/project.godot", normalizedSha256: "41021e8586c6d4fb0a5412b4cfce3830fb24161605a95c2c31d3db4d663fd1c8", responsibility: "Godot application entry configuration" }, + { path: "scripts/control-flow.ps1", normalizedSha256: "a986cce42706574f97eed37abbc9af073d01d05333e47013e53f1c9c0f8c21e7", responsibility: "typed map validation and Norn viewer lifecycle" }, + { path: "scripts/open-godot.ps1", normalizedSha256: "de789de343d03451d4b203ce9e863cc92754e59ba6251d6581e6f2aebf81b559", responsibility: "interactive Godot editor launch" }, + { path: "scripts/verify-runtime.ps1", normalizedSha256: "5456cef5679644fd2d553a954a04a224a211d6d330f1d2533a2052699a3f7976", responsibility: "executable runtime acceptance flow" }, + { path: "src/Delvehold.Protocol/Delvehold.Protocol.csproj", normalizedSha256: "d7e665c26dedd151551db6285a753034a12bfa616a20420eb1e42225eabe74b6", responsibility: "wire contract dependencies" }, + { path: "src/Delvehold.Protocol/WorldContracts.cs", normalizedSha256: "dfe85fc3ad5ec078a2b8e490284398160fcde46a6f7728d3cbae8b813877cb13", responsibility: "typed world-entry wire contract" }, + { path: "src/Delvehold.WorldHost/Delvehold.WorldHost.csproj", normalizedSha256: "6301967b27a4319dff8631e093a3ed965f64b099222061873949928fe8504693", responsibility: "world-host build dependencies" }, + { path: "src/Delvehold.WorldHost/Program.cs", normalizedSha256: "73933d99fbdad59aa68ab1115426cb63165017201be7206c991c7b3cbf698184", responsibility: "world-host composition and admission control flow" }, + { path: "src/Delvehold.WorldHost/WorldState.cs", normalizedSha256: "fe471c7f22161e96b5a9cf0cd04e87c8f85017bdbcd3836ddf496db1a8dea120", responsibility: "canonical first-world schema and seed" }, +]; + +export const controlFlowMap = { + schema: "delvehold.control_flow_map.v0" as const, + nornRevision: "14852a5ee160060e0c76872c7542af233bef7dc7" as const, + cultLibRevision: "334e60f1928b4212a29dd8b0d19b2c099fe6365e" as const, + architecture: { nodes: architectureNodes, edges: architectureEdges }, + dataflow: { nodes: dataflowNodes, edges: dataflowEdges }, + links, + sourceInventory, +}; + +export const nornState: NornGraphsState = { + architecture: controlFlowMap.architecture, + dataflow: controlFlowMap.dataflow, + links: controlFlowMap.links, +}; diff --git a/modeling/control-flow/index.html b/modeling/control-flow/index.html new file mode 100644 index 0000000..f66ce6f --- /dev/null +++ b/modeling/control-flow/index.html @@ -0,0 +1,12 @@ + + + + + + DELVE/HOLD Control Flow + + +
+ + + diff --git a/modeling/control-flow/main.tsx b/modeling/control-flow/main.tsx new file mode 100644 index 0000000..4f004be --- /dev/null +++ b/modeling/control-flow/main.tsx @@ -0,0 +1,31 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { NornViewer } from "@gamecult/norn-viewer"; +import { nornState } from "./graph"; +import "./style.css"; + +function App() { + return ( + + ); +} + +createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/modeling/control-flow/package-lock.json b/modeling/control-flow/package-lock.json new file mode 100644 index 0000000..11f6bb1 --- /dev/null +++ b/modeling/control-flow/package-lock.json @@ -0,0 +1,1944 @@ +{ + "name": "@gamecult/delvehold-control-flow", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@gamecult/delvehold-control-flow", + "version": "0.1.0", + "dependencies": { + "@gamecult/norn-viewer": "file:../../../Norn/web/norn-viewer", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@types/node": "^24.10.1", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.2", + "tsx": "^4.21.0", + "typescript": "^5.9.3", + "vite": "^7.2.6" + } + }, + "../../../Norn/web/norn-viewer": { + "name": "@gamecult/norn-viewer", + "version": "0.1.0", + "devDependencies": { + "@types/react": "^19.2.2", + "@types/react-dom": "^19.2.2", + "@vitejs/plugin-react": "^5.1.1", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "tsup": "^8.5.0", + "typescript": "^5.9.3", + "vite": "^7.2.4" + }, + "peerDependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@gamecult/norn-viewer": { + "resolved": "../../../Norn/web/norn-viewer", + "link": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.0.tgz", + "integrity": "sha512-70TeIFezKKy65LgAVyQh+w94/gjWhvPWaLaGGeMEgVrPkQhuj/M5bAYYZzIFUj9Y69oHyTm5Um/R6gcLh4A8JA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.0.tgz", + "integrity": "sha512-YC86tYIHK6M1IV+wbzO+Bxk8RCBr6ZyWYgWxUCzaZD8mc8rrFoIJDNzDrkHBYRc/wKdrsIXmm6/F7NzrAO+OrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.0.tgz", + "integrity": "sha512-oI+ECtUcli0y0fi4xpW82GdPIXdTkI8G8DSjG2LRuw09fPAGykaWYH/hXxiKuTxiAjiPSTIIuYUqof5Z2hShWw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.0.tgz", + "integrity": "sha512-NwV+1s7TiKrMe4owHyKB/dTLD7ZJD0YEBEhIz+hvav1Cu1GReJjF+rsdNwjzENQeIAbE/CoNiaAc5Vz2h5DPAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.0.tgz", + "integrity": "sha512-tWtHBTu5gOPK4u4Urtk4qAHW3zZ9rQAmbssO8gp7ELvGTGI3aCiq6NqyTQ0PCIg7KbHJF2UkGDDs77YZGxfjCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.0.tgz", + "integrity": "sha512-2qPoJiwTvtHQ27NnYvTnsgk8laXWYuVmNESG8WFZBcEPKLfZ3I27qBJarjVRQtwGeYyRfq5ZowHXih9lm2BItw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.0.tgz", + "integrity": "sha512-FQwsTRvLNuHoTdICABJQfbPUSEueISGmnpT06tXTMpfprf5NiKLSXKA0A+w45wJnCmZAnzgqBwbt6ARFuyOi5w==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.0.tgz", + "integrity": "sha512-BBVTXziw8mY1a4ZbWME9tZyfzqXCDPqaC7Z3heQ29p5dkvXzwL0NwelO8zLa8c3RBKvl3YTuSnBgsBhYBtwjIw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.0.tgz", + "integrity": "sha512-w2Iyy9+RqKwx3d9qWMKsJg0FfRBsY0/pXNv0mCQ3ueRvJI6+QAScfD4nrMlzFLs2HNVW6Ew+mtZfDl9b7Ew5/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.0.tgz", + "integrity": "sha512-YK++KtrFRHYE0P6/RtYEAy9t8F37znP+K03RrIuLPYOL6SVlObRumf/0OE4V/h63xL9DwkWbNssZfmA9hawuDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.0.tgz", + "integrity": "sha512-aBfOG6fP7YkkPmTqPwufRJeFyz7WPpECv9XNbnsk9+vg7rxdih0lbtEel7jcRng4LZrrmU3FfitCFyEj4BWDWg==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.0.tgz", + "integrity": "sha512-LGaHEOeHNAag9VuS1Crs5DFg4RrU9MPi2nVnNJk9DTePx/B6RRYKVmrIXt2h7YOJlwjaFJ6lwtFDliZxScTLrQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.0.tgz", + "integrity": "sha512-jClvk+J0FC3b7Udvegiw5/4hErbHtmsNsQgENnKXDWtNCJXsJYZH5WURvu7imDOO38xYml24eeh5x3A04ppwCw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.0.tgz", + "integrity": "sha512-0OJlaGK+8+B777Ql5okIpD7ua5Ro9+VB9Ve0OKa28OQJZ1RbuUBVNHK/e3pr4BROqsyPl1JrPO1ZxJseCNffcA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.0.tgz", + "integrity": "sha512-Ygsx+HoNH7afwi1bTIXbnTvVnsO+zurPLSYxybV1hHFVU72OWOCl6v05ql/z0hkpAPx+DK7Kn9Bi7MayCcjLTA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.0.tgz", + "integrity": "sha512-pDQxtMGb+OvG3fLwR2OkZlSd47hW+kWg4BYMG/++sR6RqorQccwPTDsxda5hPwiIeIErAnCF9ma3SAU06bdQtQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.0.tgz", + "integrity": "sha512-0BnUG9mS8I4SSHr3XsxVhuCMEiu+rX61xxZF5vujso4LaiAGFZFxvDjg6Xn6tLPNTUAfuCvQYas4LMQMVsKRSQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.0.tgz", + "integrity": "sha512-Adu/VttB1dpPNW+FEacrZ+xVm9tFty84+RrFzsqlFaPxoJB+9XXyDGtp5dCOoBwGBIEVH0To7lExFXEx0BIF4A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.0.tgz", + "integrity": "sha512-NQ3bDvjUbFKmP23671xUlXtKmqVsUBd6M4PQCvbmNtOy06hnQIdKHy8oG/6S3R/S6He1JgPk6A5VT+prAJMYEw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.0.tgz", + "integrity": "sha512-u2eDAl4+0aFvA13GxlGBtTI3SS3sdgwgtV0HyjZ0QaQVCgNE+jqNGey+GtxWiq+wxr/UycAx/OnfJzApCFamvA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.0.tgz", + "integrity": "sha512-XvRb5vfW3wAZQ+ZUG21AnHHDKtNcw99eigzEhjr//NZ3u7SoBaPP0seSc7FgP7p1epAEdAoZckMW9WY/+4w70w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.0.tgz", + "integrity": "sha512-iZPmniy4kNBf5yo2RezbkYNNK5HPbXE9+g+twnbqSng7dtLEJy1SKoxiE/ni4FDacjyuZpEeb9U054N4EoKHYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.0.tgz", + "integrity": "sha512-mFBBd+LF37fnE8JnYUOH+imj0aPFPK30vpar4ehJkgnLj9sZn8ZxiRENmLtgIwxK7TC8klF6N57fxdNBwQoqOA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.0.tgz", + "integrity": "sha512-ujeqEY3B+zbGn3Z4Q03cUBG/LGWnBJncVT36WER31LcOsQk9+1dmINKKtvmmfChUvRbK1G0R8OhMWFgHgaZtAw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.0.tgz", + "integrity": "sha512-hncn90N4sOky0L2LKE5oESKLbxCPeVo4eLA2LSMoDzM+879ml4WSr+Rr4DWknNIVVvS1Hirkc9hx02W6YxS8rQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.5.tgz", + "integrity": "sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.19.tgz", + "integrity": "sha512-Grytf1xOxOEMTGRwx6rLGKkTabd4vMg3VrKdj/7joCmV0qgh4QwMMO6xh34YEXQqirAuUdgQGa5orJQQ+69RBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.415", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.415.tgz", + "integrity": "sha512-958V+Kbhtgz+SxXeEVKBjrlKRBIDAYvUJfwhjxMZ5S6ut9jAl7l9ZKBkBrvjyjZE36PabLUo2L8kEeV5O4vgJg==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.53", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", + "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.0.tgz", + "integrity": "sha512-T5vnZ2y4QqC3/4P+w2+JO+Q/OVdnPsv4XcSYJYMEn0R9/jjl5AgLwO9LAZMzP2lN71O6pypn91rB7lDstUkfrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.0", + "@rollup/rollup-android-arm64": "4.63.0", + "@rollup/rollup-darwin-arm64": "4.63.0", + "@rollup/rollup-darwin-x64": "4.63.0", + "@rollup/rollup-freebsd-arm64": "4.63.0", + "@rollup/rollup-freebsd-x64": "4.63.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.0", + "@rollup/rollup-linux-arm-musleabihf": "4.63.0", + "@rollup/rollup-linux-arm64-gnu": "4.63.0", + "@rollup/rollup-linux-arm64-musl": "4.63.0", + "@rollup/rollup-linux-loong64-gnu": "4.63.0", + "@rollup/rollup-linux-loong64-musl": "4.63.0", + "@rollup/rollup-linux-ppc64-gnu": "4.63.0", + "@rollup/rollup-linux-ppc64-musl": "4.63.0", + "@rollup/rollup-linux-riscv64-gnu": "4.63.0", + "@rollup/rollup-linux-riscv64-musl": "4.63.0", + "@rollup/rollup-linux-s390x-gnu": "4.63.0", + "@rollup/rollup-linux-x64-gnu": "4.63.0", + "@rollup/rollup-linux-x64-musl": "4.63.0", + "@rollup/rollup-openbsd-x64": "4.63.0", + "@rollup/rollup-openharmony-arm64": "4.63.0", + "@rollup/rollup-win32-arm64-msvc": "4.63.0", + "@rollup/rollup-win32-ia32-msvc": "4.63.0", + "@rollup/rollup-win32-x64-gnu": "4.63.0", + "@rollup/rollup-win32-x64-msvc": "4.63.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tsx": { + "version": "4.23.12", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.12.tgz", + "integrity": "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", + "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/modeling/control-flow/package.json b/modeling/control-flow/package.json new file mode 100644 index 0000000..ae11cb8 --- /dev/null +++ b/modeling/control-flow/package.json @@ -0,0 +1,25 @@ +{ + "name": "@gamecult/delvehold-control-flow", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "validate": "tsx verify.ts", + "build": "npm run validate && tsc --noEmit && vite build", + "dev": "vite" + }, + "dependencies": { + "@gamecult/norn-viewer": "file:../../../Norn/web/norn-viewer", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@types/node": "^24.10.1", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.2", + "tsx": "^4.21.0", + "typescript": "^5.9.3", + "vite": "^7.2.6" + } +} diff --git a/modeling/control-flow/style.css b/modeling/control-flow/style.css new file mode 100644 index 0000000..a4db184 --- /dev/null +++ b/modeling/control-flow/style.css @@ -0,0 +1,18 @@ +:root { + color-scheme: dark; + font-family: Inter, ui-sans-serif, system-ui, sans-serif; + background: #080706; +} + +html, +body, +#root { + width: 100%; + height: 100%; + margin: 0; + overflow: hidden; +} + +* { + box-sizing: border-box; +} diff --git a/modeling/control-flow/tsconfig.json b/modeling/control-flow/tsconfig.json new file mode 100644 index 0000000..dc189e9 --- /dev/null +++ b/modeling/control-flow/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["*.ts", "*.tsx"] +} diff --git a/modeling/control-flow/verify.ts b/modeling/control-flow/verify.ts new file mode 100644 index 0000000..f468661 --- /dev/null +++ b/modeling/control-flow/verify.ts @@ -0,0 +1,155 @@ +import { createHash } from "node:crypto"; +import { execFileSync } from "node:child_process"; +import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; +import { dirname, relative, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { controlFlowMap } from "./graph"; + +const mapRoot = dirname(fileURLToPath(import.meta.url)); +const repoRoot = resolve(mapRoot, "../.."); +const failures: string[] = []; +const witnessedLocalPaths = new Set(); +const nornRoot = process.env.DELVEHOLD_NORN_ROOT ?? resolve(repoRoot, "../Norn"); +const cultLibRoot = process.env.DELVEHOLD_CULTLIB_ROOT ?? resolve(repoRoot, "../CultLib-aetheria-authority"); + +validateDependency("Norn", nornRoot, controlFlowMap.nornRevision); +validateDependency("CultLib", cultLibRoot, controlFlowMap.cultLibRevision); +const buildProps = readFileSync(resolve(repoRoot, "Directory.Build.props"), "utf8"); +if (!buildProps.includes(`${controlFlowMap.cultLibRevision}`)) { + failures.push("The control-flow CultLib pin disagrees with Directory.Build.props."); +} + +const architectureIds = validateGraph("architecture", controlFlowMap.architecture); +const dataflowIds = validateGraph("dataflow", controlFlowMap.dataflow); +const linkKeys = new Set(); +for (const link of controlFlowMap.links) { + const key = `${link.dataflow_node_id}::${link.architecture_node_id}`; + if (linkKeys.has(key)) failures.push(`Duplicate cross-link '${key}'.`); + linkKeys.add(key); + if (!dataflowIds.has(link.dataflow_node_id)) failures.push(`Cross-link '${key}' has no dataflow node.`); + if (!architectureIds.has(link.architecture_node_id)) failures.push(`Cross-link '${key}' has no architecture node.`); + validateRefs(`cross-link '${key}'`, link.code_refs ?? []); +} + +const expectedSources = collectExecutableSources(); +const inventoriedSources = new Set(controlFlowMap.sourceInventory.map((source) => normalize(source.path))); +for (const path of expectedSources) { + if (!inventoriedSources.has(path)) failures.push(`Executable source '${path}' is missing from sourceInventory.`); +} +for (const path of inventoriedSources) { + if (!expectedSources.has(path)) failures.push(`sourceInventory contains stale or out-of-scope path '${path}'.`); + if (!witnessedLocalPaths.has(path)) failures.push(`Inventoried source '${path}' is not cited by any graph node, edge, or cross-link.`); +} + +for (const source of controlFlowMap.sourceInventory) { + const absolute = resolve(repoRoot, source.path); + if (!existsSync(absolute)) { + failures.push(`Inventoried source '${source.path}' does not exist.`); + continue; + } + const actual = normalizedSha256(readFileSync(absolute, "utf8")); + if (actual !== source.normalizedSha256) { + failures.push(`Source drift '${source.path}': expected ${source.normalizedSha256}, actual ${actual}. Review the graph, then admit the new hash.`); + } +} + +if (failures.length > 0) { + console.error("DELVEHOLD_CONTROL_FLOW_INVALID"); + for (const failure of failures) console.error(`- ${failure}`); + process.exit(1); +} + +console.log(`DELVEHOLD_CONTROL_FLOW_VALID architecture=${architectureIds.size}/${controlFlowMap.architecture.edges.length} dataflow=${dataflowIds.size}/${controlFlowMap.dataflow.edges.length} links=${controlFlowMap.links.length} sources=${expectedSources.size}`); + +function validateGraph( + name: string, + graph: typeof controlFlowMap.architecture, +): Set { + const nodeIds = new Set(); + const edgeIds = new Set(); + for (const node of graph.nodes) { + if (!node.id.trim() || !node.title.trim() || !node.purpose.trim() || !node.owner) { + failures.push(`${name} node '${node.id || ""}' lacks required typed identity, title, purpose, or owner.`); + } + if (nodeIds.has(node.id)) failures.push(`Duplicate ${name} node '${node.id}'.`); + nodeIds.add(node.id); + validateRefs(`${name} node '${node.id}'`, node.code_refs); + } + for (const edge of graph.edges) { + if (edgeIds.has(edge.id)) failures.push(`Duplicate ${name} edge '${edge.id}'.`); + edgeIds.add(edge.id); + if (!nodeIds.has(edge.source_id)) failures.push(`${name} edge '${edge.id}' has missing source '${edge.source_id}'.`); + if (!nodeIds.has(edge.target_id)) failures.push(`${name} edge '${edge.id}' has missing target '${edge.target_id}'.`); + validateRefs(`${name} edge '${edge.id}'`, edge.code_refs); + } + return nodeIds; +} + +function validateRefs(scope: string, refs: readonly { path: string; symbol?: string | null }[]) { + if (refs.length === 0) failures.push(`${scope} has no source witness.`); + for (const ref of refs) { + const normalizedPath = normalize(ref.path); + if (!normalizedPath.startsWith("../") && !normalizedPath.startsWith("@")) witnessedLocalPaths.add(normalizedPath); + const absolute = resolveWitnessPath(normalizedPath); + if (!existsSync(absolute)) { + failures.push(`${scope} references missing source '${ref.path}'.`); + continue; + } + if (ref.symbol && !readFileSync(absolute, "utf8").includes(ref.symbol)) { + failures.push(`${scope} cannot find witness '${ref.symbol}' in '${ref.path}'.`); + } + } +} + +function collectExecutableSources(): Set { + const paths = new Set(); + collect(resolve(repoRoot, "client"), new Set([".cs", ".csproj", ".godot", ".tscn"]), paths); + collect(resolve(repoRoot, "src"), new Set([".cs", ".csproj"]), paths); + collectTopLevel(resolve(repoRoot, "scripts"), new Set([".ps1"]), paths); + collectTopLevel(repoRoot, new Set([".sln", ".props", ".targets"]), paths); + return paths; +} + +function collect(directory: string, extensions: Set, output: Set) { + for (const name of readdirSync(directory)) { + if (["bin", "obj", ".godot"].includes(name)) continue; + const absolute = resolve(directory, name); + if (statSync(absolute).isDirectory()) collect(absolute, extensions, output); + else if (extensions.has(name.slice(name.lastIndexOf(".")))) output.add(normalize(relative(repoRoot, absolute))); + } +} + +function normalizedSha256(content: string): string { + return createHash("sha256").update(content.replace(/\r\n/g, "\n")).digest("hex"); +} + +function collectTopLevel(directory: string, extensions: Set, output: Set) { + for (const name of readdirSync(directory)) { + const absolute = resolve(directory, name); + if (statSync(absolute).isFile() && extensions.has(name.slice(name.lastIndexOf(".")))) { + output.add(normalize(relative(repoRoot, absolute))); + } + } +} + +function resolveWitnessPath(path: string): string { + if (path.startsWith("@cultlib/")) return resolve(cultLibRoot, path.slice("@cultlib/".length)); + if (path.startsWith("@norn/")) return resolve(nornRoot, path.slice("@norn/".length)); + return resolve(repoRoot, path); +} + +function validateDependency(name: string, root: string, expectedRevision: string) { + const git = process.env.DELVEHOLD_GIT ?? "git"; + try { + const actual = execFileSync(git, ["-C", root, "rev-parse", "HEAD"], { encoding: "utf8" }).trim(); + if (actual !== expectedRevision) failures.push(`${name} revision '${actual}' is not admitted '${expectedRevision}'.`); + const status = execFileSync(git, ["-C", root, "status", "--porcelain", "--untracked-files=normal"], { encoding: "utf8" }).trim(); + if (status) failures.push(`${name} worktree '${root}' is dirty.`); + } catch (error) { + failures.push(`Cannot verify ${name} worktree '${root}': ${error instanceof Error ? error.message : String(error)}.`); + } +} + +function normalize(path: string): string { + return path.replaceAll("\\", "/"); +} diff --git a/modeling/control-flow/vite.config.ts b/modeling/control-flow/vite.config.ts new file mode 100644 index 0000000..542263f --- /dev/null +++ b/modeling/control-flow/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +export default defineConfig({ + plugins: [react()], + base: "./", + server: { host: "127.0.0.1", port: 4176, strictPort: true }, +}); diff --git a/scripts/control-flow.ps1 b/scripts/control-flow.ps1 new file mode 100644 index 0000000..5a9140c --- /dev/null +++ b/scripts/control-flow.ps1 @@ -0,0 +1,84 @@ +[CmdletBinding()] +param( + [ValidateSet('validate', 'view')] + [string]$Command = 'validate', + [string]$CultLibRoot = 'F:\Projects\CultLib-aetheria-authority' +) + +$ErrorActionPreference = 'Stop' +$repoRoot = Split-Path $PSScriptRoot -Parent +$NornRoot = Join-Path (Split-Path $repoRoot -Parent) 'Norn' +$viewerRoot = Join-Path $repoRoot 'modeling\control-flow' +$nornViewerRoot = Join-Path $NornRoot 'web\norn-viewer' +$graphSource = Get-Content (Join-Path $viewerRoot 'graph.ts') -Raw +if ($graphSource -notmatch 'nornRevision: "([0-9a-f]{40})"') { + throw 'The typed graph does not declare one Norn revision.' +} +$expectedNornRevision = $Matches[1] +$git = (Get-Command git -ErrorAction Stop).Source +$npm = (Get-Command npm.cmd -ErrorAction Stop).Source +$node = (Get-Command node -ErrorAction Stop).Source +$env:PATH = (Split-Path $node -Parent) + ';' + $env:PATH +$env:npm_config_script_shell = (Get-Command pwsh -ErrorAction Stop).Source +$env:DELVEHOLD_NORN_ROOT = $NornRoot +$env:DELVEHOLD_CULTLIB_ROOT = $CultLibRoot +$env:DELVEHOLD_GIT = $git + +if (-not (Test-Path -LiteralPath (Join-Path $nornViewerRoot 'package.json'))) { + throw "Norn was not found at '$NornRoot'. Clone GameCult/Norn beside Delvehold." +} +$actualNornRevision = (& $git -C $NornRoot rev-parse HEAD).Trim() +if ($actualNornRevision -ne $expectedNornRevision) { + throw "Norn must be revision $expectedNornRevision; found $actualNornRevision." +} +$nornStatus = (& $git -C $NornRoot status --porcelain --untracked-files=normal) -join "`n" +if ($nornStatus.Trim()) { throw 'Norn must be clean before Delvehold consumes it.' } + +if (-not (Test-Path -LiteralPath (Join-Path $nornViewerRoot 'node_modules\.bin\tsup.cmd'))) { + & $npm --prefix $nornViewerRoot ci + if ($LASTEXITCODE -ne 0) { throw 'Norn dependency restore failed.' } +} +Push-Location $nornViewerRoot +try { + & $node '.\node_modules\tsup\dist\cli-default.js' 'src\index.ts' '--format' 'esm,cjs' '--dts' '--external' 'react' '--external' 'react-dom' '--clean' + if ($LASTEXITCODE -ne 0) { throw 'Norn library build failed.' } +} finally { + Pop-Location +} + +if (-not (Test-Path -LiteralPath (Join-Path $viewerRoot 'node_modules\.bin\vite.cmd'))) { + & $npm --prefix $viewerRoot install + if ($LASTEXITCODE -ne 0) { throw 'Delvehold control-flow viewer dependency restore failed.' } +} +& $npm --prefix $viewerRoot run build +if ($LASTEXITCODE -ne 0) { throw 'Typed control-flow validation or Norn viewer build failed.' } + +if ($Command -eq 'validate') { return } + +$runRoot = Join-Path $repoRoot 'artifacts\control-flow' +New-Item -ItemType Directory -Path $runRoot -Force | Out-Null +$logPath = Join-Path $runRoot 'norn-viewer.log' +$errorPath = Join-Path $runRoot 'norn-viewer.error.log' +$pidPath = Join-Path $runRoot 'norn-viewer.pid' +$existingPid = if (Test-Path -LiteralPath $pidPath) { [int](Get-Content $pidPath) } else { 0 } +$existing = if ($existingPid -gt 0) { Get-Process -Id $existingPid -ErrorAction SilentlyContinue } else { $null } +if (-not $existing) { + $process = Start-Process -FilePath $npm -WindowStyle Hidden -PassThru ` + -WorkingDirectory $viewerRoot -ArgumentList @('run', 'dev') ` + -RedirectStandardOutput $logPath -RedirectStandardError $errorPath + Set-Content -Path $pidPath -Value $process.Id + $deadline = [DateTime]::UtcNow.AddSeconds(20) + while ([DateTime]::UtcNow -lt $deadline) { + if ($process.HasExited) { throw "Norn viewer exited. See '$errorPath'." } + try { + $null = Invoke-WebRequest -Uri 'http://127.0.0.1:4176' -UseBasicParsing -TimeoutSec 1 + break + } catch { + Start-Sleep -Milliseconds 150 + } + } + if ([DateTime]::UtcNow -ge $deadline) { throw "Norn viewer did not become ready. See '$logPath'." } +} + +Write-Host "NORN_VIEWER_READY http://127.0.0.1:4176 log=$logPath pid=$(Get-Content $pidPath)" +Start-Process 'http://127.0.0.1:4176' diff --git a/scripts/verify-runtime.ps1 b/scripts/verify-runtime.ps1 index 47e86a4..cc93135 100644 --- a/scripts/verify-runtime.ps1 +++ b/scripts/verify-runtime.ps1 @@ -11,6 +11,9 @@ if (-not (Test-Path -LiteralPath $godot)) { throw "Godot 4.7.2 Mono console was not found at '$godot'." } +& (Join-Path $PSScriptRoot 'control-flow.ps1') validate -CultLibRoot $CultLibRoot +if ($LASTEXITCODE -ne 0) { throw 'The typed control-flow map is stale or invalid.' } + dotnet build (Join-Path $repoRoot 'Delvehold.sln') -p:CultLibRoot=$CultLibRoot if ($LASTEXITCODE -ne 0) { throw 'The focused .NET/Godot build failed.' } From e9b8cbf41f21440bf445d2d4e37c95f07ad4a15a Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 26 Aug 2026 23:00:58 +0200 Subject: [PATCH 02/20] Reframe the site around HOLD --- Delvehold/index.md | 4 +- site/quartz.config.ts | 16 ++--- site/quartz/styles/custom.scss | 127 ++++++++++++++++++++------------- 3 files changed, 87 insertions(+), 60 deletions(-) diff --git a/Delvehold/index.md b/Delvehold/index.md index 394e84a..300eb24 100644 --- a/Delvehold/index.md +++ b/Delvehold/index.md @@ -8,6 +8,8 @@ description: One persistent world, experienced from opposite sides of the contra

Delvehold is a social MMO about building a life together through a magical industrial revolution of the players' own making. Workshops, guilds, and neighborhoods must organize around the abundance, upheaval, and ecological pressure created by their own relentless acceleration. An AI-native world simulation lets institutions, populations, and rival powers remember those changes and pursue their own responses, while the world-dungeon itself learns from every expedition sent to harvest what its living body grows and sheds. DELVE and HOLD are two ways of inhabiting that same persistent world.

+ +

DELVE

Enter a world that needs you to trespass.

@@ -15,7 +17,7 @@ description: One persistent world, experienced from opposite sides of the contra

Dungeons need to be delved. The danger is not an obstacle around the resource; it is part of the old metabolic bargain that lets a core spend, learn, shed, and renew itself.

- +

HOLD

diff --git a/site/quartz.config.ts b/site/quartz.config.ts index a8da7bd..8618356 100644 --- a/site/quartz.config.ts +++ b/site/quartz.config.ts @@ -28,10 +28,10 @@ const config: QuartzConfig = { gray: "#8f8f8f", darkgray: "#f4f4f4", dark: "#ffffff", - secondary: "#2f9663", - tertiary: "#2f9663", - highlight: "rgba(47, 150, 99, 0.15)", - textHighlight: "#2f966355", + secondary: "#996de3", + tertiary: "#996de3", + highlight: "rgba(153, 109, 227, 0.15)", + textHighlight: "#996de355", }, darkMode: { light: "#050505", @@ -39,10 +39,10 @@ const config: QuartzConfig = { gray: "#8f8f8f", darkgray: "#f4f4f4", dark: "#ffffff", - secondary: "#2f9663", - tertiary: "#2f9663", - highlight: "rgba(47, 150, 99, 0.15)", - textHighlight: "#2f966355", + secondary: "#996de3", + tertiary: "#996de3", + highlight: "rgba(153, 109, 227, 0.15)", + textHighlight: "#996de355", }, }, }, diff --git a/site/quartz/styles/custom.scss b/site/quartz/styles/custom.scss index 7be455c..19de68f 100644 --- a/site/quartz/styles/custom.scss +++ b/site/quartz/styles/custom.scss @@ -94,7 +94,7 @@ body { .delvehold-wordmark-hold { color: var(--hold-purple); - font-weight: 100; + font-weight: 800; } .delvehold-wordmark-slash { @@ -126,7 +126,7 @@ article > h3 { a:hover, a.active { - color: var(--delve-green); + color: var(--hold-purple); } } @@ -143,37 +143,30 @@ article > h3 { } body[data-slug="index"] { - background: color-mix(in srgb, var(--delve-green) 8%, #050505); - timeline-scope: --delvehold-mode-shift; + background: color-mix(in srgb, var(--hold-purple) 9%, #050505); + timeline-scope: --delvehold-enter-delve, --delvehold-enter-hold; - &::before { + &::before, + &::after { content: ""; position: fixed; z-index: 0; inset: -35vmax; pointer-events: none; - background: color-mix(in srgb, var(--hold-purple) 9%, #050505); clip-path: polygon(120% 0, 100% 0, 100% 100%, 100% 100%); + filter: drop-shadow(-2px 0 rgba(255, 255, 255, 0.58)); animation: delvehold-diagonal-wipe linear both; - animation-timeline: --delvehold-mode-shift; - animation-range: entry 0% exit 100%; + animation-range: entry 0% cover 50%; + } + + &::before { + background: color-mix(in srgb, var(--delve-green) 8%, #050505); + animation-timeline: --delvehold-enter-delve; } &::after { - content: ""; - position: fixed; - z-index: 0; - top: -20vh; - left: 0; - width: 2px; - height: 140vh; - pointer-events: none; - background: rgba(255, 255, 255, 0.58); - transform: translateX(130vw) rotate(11deg); - transform-origin: center; - animation: delvehold-wipe-edge linear both; - animation-timeline: --delvehold-mode-shift; - animation-range: entry 0% exit 100%; + background: color-mix(in srgb, var(--hold-purple) 9%, #050505); + animation-timeline: --delvehold-enter-hold; } #quartz-root { @@ -195,15 +188,20 @@ body[data-slug="index"] { } .delvehold-wordmark-delve { - animation: delvehold-delve-weight linear both; - animation-timeline: --delvehold-mode-shift; - animation-range: entry 0% exit 100%; + --delvehold-delve-gain: 0; + --delvehold-delve-loss: 0; + font-weight: calc(100 + var(--delvehold-delve-gain) - var(--delvehold-delve-loss)); + animation: delvehold-delve-gain linear both, delvehold-delve-loss linear both; + animation-timeline: --delvehold-enter-delve, --delvehold-enter-hold; + animation-range: entry 0% cover 50%, entry 0% cover 50%; } .delvehold-wordmark-hold { - animation: delvehold-hold-weight linear both; - animation-timeline: --delvehold-mode-shift; - animation-range: entry 0% exit 100%; + --delvehold-hold-gain: 0; + font-weight: calc(100 + var(--delvehold-hold-gain)); + animation: delvehold-hold-gain linear both; + animation-timeline: --delvehold-enter-hold; + animation-range: entry 0% cover 50%; } } @@ -211,7 +209,14 @@ body[data-slug="index"] { height: 1px; margin-top: -1px; pointer-events: none; - view-timeline: --delvehold-mode-shift block; +} + +.delvehold-mode-boundary-delve { + view-timeline: --delvehold-enter-delve block; +} + +.delvehold-mode-boundary-hold { + view-timeline: --delvehold-enter-hold block; } .delvehold-landing { @@ -219,7 +224,10 @@ body[data-slug="index"] { } .delvehold-intro { - padding: 5rem 0 1rem; + display: flex; + align-items: center; + min-height: 72vh; + padding: 5rem 0; p { max-width: 48rem; @@ -281,23 +289,51 @@ body[data-slug="index"] { } } -@keyframes delvehold-delve-weight { - 0%, 42% { - font-weight: 800; +@property --delvehold-delve-gain { + syntax: ""; + inherits: false; + initial-value: 0; +} + +@property --delvehold-delve-loss { + syntax: ""; + inherits: false; + initial-value: 0; +} + +@property --delvehold-hold-gain { + syntax: ""; + inherits: false; + initial-value: 0; +} + +@keyframes delvehold-delve-gain { + from { + --delvehold-delve-gain: 0; } - 58%, 100% { - font-weight: 100; + to { + --delvehold-delve-gain: 700; } } -@keyframes delvehold-hold-weight { - 0%, 42% { - font-weight: 100; +@keyframes delvehold-delve-loss { + from { + --delvehold-delve-loss: 0; } - 58%, 100% { - font-weight: 800; + to { + --delvehold-delve-loss: 700; + } +} + +@keyframes delvehold-hold-gain { + from { + --delvehold-hold-gain: 0; + } + + to { + --delvehold-hold-gain: 700; } } @@ -311,18 +347,7 @@ body[data-slug="index"] { } } -@keyframes delvehold-wipe-edge { - from { - transform: translateX(130vw) rotate(11deg); - } - - to { - transform: translateX(-30vw) rotate(11deg); - } -} - @media (prefers-reduced-motion: reduce) { - body[data-slug="index"], body[data-slug="index"]::before, body[data-slug="index"]::after, body[data-slug="index"] .delvehold-wordmark-delve, From 82458818bc0d968a297f2c2bd151706ef05f2150 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 27 Aug 2026 00:26:53 +0200 Subject: [PATCH 03/20] Advance the admitted Norn revision --- modeling/control-flow/graph.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modeling/control-flow/graph.ts b/modeling/control-flow/graph.ts index 3c65872..24aa3e9 100644 --- a/modeling/control-flow/graph.ts +++ b/modeling/control-flow/graph.ts @@ -277,7 +277,7 @@ export const sourceInventory: SourceSurface[] = [ export const controlFlowMap = { schema: "delvehold.control_flow_map.v0" as const, - nornRevision: "14852a5ee160060e0c76872c7542af233bef7dc7" as const, + nornRevision: "fa4a46bb13333eb703e97ccea9edc1b70ee109b7" as const, cultLibRevision: "334e60f1928b4212a29dd8b0d19b2c099fe6365e" as const, architecture: { nodes: architectureNodes, edges: architectureEdges }, dataflow: { nodes: dataflowNodes, edges: dataflowEdges }, From 8f0f7c10a59bcb131f5f7be961c0baaf9d38895f Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 27 Aug 2026 02:10:20 +0200 Subject: [PATCH 04/20] Restore neutral site presentation --- Delvehold/index.md | 2 +- site/quartz/styles/custom.scss | 38 ++++++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/Delvehold/index.md b/Delvehold/index.md index 300eb24..690d287 100644 --- a/Delvehold/index.md +++ b/Delvehold/index.md @@ -5,7 +5,7 @@ description: One persistent world, experienced from opposite sides of the contra
-

Delvehold is a social MMO about building a life together through a magical industrial revolution of the players' own making. Workshops, guilds, and neighborhoods must organize around the abundance, upheaval, and ecological pressure created by their own relentless acceleration. An AI-native world simulation lets institutions, populations, and rival powers remember those changes and pursue their own responses, while the world-dungeon itself learns from every expedition sent to harvest what its living body grows and sheds. DELVE and HOLD are two ways of inhabiting that same persistent world.

+

Delvehold is a social MMO about building a life together through a magical industrial revolution of the players' own making. Workshops, guilds, and neighborhoods must organize around the abundance, upheaval, and ecological pressure created by their own relentless acceleration.
An AI-native world simulation lets institutions, populations, and rival powers remember those changes and pursue their own responses, while the world-dungeon itself learns from every expedition sent to harvest what its living body grows and sheds.
DELVE and HOLD are two ways of inhabiting that same persistent world.

diff --git a/site/quartz/styles/custom.scss b/site/quartz/styles/custom.scss index 19de68f..4004c23 100644 --- a/site/quartz/styles/custom.scss +++ b/site/quartz/styles/custom.scss @@ -94,7 +94,7 @@ body { .delvehold-wordmark-hold { color: var(--hold-purple); - font-weight: 800; + font-weight: 100; } .delvehold-wordmark-slash { @@ -143,7 +143,7 @@ article > h3 { } body[data-slug="index"] { - background: color-mix(in srgb, var(--hold-purple) 9%, #050505); + background: #050505; timeline-scope: --delvehold-enter-delve, --delvehold-enter-hold; &::before, @@ -154,7 +154,6 @@ body[data-slug="index"] { inset: -35vmax; pointer-events: none; clip-path: polygon(120% 0, 100% 0, 100% 100%, 100% 100%); - filter: drop-shadow(-2px 0 rgba(255, 255, 255, 0.58)); animation: delvehold-diagonal-wipe linear both; animation-range: entry 0% cover 50%; } @@ -209,14 +208,34 @@ body[data-slug="index"] { height: 1px; margin-top: -1px; pointer-events: none; + + &::after { + content: ""; + position: fixed; + z-index: 2; + inset: -35vmax; + pointer-events: none; + background: #fff; + clip-path: polygon(120% 0, 120.15% 0, 100.15% 100%, 100% 100%); + animation: delvehold-wipe-edge linear both; + animation-range: entry 0% cover 50%; + } } .delvehold-mode-boundary-delve { view-timeline: --delvehold-enter-delve block; + + &::after { + animation-timeline: --delvehold-enter-delve; + } } .delvehold-mode-boundary-hold { view-timeline: --delvehold-enter-hold block; + + &::after { + animation-timeline: --delvehold-enter-hold; + } } .delvehold-landing { @@ -347,11 +366,22 @@ body[data-slug="index"] { } } +@keyframes delvehold-wipe-edge { + from { + clip-path: polygon(120% 0, 120.15% 0, 100.15% 100%, 100% 100%); + } + + to { + clip-path: polygon(0 0, 0.15% 0, -19.85% 100%, -20% 100%); + } +} + @media (prefers-reduced-motion: reduce) { body[data-slug="index"]::before, body[data-slug="index"]::after, body[data-slug="index"] .delvehold-wordmark-delve, - body[data-slug="index"] .delvehold-wordmark-hold { + body[data-slug="index"] .delvehold-wordmark-hold, + .delvehold-mode-boundary::after { animation: none; } } From a48410a26883f219da012cc25fe853df7174ba93 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 27 Aug 2026 02:18:13 +0200 Subject: [PATCH 05/20] Lighten site body copy --- Delvehold/index.md | 2 +- site/quartz/styles/custom.scss | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Delvehold/index.md b/Delvehold/index.md index 690d287..0bf4c2c 100644 --- a/Delvehold/index.md +++ b/Delvehold/index.md @@ -5,7 +5,7 @@ description: One persistent world, experienced from opposite sides of the contra
-

Delvehold is a social MMO about building a life together through a magical industrial revolution of the players' own making. Workshops, guilds, and neighborhoods must organize around the abundance, upheaval, and ecological pressure created by their own relentless acceleration.
An AI-native world simulation lets institutions, populations, and rival powers remember those changes and pursue their own responses, while the world-dungeon itself learns from every expedition sent to harvest what its living body grows and sheds.
DELVE and HOLD are two ways of inhabiting that same persistent world.

+

Delvehold is a social MMO about building a life together through a magical industrial revolution of the players' own making. Workshops, guilds, and neighborhoods must organize around the abundance, upheaval, and ecological pressure created by their own relentless acceleration.
An AI-native world simulation lets institutions, populations, and rival powers remember those changes and pursue their own responses, while the world-dungeon itself learns from every expedition sent to harvest what its living body grows and sheds.
DELVE and HOLD are two ways of inhabiting that same persistent world.

diff --git a/site/quartz/styles/custom.scss b/site/quartz/styles/custom.scss index 4004c23..a4af5df 100644 --- a/site/quartz/styles/custom.scss +++ b/site/quartz/styles/custom.scss @@ -8,6 +8,7 @@ body { background: #050505; + font-weight: 300; } .page > #quartz-body .page-header { @@ -255,6 +256,12 @@ body[data-slug="index"] { font-size: clamp(1.15rem, 2vw, 1.5rem); line-height: 1.55; } + + .delvehold-intro-break { + display: block; + content: ""; + margin-top: 1rem; + } } .delvehold-mode-panel { From 396686c600308b65c785198d2101c9ecec23db44 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 27 Aug 2026 02:40:15 +0200 Subject: [PATCH 06/20] Use paragraphs for landing intro --- Delvehold/index.md | 4 +++- site/quartz/styles/custom.scss | 10 ++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Delvehold/index.md b/Delvehold/index.md index 0bf4c2c..2464273 100644 --- a/Delvehold/index.md +++ b/Delvehold/index.md @@ -5,7 +5,9 @@ description: One persistent world, experienced from opposite sides of the contra
-

Delvehold is a social MMO about building a life together through a magical industrial revolution of the players' own making. Workshops, guilds, and neighborhoods must organize around the abundance, upheaval, and ecological pressure created by their own relentless acceleration.
An AI-native world simulation lets institutions, populations, and rival powers remember those changes and pursue their own responses, while the world-dungeon itself learns from every expedition sent to harvest what its living body grows and sheds.
DELVE and HOLD are two ways of inhabiting that same persistent world.

+

Delvehold is a social MMO about building a life together through a magical industrial revolution of the players' own making. Workshops, guilds, and neighborhoods must organize around the abundance, upheaval, and ecological pressure created by their own relentless acceleration.

+

An AI-native world simulation lets institutions, populations, and rival powers remember those changes and pursue their own responses, while the world-dungeon itself learns from every expedition sent to harvest what its living body grows and sheds.

+

DELVE and HOLD are two ways of inhabiting that same persistent world.

diff --git a/site/quartz/styles/custom.scss b/site/quartz/styles/custom.scss index a4af5df..a03146f 100644 --- a/site/quartz/styles/custom.scss +++ b/site/quartz/styles/custom.scss @@ -245,23 +245,17 @@ body[data-slug="index"] { .delvehold-intro { display: flex; - align-items: center; + flex-direction: column; + justify-content: center; min-height: 72vh; padding: 5rem 0; p { max-width: 48rem; - margin: 0; color: #fff; font-size: clamp(1.15rem, 2vw, 1.5rem); line-height: 1.55; } - - .delvehold-intro-break { - display: block; - content: ""; - margin-top: 1rem; - } } .delvehold-mode-panel { From 035a4736c2babce9cc05f64e3efd1af163aefdb7 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 27 Aug 2026 14:24:47 +0200 Subject: [PATCH 07/20] Remove the control-flow modeling surface --- .gitignore | 2 - AGENTS.md | 7 - Delvehold.sln | 9 - Delvehold/Documentation/Control Flow Map.md | 31 - Delvehold/Documentation/Implementation Map.md | 3 +- Delvehold/Documentation/index.md | 1 - README.md | 7 - modeling/control-flow/graph.ts | 292 --- modeling/control-flow/index.html | 12 - modeling/control-flow/main.tsx | 31 - modeling/control-flow/package-lock.json | 1944 ----------------- modeling/control-flow/package.json | 25 - modeling/control-flow/style.css | 18 - modeling/control-flow/tsconfig.json | 20 - modeling/control-flow/verify.ts | 155 -- modeling/control-flow/vite.config.ts | 8 - scripts/control-flow.ps1 | 84 - scripts/verify-runtime.ps1 | 3 - 18 files changed, 1 insertion(+), 2651 deletions(-) delete mode 100644 Delvehold/Documentation/Control Flow Map.md delete mode 100644 modeling/control-flow/graph.ts delete mode 100644 modeling/control-flow/index.html delete mode 100644 modeling/control-flow/main.tsx delete mode 100644 modeling/control-flow/package-lock.json delete mode 100644 modeling/control-flow/package.json delete mode 100644 modeling/control-flow/style.css delete mode 100644 modeling/control-flow/tsconfig.json delete mode 100644 modeling/control-flow/verify.ts delete mode 100644 modeling/control-flow/vite.config.ts delete mode 100644 scripts/control-flow.ps1 diff --git a/.gitignore b/.gitignore index 25f8f6f..6cebb1b 100644 --- a/.gitignore +++ b/.gitignore @@ -18,5 +18,3 @@ Thumbs.db client/.godot/ *.user *.suo -modeling/control-flow/node_modules/ -modeling/control-flow/dist/ diff --git a/AGENTS.md b/AGENTS.md index 42898f5..6afcd52 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,13 +27,6 @@ - Shared Quartz machinery remains owned by `GameCult/GameCult-Quartz`; do not vendor it here. - Generated output under `quartz-site/public/` is disposable and must not become source truth. -## Control-flow modeling - -- `modeling/control-flow/graph.ts` owns the typed, source-addressed map of the current executable body. Delvehold owns its semantics and provenance; Norn owns rendering behavior only. -- Any change to client, host, protocol, solution/build, or launcher control flow requires review of the affected nodes, edges, owners, and source witnesses. Admit the resulting normalized source hash only after that review. -- Run `scripts/control-flow.ps1 validate` before accepting runtime work. New executable files within the inventoried game-runtime surfaces (`client/`, `src/`, root solution/MSBuild files, and top-level runtime launchers), stale witnesses, broken graph references, and changed inventoried sources must fail this check rather than disappearing from the map. Quartz publishing belongs to the documentation body; the Norn viewer belongs to the modeling tool body. -- Graph inspection is non-mutating. A future control surface must still cross the typed Delvehold command-and-receipt boundary; selection in Norn is never gameplay authority. - ## Lore prose - Use `F:\Projects\Kalsa\workshop\review-council\critics\AI-Isms and Prose.md` as the review brief for Lore prose. Its named patterns are prompts to reread, not forbidden-token rules or quality metrics. diff --git a/Delvehold.sln b/Delvehold.sln index d37728a..a191be1 100644 --- a/Delvehold.sln +++ b/Delvehold.sln @@ -8,15 +8,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Delvehold.WorldHost", "src\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Delvehold.Client", "client\Delvehold.Client.csproj", "{F764A271-81B2-42ED-8D07-BB6C00281029}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Control Flow", "Control Flow", "{612EA672-270E-476C-9B76-B29A1CC6327B}" - ProjectSection(SolutionItems) = preProject - modeling\control-flow\graph.ts = modeling\control-flow\graph.ts - modeling\control-flow\main.tsx = modeling\control-flow\main.tsx - modeling\control-flow\package.json = modeling\control-flow\package.json - modeling\control-flow\verify.ts = modeling\control-flow\verify.ts - scripts\control-flow.ps1 = scripts\control-flow.ps1 - EndProjectSection -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/Delvehold/Documentation/Control Flow Map.md b/Delvehold/Documentation/Control Flow Map.md deleted file mode 100644 index 1228395..0000000 --- a/Delvehold/Documentation/Control Flow Map.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Control Flow Map -description: Typed, source-addressed architecture and execution graph rendered through Norn. -aliases: - - /documentation/control-flow ---- - -# Control Flow Map - -The maintained graph lives in `modeling/control-flow/graph.ts`. It separates two views: - -- **Authority body** names the runtime organs, their owners, and which edges may read, write, construct, dispatch, or render. -- **Control flow** follows executable paths through Godot startup, local projection selection, CultMesh discovery and session establishment, host startup, state seeding, entry admission branches, receipt persistence, response projection, shutdown, and verification. - -Every node and edge carries source witnesses. The source inventory covers the executable game client, host, protocol, root solution and MSBuild files, and top-level runtime launchers with normalized SHA-256 digests. Changing one of those files makes validation fail until the graph is reviewed and the new digest is explicitly admitted. New executable files inside those surfaces also fail the inventory check. Quartz publishing remains part of the documentation body; this Norn view remains part of the modeling-tool body. - -Norn owns graph layout, motion, selection, inspection, and rendering. It does not own the graph's meaning, execute control flow, or issue game commands. - -Run the integrity check and build the viewer: - -```powershell -.\scripts\control-flow.ps1 validate -``` - -Open the interactive Norn view: - -```powershell -.\scripts\control-flow.ps1 view -``` - -The viewer runs at `http://127.0.0.1:4176`. Its process ID and logs live under `artifacts/control-flow/`. diff --git a/Delvehold/Documentation/Implementation Map.md b/Delvehold/Documentation/Implementation Map.md index 9f64aeb..17a96c2 100644 --- a/Delvehold/Documentation/Implementation Map.md +++ b/Delvehold/Documentation/Implementation Map.md @@ -15,7 +15,6 @@ This page describes the body that exists in source. The [[Game Design/index|Game - `Delvehold.Client` is one Godot 4.7.2 C# application. DELVE and HOLD are views inside that application, not separate clients or authorities. - `Delvehold.Protocol` contains the typed CultMesh operation payloads shared by the client and host. It owns no state and performs no simulation. - CultLib revision `334e60f1928b4212a29dd8b0d19b2c099fe6365e` supplies CultCache, CultNet, and CultMesh. Builds reject another revision or a dirty CultLib worktree. The host targets the installed .NET 10 runtime; Godot's C# client targets its supported .NET 8 surface. -- Delvehold owns the semantic [[Control Flow Map|architecture and control-flow graph]] derived from its current source. Norn renders that typed projection and owns presentation behavior only. Selection and inspection are non-mutating; gameplay actions still cross the existing command-and-receipt boundary. ## First live path @@ -32,6 +31,6 @@ The seed state names one workshop, one commons, one dungeon core, one contract, ## Verification layer -`scripts/verify-runtime.ps1` first requires the typed control-flow graph and its Norn viewer to validate, then builds the three projects, starts the host, connects the headless Godot client, rejects an idempotency collision and an invalid projection, restarts the host against the same CultCache, and replays the original enter intent. The smoke compares the complete accepted receipt summary, including its original effective time, across both accepted calls, requires the CultCache state file to remain present, and rejects known canonical-write APIs in client source. +`scripts/verify-runtime.ps1` builds the three projects, starts the host, connects the headless Godot client, rejects an idempotency collision and an invalid projection, restarts the host against the same CultCache, and replays the original enter intent. The smoke compares the complete accepted receipt summary, including its original effective time, across both accepted calls, requires the CultCache state file to remain present, and rejects known canonical-write APIs in client source. Dungeon generation, CultMath randomness, CultGeometry isosurface extraction, gameplay mutation, and the Ghostlight boundary remain subsequent cuts. diff --git a/Delvehold/Documentation/index.md b/Delvehold/Documentation/index.md index f58858f..a02992d 100644 --- a/Delvehold/Documentation/index.md +++ b/Delvehold/Documentation/index.md @@ -7,4 +7,3 @@ The machinery beneath DELVE/HOLD: typed world state, ownership boundaries, clien - [[Authority Map|Authority map]] - [[Implementation Map|Implementation map]] -- [[Control Flow Map|Control-flow map]] diff --git a/README.md b/README.md index fcc1873..6cd6596 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,3 @@ Open the project in the Godot editor with: ```powershell .\scripts\open-godot.ps1 ``` - -Validate the source-addressed control-flow map or open its interactive Norn view: - -```powershell -.\scripts\control-flow.ps1 validate -.\scripts\control-flow.ps1 view -``` diff --git a/modeling/control-flow/graph.ts b/modeling/control-flow/graph.ts deleted file mode 100644 index 24aa3e9..0000000 --- a/modeling/control-flow/graph.ts +++ /dev/null @@ -1,292 +0,0 @@ -import type { - EpiphanyCodeRef, - NornGraphEdge, - NornGraphLink, - NornGraphNode, - NornGraphsState, -} from "@gamecult/norn-viewer"; - -export type AuthorityOwner = - | "godot-client" - | "protocol" - | "world-host" - | "cultlib" - | "verification" - | "developer-tooling" - | "modeling" - | "norn"; - -export type ControlEdgeKind = - | "calls" - | "constructs" - | "signals" - | "discovers" - | "opens-session" - | "sends-intent" - | "dispatches" - | "validates" - | "reads" - | "writes" - | "flushes" - | "returns-receipt" - | "branches" - | "replays" - | "rejects" - | "waits-for" - | "terminates" - | "verifies" - | "renders"; - -export interface OwnedGraphNode extends NornGraphNode { - owner: AuthorityOwner; - runtime: "godot" | "world-host" | "shared" | "tooling" | "external-library"; - code_refs: EpiphanyCodeRef[]; -} - -export interface TypedControlEdge extends NornGraphEdge { - id: string; - kind: ControlEdgeKind; - code_refs: EpiphanyCodeRef[]; -} - -export interface SourceSurface { - path: string; - normalizedSha256: string; - responsibility: string; -} - -const refs = { - client: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "client/Main.cs", symbol, note }), - scene: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "client/Main.tscn", symbol, note }), - project: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "client/project.godot", symbol, note }), - protocol: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "src/Delvehold.Protocol/WorldContracts.cs", symbol, note }), - host: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "src/Delvehold.WorldHost/Program.cs", symbol, note }), - world: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "src/Delvehold.WorldHost/WorldState.cs", symbol, note }), - meshClient: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "@cultlib/src/GameCult.Mesh/CultMeshClient.cs", symbol, note }), - operationServer: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "@cultlib/src/GameCult.Networking/CultNetOperationServer.cs", symbol, note }), - tcp: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "@cultlib/src/GameCult.Networking/CultNetTcpSchemaTransport.cs", symbol, note }), - cache: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "@cultlib/src/GameCult.Mesh/CultMesh.cs", symbol, note }), - verify: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "scripts/verify-runtime.ps1", symbol, note }), - controlFlow: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "scripts/control-flow.ps1", symbol, note }), - openGodot: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "scripts/open-godot.ps1", symbol, note }), - solution: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "Delvehold.sln", symbol, note }), - buildProps: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "Directory.Build.props", symbol, note }), - buildTargets: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "Directory.Build.targets", symbol, note }), - clientProject: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "client/Delvehold.Client.csproj", symbol, note }), - protocolProject: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "src/Delvehold.Protocol/Delvehold.Protocol.csproj", symbol, note }), - hostProject: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "src/Delvehold.WorldHost/Delvehold.WorldHost.csproj", symbol, note }), - graph: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "modeling/control-flow/graph.ts", symbol, note }), - graphVerify: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "modeling/control-flow/verify.ts", symbol, note }), - norn: (symbol: string, note?: string): EpiphanyCodeRef => ({ path: "@norn/web/norn-viewer/src/lib/NornViewer.tsx", symbol, note }), -}; - -const architectureNodes: OwnedGraphNode[] = [ - { id: "arch-godot-project", title: "Godot application", purpose: "Own the one DELVE/HOLD presentation process and scene lifetime.", mechanism: "Godot loads Main.tscn and attaches Main.cs to the root Control.", status: "verified", owner: "godot-client", runtime: "godot", code_refs: [refs.project("run/main_scene"), refs.scene("script = ExtResource")] }, - { id: "arch-main-control", title: "Main projection controller", purpose: "Build the current interface, select DELVE or HOLD presentation, and coordinate world entry.", mechanism: "One Godot Control owns local mode widgets and one application-lifetime CultMesh client.", status: "verified", owner: "godot-client", runtime: "godot", code_refs: [refs.client("public partial class Main")] }, - { id: "arch-cultmesh-client", title: "CultMesh client session owner", purpose: "Discover the stable world authority and reuse its typed operation session.", mechanism: "CultMeshClient owns discovery, transport connectors, reconnection, correlation, and response decoding.", status: "verified", owner: "cultlib", runtime: "external-library", code_refs: [refs.meshClient("public sealed class CultMeshClient")] }, - { id: "arch-world-contract", title: "World-entry wire contract", purpose: "Name the Verse, authority runtime, operation, and typed intent/receipt schemas without owning world state.", mechanism: "Protocol DTOs cross CultMesh; they contain no persistence or simulation authority.", status: "verified", owner: "protocol", runtime: "shared", code_refs: [refs.protocol("public static class DelveholdWorldContract"), refs.protocol("public sealed class WorldEnterIntent"), refs.protocol("public sealed class WorldEnterReceipt")] }, - { id: "arch-world-host", title: "Central world host", purpose: "Own the canonical Delvehold world process, admission boundary, and local persistence lifetime.", mechanism: "A single C# process composes CultMesh storage, TCP ingress, discovery, identity, and typed operations.", status: "verified", owner: "world-host", runtime: "world-host", code_refs: [refs.host("var settings = HostSettings.Parse(args)")] }, - { id: "arch-cultmesh-ingress", title: "CultMesh ingress", purpose: "Bind loopback transport, stable authority identity, Verse catalog, and typed operation dispatch.", mechanism: "TCP schema server carries discovery, session identity, and operation envelopes without owning domain admission.", status: "verified", owner: "cultlib", runtime: "world-host", code_refs: [refs.host("TcpFramedCultNetSchemaServer"), refs.host("CultMeshSessionIdentityServer"), refs.host("CultNetOperationServer")] }, - { id: "arch-entry-admission", title: "World-entry admission", purpose: "Validate projection, serialize idempotency decisions, and author accepted or rejected operation results.", mechanism: "One semaphore protects receipt lookup and creation; the host admits only DELVE or HOLD.", status: "verified", owner: "world-host", runtime: "world-host", code_refs: [refs.host("operationGate"), refs.host("invalid-world-entry"), refs.host("idempotency-key-collision")] }, - { id: "arch-world-state", title: "Canonical first-world state", purpose: "Own the first world identity, revision, and initial fixture identifiers.", mechanism: "WorldHost alone defines and seeds DelveholdWorldState when the canonical record is absent.", status: "verified", owner: "world-host", runtime: "world-host", code_refs: [refs.world("public sealed class DelveholdWorldState"), refs.world("FirstWorld()")] }, - { id: "arch-cultcache", title: "CultCache persistence", purpose: "Persist canonical world state and entry receipts across host lifetimes.", mechanism: "CultMeshNode exposes the typed database/cache; explicit flushes make admitted records durable.", status: "verified", owner: "cultlib", runtime: "world-host", code_refs: [refs.cache("CreateNodeAsync"), refs.host("node.Database.PutAsync"), refs.host("node.FlushAsync")] }, - { id: "arch-runtime-verifier", title: "Verification harness", purpose: "Falsify source-map integrity, connection, rejection, forbidden-writer, and restart-persistence claims.", mechanism: "A bounded PowerShell harness validates the typed map, builds three projects, and drives headless Godot against disposable host state.", status: "verified", owner: "verification", runtime: "tooling", code_refs: [refs.verify("control-flow.ps1"), refs.verify("Invoke-GodotSmoke"), refs.verify("forbiddenClientWriters"), refs.controlFlow("run build"), refs.graphVerify("DELVEHOLD_CONTROL_FLOW_INVALID")] }, - { id: "arch-developer-tooling", title: "Build and launch tooling", purpose: "Give Rider, command-line verification, and the Godot editor one admitted project graph and dependency path.", mechanism: "The conventional solution selects three projects; shared MSBuild files pin CultLib; bounded launchers start the editor and focused checks.", status: "verified", owner: "developer-tooling", runtime: "tooling", code_refs: [refs.solution("Delvehold.Client"), refs.buildProps("CultLibRevision"), refs.buildTargets("VerifyCultLibRevision"), refs.openGodot("Start-Process")] }, - { id: "arch-control-flow-map", title: "Typed control-flow map", purpose: "Publish the admitted, source-addressed semantic graph for review and rendering.", mechanism: "TypeScript narrows owners and edge kinds while carrying source witnesses and the admitted executable-source inventory.", status: "active", owner: "modeling", runtime: "tooling", code_refs: [refs.graph("export const controlFlowMap"), refs.graph("sourceInventory")] }, - { id: "arch-norn-viewer", title: "Norn inspection view", purpose: "Render authority and execution graphs for human inspection without gaining command authority.", mechanism: "The React NornViewer consumes the typed projection and owns layout, motion, selection, and detail presentation only.", status: "verified", owner: "norn", runtime: "tooling", code_refs: [refs.norn("export function NornViewer"), refs.graph("nornState")] }, -]; - -const architectureEdges: TypedControlEdge[] = [ - { id: "arch-edge-project-main", source_id: "arch-godot-project", target_id: "arch-main-control", kind: "constructs", label: "root scene", code_refs: [refs.scene("script = ExtResource")] }, - { id: "arch-edge-main-client", source_id: "arch-main-control", target_id: "arch-cultmesh-client", kind: "constructs", label: "application lifetime", code_refs: [refs.client("new CultMeshClient")] }, - { id: "arch-edge-main-contract", source_id: "arch-main-control", target_id: "arch-world-contract", kind: "sends-intent", label: "typed entry", code_refs: [refs.client("InvokeAsync")] }, - { id: "arch-edge-client-ingress", source_id: "arch-cultmesh-client", target_id: "arch-cultmesh-ingress", kind: "opens-session", label: "CultNet TCP", code_refs: [refs.meshClient("ConnectAsync"), refs.tcp("TcpFramedCultNetSchemaServer")] }, - { id: "arch-edge-ingress-admission", source_id: "arch-cultmesh-ingress", target_id: "arch-entry-admission", kind: "dispatches", label: "delvehold.world/enter", code_refs: [refs.host("Register")] }, - { id: "arch-edge-admission-world", source_id: "arch-entry-admission", target_id: "arch-world-state", kind: "reads", label: "world identity/revision", code_refs: [refs.host("node.Cache.Get")] }, - { id: "arch-edge-admission-cache", source_id: "arch-entry-admission", target_id: "arch-cultcache", kind: "writes", label: "accepted receipts only", code_refs: [refs.host("node.Database.PutAsync(receiptKey, receipt)")] }, - { id: "arch-edge-host-ingress", source_id: "arch-world-host", target_id: "arch-cultmesh-ingress", kind: "constructs", label: "one host lifetime", code_refs: [refs.host("using var server") ] }, - { id: "arch-edge-host-world", source_id: "arch-world-host", target_id: "arch-world-state", kind: "constructs", label: "seed if absent", code_refs: [refs.host("DelveholdWorldState.FirstWorld()")] }, - { id: "arch-edge-host-cache", source_id: "arch-world-host", target_id: "arch-cultcache", kind: "constructs", label: "typed store", code_refs: [refs.host("CultMesh.CreateNodeAsync")] }, - { id: "arch-edge-verifier-app", source_id: "arch-runtime-verifier", target_id: "arch-godot-project", kind: "verifies", label: "headless path", code_refs: [refs.verify("$godot --headless")] }, - { id: "arch-edge-verifier-host", source_id: "arch-runtime-verifier", target_id: "arch-world-host", kind: "verifies", label: "restart and replay", code_refs: [refs.verify("Start-WorldHost 'restart'")] }, - { id: "arch-edge-tooling-client", source_id: "arch-developer-tooling", target_id: "arch-godot-project", kind: "constructs", label: "Godot build and editor", code_refs: [refs.clientProject("Godot.NET.Sdk/4.7.2"), refs.openGodot("--editor")] }, - { id: "arch-edge-tooling-host", source_id: "arch-developer-tooling", target_id: "arch-world-host", kind: "constructs", label: "focused .NET build", code_refs: [refs.hostProject("Delvehold.Protocol"), refs.solution("Delvehold.WorldHost")] }, - { id: "arch-edge-tooling-contract", source_id: "arch-developer-tooling", target_id: "arch-world-contract", kind: "constructs", label: "shared protocol build", code_refs: [refs.protocolProject("GameCult.Caching"), refs.solution("Delvehold.Protocol")] }, - { id: "arch-edge-verifier-map", source_id: "arch-runtime-verifier", target_id: "arch-control-flow-map", kind: "verifies", label: "typed graph and source-inventory integrity", code_refs: [refs.verify("control-flow.ps1') validate"), refs.graphVerify("normalizedSha256")] }, - { id: "arch-edge-norn-map", source_id: "arch-norn-viewer", target_id: "arch-control-flow-map", kind: "renders", label: "non-mutating projection", code_refs: [refs.graph("nornState")] }, -]; - -const dataflowNodes: OwnedGraphNode[] = [ - { id: "flow-client-start", title: "Godot loads Main", purpose: "Instantiate the one client root and begin its scene lifetime.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.scene("[node name=\"Delvehold\"")] }, - { id: "flow-ready", title: "Ready", purpose: "Build local presentation and begin asynchronous world entry.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("public override void _Ready()")] }, - { id: "flow-build-ui", title: "Build interface", purpose: "Construct title, mode controls, and connection status without touching world truth.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("private void BuildInterface()")] }, - { id: "flow-mode-select", title: "Select local projection", purpose: "Switch visible DELVE/HOLD mode labels and buttons only.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("private void SelectProjection") ] }, - { id: "flow-enter", title: "Begin world entry", purpose: "Parse launch arguments, construct the application client, and invoke typed entry.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("private async Task EnterWorldAsync()")] }, - { id: "flow-discovery", title: "Discover authority", purpose: "Resolve the stable Verse and world-host runtime to the current physical route.", owner: "cultlib", runtime: "external-library", status: "verified", code_refs: [refs.meshClient("RendezvousEndpoints"), refs.meshClient("ConnectOnlineAsync")] }, - { id: "flow-catalog-response", title: "Return Verse catalog", purpose: "Publish the host route and supported document protocol to discovery callers.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("CreateCatalogResponse")] }, - { id: "flow-session-open", title: "Open identity-bound session", purpose: "Establish the reusable CultMesh documents session to the world host.", owner: "cultlib", runtime: "shared", status: "verified", code_refs: [refs.meshClient("ConnectAsync(target, CultMeshProtocols.Documents") ] }, - { id: "flow-send-entry", title: "Send entry intent", purpose: "Correlate one typed WorldEnterIntent under the caller idempotency key.", owner: "cultlib", runtime: "godot", status: "verified", code_refs: [refs.client("InvokeAsync"), refs.meshClient("CultNetOperationRequestMessage")] }, - { id: "flow-host-start", title: "Start world host", purpose: "Parse state/port settings and assemble the authoritative process.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("HostSettings.Parse(args)")] }, - { id: "flow-open-state", title: "Open typed state", purpose: "Create the CultMesh node over the configured CultCache path and registries.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("CreateCultCacheDocumentRegistry"), refs.host("CultMesh.CreateNodeAsync")] }, - { id: "flow-seed-world", title: "Seed world if absent", purpose: "Create the explicit first-world record once, then flush it.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("if (node.Cache.Get(worldKey) is null)"), refs.world("FirstWorld()")] }, - { id: "flow-bind-ingress", title: "Bind CultMesh ingress", purpose: "Start TCP, identity, catalog, and operation handlers before readiness.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("DELVEHOLD_HOST_READY")] }, - { id: "flow-dispatch-entry", title: "Dispatch entry operation", purpose: "Decode the typed payload and route it to host-owned admission.", owner: "cultlib", runtime: "world-host", status: "verified", code_refs: [refs.operationServer("Register"), refs.host("Register")] }, - { id: "flow-acquire-gate", title: "Serialize admission", purpose: "Prevent concurrent receipt lookup and creation from splitting idempotency authority.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("await operationGate.WaitAsync()")] }, - { id: "flow-validate-entry", title: "Validate entry", purpose: "Normalize the caller identity and admit only DELVE or HOLD projections.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("projection != \"delve\" && projection != \"hold\"")] }, - { id: "flow-invalid-reject", title: "Reject invalid entry", purpose: "Return typed rejection without persisting a receipt.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("invalid-world-entry")] }, - { id: "flow-receipt-lookup", title: "Lookup receipt", purpose: "Read the canonical receipt record for this idempotency key.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("node.Cache.Get(receiptKey)")] }, - { id: "flow-replay-accept", title: "Accept exact replay", purpose: "Return the original receipt when caller and projection match.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("Accepted(existing)")] }, - { id: "flow-collision-reject", title: "Reject key collision", purpose: "Return rejection evidence without replacing the original receipt.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("idempotency-key-collision")] }, - { id: "flow-create-receipt", title: "Create entry receipt", purpose: "Author the first accepted receipt from host-owned world identity and revision.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("var receipt = new WorldEnterReceipt")] }, - { id: "flow-persist-receipt", title: "Persist and flush receipt", purpose: "Commit the accepted receipt before reporting acceptance.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("node.Database.PutAsync(receiptKey, receipt)"), refs.host("await node.FlushAsync()")] }, - { id: "flow-return-response", title: "Return operation response", purpose: "Carry provider-authored status and typed receipt back over the reusable session.", owner: "cultlib", runtime: "shared", status: "verified", code_refs: [refs.operationServer("CultNetOperationReply"), refs.meshClient("CultMeshOperationResult")] }, - { id: "flow-show-result", title: "Show result", purpose: "Project the expected provider status into UI text or smoke exit code.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("private void ShowResult")] }, - { id: "flow-client-error", title: "Project client failure", purpose: "Convert discovery, session, or invocation exceptions into a failed local result without inventing world state.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("catch (Exception error)"), refs.client("error.Message, false")] }, - { id: "flow-client-exit", title: "Dispose client", purpose: "Release the application-lifetime CultMesh client with the scene.", owner: "godot-client", runtime: "godot", status: "verified", code_refs: [refs.client("public override void _ExitTree()")] }, - { id: "flow-host-wait", title: "Wait for stop or failure", purpose: "Keep authority alive until Ctrl+C or ingress failure terminates the process.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("Task.WhenAny(stopped.Task, server.BackgroundFailure)")] }, - { id: "flow-host-cancel", title: "Handle operator cancellation", purpose: "Convert Ctrl+C into orderly completion of the host lifetime.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("Console.CancelKeyPress"), refs.host("stopped.TrySetResult()")] }, - { id: "flow-host-failure", title: "Propagate ingress failure", purpose: "Terminate the host by throwing the captured background listener failure.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("if (server.BackgroundFailure.IsCompleted)"), refs.host("throw await server.BackgroundFailure")] }, - { id: "flow-host-terminated", title: "Host process terminates", purpose: "Dispose host-owned resources after orderly cancellation or propagate a fatal ingress error to the process boundary.", owner: "world-host", runtime: "world-host", status: "verified", code_refs: [refs.host("using var node"), refs.host("throw await server.BackgroundFailure")] }, - { id: "flow-smoke", title: "Runtime smoke sequence", purpose: "Drive accepted entry, collision rejection, invalid rejection, restart, and exact receipt replay.", owner: "verification", runtime: "tooling", status: "verified", code_refs: [refs.verify("Invoke-GodotSmoke 'first'"), refs.verify("Invoke-GodotSmoke 'collision'"), refs.verify("Invoke-GodotSmoke 'restart'")] }, - { id: "flow-map-validation", title: "Validate typed map", purpose: "Reject graph, witness, dependency-pin, inventory, or source-digest drift before compiling the runtime.", owner: "verification", runtime: "tooling", status: "verified", code_refs: [refs.verify("control-flow.ps1') validate"), refs.graphVerify("DELVEHOLD_CONTROL_FLOW_INVALID")] }, - { id: "flow-focused-build", title: "Build focused solution", purpose: "Compile the Godot client, protocol, and world host under the admitted CultLib revision.", owner: "developer-tooling", runtime: "tooling", status: "verified", code_refs: [refs.verify("dotnet build"), refs.solution("Delvehold.Client"), refs.buildProps("CultLibRoot"), refs.buildTargets("CultLib must be revision")] }, - { id: "flow-cultlib-admission", title: "Admit CultLib dependency", purpose: "Require the exact CultLib revision and a clean worktree before project references resolve.", owner: "developer-tooling", runtime: "tooling", status: "verified", code_refs: [refs.buildTargets("VerifyCultLibRevision"), refs.buildProps("CultLibRevision")] }, - { id: "flow-revision-reject", title: "Reject CultLib revision", purpose: "Fail the build when the selected CultLib checkout is not the admitted revision.", owner: "developer-tooling", runtime: "tooling", status: "verified", code_refs: [refs.buildTargets("CultLib must be revision")] }, - { id: "flow-dirty-reject", title: "Reject dirty CultLib", purpose: "Fail the build when the selected CultLib worktree contains unadmitted changes.", owner: "developer-tooling", runtime: "tooling", status: "verified", code_refs: [refs.buildTargets("CultLibRoot must be clean")] }, - { id: "flow-open-editor", title: "Open Godot editor", purpose: "Launch the admitted Godot executable against the client project with the selected CultLib worktree.", owner: "developer-tooling", runtime: "tooling", status: "verified", code_refs: [refs.openGodot("Godot_v4.7.2-stable_mono_win64.exe"), refs.openGodot("Start-Process")] }, -]; - -const e = (id: string, source_id: string, target_id: string, kind: ControlEdgeKind, code_refs: EpiphanyCodeRef[], label?: string): TypedControlEdge => ({ id, source_id, target_id, kind, code_refs, label }); - -const dataflowEdges: TypedControlEdge[] = [ - e("flow-edge-start-ready", "flow-client-start", "flow-ready", "calls", [refs.client("_Ready")]), - e("flow-edge-ready-ui", "flow-ready", "flow-build-ui", "calls", [refs.client("BuildInterface();")]), - e("flow-edge-ready-enter", "flow-ready", "flow-enter", "calls", [refs.client("_ = EnterWorldAsync();")]), - e("flow-edge-ui-mode", "flow-build-ui", "flow-mode-select", "signals", [refs.client("_delve.Pressed"), refs.client("_hold.Pressed")]), - e("flow-edge-enter-mode", "flow-enter", "flow-mode-select", "calls", [refs.client("SelectProjection(options.Projection)")]), - e("flow-edge-enter-discovery", "flow-enter", "flow-discovery", "discovers", [refs.client("RendezvousEndpoints")]), - e("flow-edge-discovery-catalog", "flow-discovery", "flow-catalog-response", "calls", [refs.host("CultMeshVerseCatalogRequestMessage")]), - e("flow-edge-catalog-session", "flow-catalog-response", "flow-session-open", "opens-session", [refs.host("authorityRoutes"), refs.meshClient("ConnectAsync")]), - e("flow-edge-session-send", "flow-session-open", "flow-send-entry", "sends-intent", [refs.meshClient("session.SendCultNet(message)")]), - e("flow-edge-host-open", "flow-host-start", "flow-open-state", "calls", [refs.host("CultMesh.CreateNodeAsync")]), - e("flow-edge-open-seed", "flow-open-state", "flow-seed-world", "branches", [refs.host("is null")]), - e("flow-edge-seed-bind", "flow-seed-world", "flow-bind-ingress", "calls", [refs.host("using var server")]), - e("flow-edge-bind-catalog", "flow-bind-ingress", "flow-catalog-response", "calls", [refs.host("CreateCatalogResponse")]), - e("flow-edge-send-dispatch", "flow-send-entry", "flow-dispatch-entry", "dispatches", [refs.operationServer("HandleAsync")]), - e("flow-edge-dispatch-gate", "flow-dispatch-entry", "flow-acquire-gate", "calls", [refs.host("operationGate.WaitAsync")]), - e("flow-edge-gate-validate", "flow-acquire-gate", "flow-validate-entry", "validates", [refs.host("sourceRuntimeId"), refs.host("projection")]), - e("flow-edge-validate-invalid", "flow-validate-entry", "flow-invalid-reject", "rejects", [refs.host("invalid-world-entry")], "invalid caller or projection"), - e("flow-edge-validate-lookup", "flow-validate-entry", "flow-receipt-lookup", "reads", [refs.host("receiptKey")], "valid entry"), - e("flow-edge-lookup-replay", "flow-receipt-lookup", "flow-replay-accept", "replays", [refs.host("Accepted(existing)")], "exact match"), - e("flow-edge-lookup-collision", "flow-receipt-lookup", "flow-collision-reject", "rejects", [refs.host("idempotency-key-collision")], "same key, different input"), - e("flow-edge-lookup-create", "flow-receipt-lookup", "flow-create-receipt", "branches", [refs.host("var receipt = new WorldEnterReceipt")], "missing receipt"), - e("flow-edge-create-persist", "flow-create-receipt", "flow-persist-receipt", "writes", [refs.host("PutAsync(receiptKey, receipt)")]), - e("flow-edge-persist-response", "flow-persist-receipt", "flow-return-response", "returns-receipt", [refs.host("Accepted(receipt)")]), - e("flow-edge-replay-response", "flow-replay-accept", "flow-return-response", "returns-receipt", [refs.host("Accepted(existing)")]), - e("flow-edge-collision-response", "flow-collision-reject", "flow-return-response", "returns-receipt", [refs.host("Rejected(")]), - e("flow-edge-invalid-response", "flow-invalid-reject", "flow-return-response", "returns-receipt", [refs.host("invalid-world-entry")]), - e("flow-edge-response-show", "flow-return-response", "flow-show-result", "calls", [refs.client("CallDeferred(MethodName.ShowResult")]), - e("flow-edge-enter-error", "flow-enter", "flow-client-error", "branches", [refs.client("catch (Exception error)")], "discovery, session, or invocation failure"), - e("flow-edge-error-show", "flow-client-error", "flow-show-result", "calls", [refs.client("error.Message, false")]), - e("flow-edge-show-exit", "flow-show-result", "flow-client-exit", "terminates", [refs.client("GetTree().Quit")], "headless smoke only"), - e("flow-edge-scene-exit", "flow-client-start", "flow-client-exit", "terminates", [refs.client("_ExitTree")], "ordinary scene teardown"), - e("flow-edge-bind-wait", "flow-bind-ingress", "flow-host-wait", "waits-for", [refs.host("Task.WhenAny")]), - e("flow-edge-wait-cancel", "flow-host-wait", "flow-host-cancel", "branches", [refs.host("stopped.Task")], "Ctrl+C"), - e("flow-edge-wait-failure", "flow-host-wait", "flow-host-failure", "branches", [refs.host("server.BackgroundFailure")], "listener failure"), - e("flow-edge-cancel-stop", "flow-host-cancel", "flow-host-terminated", "terminates", [refs.host("stopped.TrySetResult()")], "complete wait and dispose"), - e("flow-edge-failure-stop", "flow-host-failure", "flow-host-terminated", "terminates", [refs.host("throw await server.BackgroundFailure")], "fatal process exit"), - e("flow-edge-smoke-start", "flow-smoke", "flow-host-start", "verifies", [refs.verify("Start-WorldHost")]), - e("flow-edge-smoke-map", "flow-smoke", "flow-map-validation", "calls", [refs.verify("control-flow.ps1') validate")]), - e("flow-edge-map-build", "flow-map-validation", "flow-focused-build", "calls", [refs.verify("dotnet build")]), - e("flow-edge-smoke-client", "flow-smoke", "flow-client-start", "verifies", [refs.verify("$godot --headless")]), - e("flow-edge-smoke-replay", "flow-smoke", "flow-replay-accept", "verifies", [refs.verify("$firstReceipt -ne $restartReceipt")]), - e("flow-edge-smoke-invalid", "flow-smoke", "flow-invalid-reject", "verifies", [refs.verify("invalid-projection")]), - e("flow-edge-smoke-collision", "flow-smoke", "flow-collision-reject", "verifies", [refs.verify("collision")]), - e("flow-edge-build-admission", "flow-focused-build", "flow-cultlib-admission", "validates", [refs.buildTargets("BeforeTargets=\"ResolveProjectReferences\"")]), - e("flow-edge-admission-revision", "flow-cultlib-admission", "flow-revision-reject", "rejects", [refs.buildTargets("found $([System.String]")], "wrong revision"), - e("flow-edge-admission-dirty", "flow-cultlib-admission", "flow-dirty-reject", "rejects", [refs.buildTargets("CultLibStatus")], "dirty worktree"), - e("flow-edge-admission-client", "flow-cultlib-admission", "flow-client-start", "constructs", [refs.clientProject("Godot.NET.Sdk/4.7.2")], "admitted dependency"), - e("flow-edge-admission-host", "flow-cultlib-admission", "flow-host-start", "constructs", [refs.hostProject("OutputType>Exe")], "admitted dependency"), - e("flow-edge-editor-client", "flow-open-editor", "flow-client-start", "constructs", [refs.openGodot("--editor")], "interactive editor projection"), -]; - -const architectureByFlowId: Record = { - "flow-client-start": "arch-godot-project", - "flow-ready": "arch-main-control", - "flow-build-ui": "arch-main-control", - "flow-mode-select": "arch-main-control", - "flow-enter": "arch-main-control", - "flow-discovery": "arch-cultmesh-client", - "flow-catalog-response": "arch-cultmesh-ingress", - "flow-session-open": "arch-cultmesh-client", - "flow-send-entry": "arch-world-contract", - "flow-host-start": "arch-world-host", - "flow-open-state": "arch-cultcache", - "flow-seed-world": "arch-world-state", - "flow-bind-ingress": "arch-cultmesh-ingress", - "flow-dispatch-entry": "arch-cultmesh-ingress", - "flow-acquire-gate": "arch-entry-admission", - "flow-validate-entry": "arch-entry-admission", - "flow-invalid-reject": "arch-entry-admission", - "flow-receipt-lookup": "arch-cultcache", - "flow-replay-accept": "arch-entry-admission", - "flow-collision-reject": "arch-entry-admission", - "flow-create-receipt": "arch-entry-admission", - "flow-persist-receipt": "arch-cultcache", - "flow-return-response": "arch-cultmesh-ingress", - "flow-show-result": "arch-main-control", - "flow-client-error": "arch-main-control", - "flow-client-exit": "arch-main-control", - "flow-host-wait": "arch-world-host", - "flow-host-cancel": "arch-world-host", - "flow-host-failure": "arch-world-host", - "flow-host-terminated": "arch-world-host", - "flow-smoke": "arch-runtime-verifier", - "flow-map-validation": "arch-runtime-verifier", - "flow-focused-build": "arch-developer-tooling", - "flow-cultlib-admission": "arch-developer-tooling", - "flow-revision-reject": "arch-developer-tooling", - "flow-dirty-reject": "arch-developer-tooling", - "flow-open-editor": "arch-developer-tooling", -}; - -const links: NornGraphLink[] = dataflowNodes.map((node) => ({ - dataflow_node_id: node.id, - architecture_node_id: architectureByFlowId[node.id] ?? `unmapped:${node.id}`, - relationship: "owned-by", - code_refs: node.code_refs, -})); - -export const sourceInventory: SourceSurface[] = [ - { path: "Delvehold.sln", normalizedSha256: "19ce0e87a3b8c7d34b957db8fa1c50f25230a849c39de6872afb81746cb1ca65", responsibility: "Rider and focused build project membership" }, - { path: "Directory.Build.props", normalizedSha256: "898df5c1ff8b0bf7110ac2e27aa585d8d241af85b718e53aaefecd9c1fc8098f", responsibility: "shared target and dependency admission defaults" }, - { path: "Directory.Build.targets", normalizedSha256: "fb703044f822d690ff3afdfa82219d43951f740834f533b7988c18e446da41a6", responsibility: "CultLib revision and cleanliness gate" }, - { path: "client/Delvehold.Client.csproj", normalizedSha256: "0a9a92a87761f2a2525a19ac73bad22c621e2640ab91127c17660e111497cf26", responsibility: "Godot client build dependencies" }, - { path: "client/Main.cs", normalizedSha256: "ea636fd09961d93991e28653f237bc1a3f52661101a4311d697b4865ba4ff23f", responsibility: "Godot client control flow" }, - { path: "client/Main.tscn", normalizedSha256: "a1c1d81dfe77e1921a0f42f0c17d7205ca5d0a0e9ec9373382a568a1885c2f55", responsibility: "Godot root scene construction" }, - { path: "client/project.godot", normalizedSha256: "41021e8586c6d4fb0a5412b4cfce3830fb24161605a95c2c31d3db4d663fd1c8", responsibility: "Godot application entry configuration" }, - { path: "scripts/control-flow.ps1", normalizedSha256: "a986cce42706574f97eed37abbc9af073d01d05333e47013e53f1c9c0f8c21e7", responsibility: "typed map validation and Norn viewer lifecycle" }, - { path: "scripts/open-godot.ps1", normalizedSha256: "de789de343d03451d4b203ce9e863cc92754e59ba6251d6581e6f2aebf81b559", responsibility: "interactive Godot editor launch" }, - { path: "scripts/verify-runtime.ps1", normalizedSha256: "5456cef5679644fd2d553a954a04a224a211d6d330f1d2533a2052699a3f7976", responsibility: "executable runtime acceptance flow" }, - { path: "src/Delvehold.Protocol/Delvehold.Protocol.csproj", normalizedSha256: "d7e665c26dedd151551db6285a753034a12bfa616a20420eb1e42225eabe74b6", responsibility: "wire contract dependencies" }, - { path: "src/Delvehold.Protocol/WorldContracts.cs", normalizedSha256: "dfe85fc3ad5ec078a2b8e490284398160fcde46a6f7728d3cbae8b813877cb13", responsibility: "typed world-entry wire contract" }, - { path: "src/Delvehold.WorldHost/Delvehold.WorldHost.csproj", normalizedSha256: "6301967b27a4319dff8631e093a3ed965f64b099222061873949928fe8504693", responsibility: "world-host build dependencies" }, - { path: "src/Delvehold.WorldHost/Program.cs", normalizedSha256: "73933d99fbdad59aa68ab1115426cb63165017201be7206c991c7b3cbf698184", responsibility: "world-host composition and admission control flow" }, - { path: "src/Delvehold.WorldHost/WorldState.cs", normalizedSha256: "fe471c7f22161e96b5a9cf0cd04e87c8f85017bdbcd3836ddf496db1a8dea120", responsibility: "canonical first-world schema and seed" }, -]; - -export const controlFlowMap = { - schema: "delvehold.control_flow_map.v0" as const, - nornRevision: "fa4a46bb13333eb703e97ccea9edc1b70ee109b7" as const, - cultLibRevision: "334e60f1928b4212a29dd8b0d19b2c099fe6365e" as const, - architecture: { nodes: architectureNodes, edges: architectureEdges }, - dataflow: { nodes: dataflowNodes, edges: dataflowEdges }, - links, - sourceInventory, -}; - -export const nornState: NornGraphsState = { - architecture: controlFlowMap.architecture, - dataflow: controlFlowMap.dataflow, - links: controlFlowMap.links, -}; diff --git a/modeling/control-flow/index.html b/modeling/control-flow/index.html deleted file mode 100644 index f66ce6f..0000000 --- a/modeling/control-flow/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - DELVE/HOLD Control Flow - - -
- - - diff --git a/modeling/control-flow/main.tsx b/modeling/control-flow/main.tsx deleted file mode 100644 index 4f004be..0000000 --- a/modeling/control-flow/main.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { StrictMode } from "react"; -import { createRoot } from "react-dom/client"; -import { NornViewer } from "@gamecult/norn-viewer"; -import { nornState } from "./graph"; -import "./style.css"; - -function App() { - return ( - - ); -} - -createRoot(document.getElementById("root")!).render( - - - , -); diff --git a/modeling/control-flow/package-lock.json b/modeling/control-flow/package-lock.json deleted file mode 100644 index 11f6bb1..0000000 --- a/modeling/control-flow/package-lock.json +++ /dev/null @@ -1,1944 +0,0 @@ -{ - "name": "@gamecult/delvehold-control-flow", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@gamecult/delvehold-control-flow", - "version": "0.1.0", - "dependencies": { - "@gamecult/norn-viewer": "file:../../../Norn/web/norn-viewer", - "react": "^19.2.0", - "react-dom": "^19.2.0" - }, - "devDependencies": { - "@types/node": "^24.10.1", - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.2", - "tsx": "^4.21.0", - "typescript": "^5.9.3", - "vite": "^7.2.6" - } - }, - "../../../Norn/web/norn-viewer": { - "name": "@gamecult/norn-viewer", - "version": "0.1.0", - "devDependencies": { - "@types/react": "^19.2.2", - "@types/react-dom": "^19.2.2", - "@vitejs/plugin-react": "^5.1.1", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "tsup": "^8.5.0", - "typescript": "^5.9.3", - "vite": "^7.2.4" - }, - "peerDependencies": { - "react": "^19.0.0", - "react-dom": "^19.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", - "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helpers": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", - "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.8", - "@babel/types": "^7.29.8", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", - "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", - "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", - "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", - "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", - "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", - "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", - "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", - "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", - "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", - "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", - "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.8", - "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.8", - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.8", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", - "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", - "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", - "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", - "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", - "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", - "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", - "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", - "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", - "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", - "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", - "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", - "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", - "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", - "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", - "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", - "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", - "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", - "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", - "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", - "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", - "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", - "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", - "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", - "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", - "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", - "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@gamecult/norn-viewer": { - "resolved": "../../../Norn/web/norn-viewer", - "link": true - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/lzma-linux-x64-gnu": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", - "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^22.20 || ^24.12 || >=25" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", - "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.0.tgz", - "integrity": "sha512-70TeIFezKKy65LgAVyQh+w94/gjWhvPWaLaGGeMEgVrPkQhuj/M5bAYYZzIFUj9Y69oHyTm5Um/R6gcLh4A8JA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.0.tgz", - "integrity": "sha512-YC86tYIHK6M1IV+wbzO+Bxk8RCBr6ZyWYgWxUCzaZD8mc8rrFoIJDNzDrkHBYRc/wKdrsIXmm6/F7NzrAO+OrA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.0.tgz", - "integrity": "sha512-oI+ECtUcli0y0fi4xpW82GdPIXdTkI8G8DSjG2LRuw09fPAGykaWYH/hXxiKuTxiAjiPSTIIuYUqof5Z2hShWw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.0.tgz", - "integrity": "sha512-NwV+1s7TiKrMe4owHyKB/dTLD7ZJD0YEBEhIz+hvav1Cu1GReJjF+rsdNwjzENQeIAbE/CoNiaAc5Vz2h5DPAA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.0.tgz", - "integrity": "sha512-tWtHBTu5gOPK4u4Urtk4qAHW3zZ9rQAmbssO8gp7ELvGTGI3aCiq6NqyTQ0PCIg7KbHJF2UkGDDs77YZGxfjCA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.0.tgz", - "integrity": "sha512-2qPoJiwTvtHQ27NnYvTnsgk8laXWYuVmNESG8WFZBcEPKLfZ3I27qBJarjVRQtwGeYyRfq5ZowHXih9lm2BItw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.0.tgz", - "integrity": "sha512-FQwsTRvLNuHoTdICABJQfbPUSEueISGmnpT06tXTMpfprf5NiKLSXKA0A+w45wJnCmZAnzgqBwbt6ARFuyOi5w==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.0.tgz", - "integrity": "sha512-BBVTXziw8mY1a4ZbWME9tZyfzqXCDPqaC7Z3heQ29p5dkvXzwL0NwelO8zLa8c3RBKvl3YTuSnBgsBhYBtwjIw==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.0.tgz", - "integrity": "sha512-w2Iyy9+RqKwx3d9qWMKsJg0FfRBsY0/pXNv0mCQ3ueRvJI6+QAScfD4nrMlzFLs2HNVW6Ew+mtZfDl9b7Ew5/Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.0.tgz", - "integrity": "sha512-YK++KtrFRHYE0P6/RtYEAy9t8F37znP+K03RrIuLPYOL6SVlObRumf/0OE4V/h63xL9DwkWbNssZfmA9hawuDA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.0.tgz", - "integrity": "sha512-aBfOG6fP7YkkPmTqPwufRJeFyz7WPpECv9XNbnsk9+vg7rxdih0lbtEel7jcRng4LZrrmU3FfitCFyEj4BWDWg==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.0.tgz", - "integrity": "sha512-LGaHEOeHNAag9VuS1Crs5DFg4RrU9MPi2nVnNJk9DTePx/B6RRYKVmrIXt2h7YOJlwjaFJ6lwtFDliZxScTLrQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.0.tgz", - "integrity": "sha512-jClvk+J0FC3b7Udvegiw5/4hErbHtmsNsQgENnKXDWtNCJXsJYZH5WURvu7imDOO38xYml24eeh5x3A04ppwCw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.0.tgz", - "integrity": "sha512-0OJlaGK+8+B777Ql5okIpD7ua5Ro9+VB9Ve0OKa28OQJZ1RbuUBVNHK/e3pr4BROqsyPl1JrPO1ZxJseCNffcA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.0.tgz", - "integrity": "sha512-Ygsx+HoNH7afwi1bTIXbnTvVnsO+zurPLSYxybV1hHFVU72OWOCl6v05ql/z0hkpAPx+DK7Kn9Bi7MayCcjLTA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.0.tgz", - "integrity": "sha512-pDQxtMGb+OvG3fLwR2OkZlSd47hW+kWg4BYMG/++sR6RqorQccwPTDsxda5hPwiIeIErAnCF9ma3SAU06bdQtQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.0.tgz", - "integrity": "sha512-0BnUG9mS8I4SSHr3XsxVhuCMEiu+rX61xxZF5vujso4LaiAGFZFxvDjg6Xn6tLPNTUAfuCvQYas4LMQMVsKRSQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.0.tgz", - "integrity": "sha512-Adu/VttB1dpPNW+FEacrZ+xVm9tFty84+RrFzsqlFaPxoJB+9XXyDGtp5dCOoBwGBIEVH0To7lExFXEx0BIF4A==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.0.tgz", - "integrity": "sha512-NQ3bDvjUbFKmP23671xUlXtKmqVsUBd6M4PQCvbmNtOy06hnQIdKHy8oG/6S3R/S6He1JgPk6A5VT+prAJMYEw==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.0.tgz", - "integrity": "sha512-u2eDAl4+0aFvA13GxlGBtTI3SS3sdgwgtV0HyjZ0QaQVCgNE+jqNGey+GtxWiq+wxr/UycAx/OnfJzApCFamvA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.0.tgz", - "integrity": "sha512-XvRb5vfW3wAZQ+ZUG21AnHHDKtNcw99eigzEhjr//NZ3u7SoBaPP0seSc7FgP7p1epAEdAoZckMW9WY/+4w70w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.0.tgz", - "integrity": "sha512-iZPmniy4kNBf5yo2RezbkYNNK5HPbXE9+g+twnbqSng7dtLEJy1SKoxiE/ni4FDacjyuZpEeb9U054N4EoKHYw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.0.tgz", - "integrity": "sha512-mFBBd+LF37fnE8JnYUOH+imj0aPFPK30vpar4ehJkgnLj9sZn8ZxiRENmLtgIwxK7TC8klF6N57fxdNBwQoqOA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.0.tgz", - "integrity": "sha512-ujeqEY3B+zbGn3Z4Q03cUBG/LGWnBJncVT36WER31LcOsQk9+1dmINKKtvmmfChUvRbK1G0R8OhMWFgHgaZtAw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.0.tgz", - "integrity": "sha512-hncn90N4sOky0L2LKE5oESKLbxCPeVo4eLA2LSMoDzM+879ml4WSr+Rr4DWknNIVVvS1Hirkc9hx02W6YxS8rQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.13.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", - "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.18.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", - "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.5.tgz", - "integrity": "sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", - "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.29.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-rc.3", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.18.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.11.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.19.tgz", - "integrity": "sha512-Grytf1xOxOEMTGRwx6rLGKkTabd4vMg3VrKdj/7joCmV0qgh4QwMMO6xh34YEXQqirAuUdgQGa5orJQQ+69RBw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/browserslist": { - "version": "4.28.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", - "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.11.12", - "caniuse-lite": "^1.0.30001809", - "electron-to-chromium": "^1.5.402", - "node-releases": "^2.0.53", - "update-browserslist-db": "^1.3.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001810", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", - "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.415", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.415.tgz", - "integrity": "sha512-958V+Kbhtgz+SxXeEVKBjrlKRBIDAYvUJfwhjxMZ5S6ut9jAl7l9ZKBkBrvjyjZE36PabLUo2L8kEeV5O4vgJg==", - "dev": true, - "license": "ISC" - }, - "node_modules/esbuild": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", - "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.2", - "@esbuild/android-arm": "0.28.2", - "@esbuild/android-arm64": "0.28.2", - "@esbuild/android-x64": "0.28.2", - "@esbuild/darwin-arm64": "0.28.2", - "@esbuild/darwin-x64": "0.28.2", - "@esbuild/freebsd-arm64": "0.28.2", - "@esbuild/freebsd-x64": "0.28.2", - "@esbuild/linux-arm": "0.28.2", - "@esbuild/linux-arm64": "0.28.2", - "@esbuild/linux-ia32": "0.28.2", - "@esbuild/linux-loong64": "0.28.2", - "@esbuild/linux-mips64el": "0.28.2", - "@esbuild/linux-ppc64": "0.28.2", - "@esbuild/linux-riscv64": "0.28.2", - "@esbuild/linux-s390x": "0.28.2", - "@esbuild/linux-x64": "0.28.2", - "@esbuild/netbsd-arm64": "0.28.2", - "@esbuild/netbsd-x64": "0.28.2", - "@esbuild/openbsd-arm64": "0.28.2", - "@esbuild/openbsd-x64": "0.28.2", - "@esbuild/openharmony-arm64": "0.28.2", - "@esbuild/sunos-x64": "0.28.2", - "@esbuild/win32-arm64": "0.28.2", - "@esbuild/win32-ia32": "0.28.2", - "@esbuild/win32-x64": "0.28.2" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/node-releases": { - "version": "2.0.53", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", - "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.26", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", - "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.17", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/react": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", - "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", - "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.8" - } - }, - "node_modules/react-refresh": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.0.tgz", - "integrity": "sha512-T5vnZ2y4QqC3/4P+w2+JO+Q/OVdnPsv4XcSYJYMEn0R9/jjl5AgLwO9LAZMzP2lN71O6pypn91rB7lDstUkfrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.63.0", - "@rollup/rollup-android-arm64": "4.63.0", - "@rollup/rollup-darwin-arm64": "4.63.0", - "@rollup/rollup-darwin-x64": "4.63.0", - "@rollup/rollup-freebsd-arm64": "4.63.0", - "@rollup/rollup-freebsd-x64": "4.63.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.63.0", - "@rollup/rollup-linux-arm-musleabihf": "4.63.0", - "@rollup/rollup-linux-arm64-gnu": "4.63.0", - "@rollup/rollup-linux-arm64-musl": "4.63.0", - "@rollup/rollup-linux-loong64-gnu": "4.63.0", - "@rollup/rollup-linux-loong64-musl": "4.63.0", - "@rollup/rollup-linux-ppc64-gnu": "4.63.0", - "@rollup/rollup-linux-ppc64-musl": "4.63.0", - "@rollup/rollup-linux-riscv64-gnu": "4.63.0", - "@rollup/rollup-linux-riscv64-musl": "4.63.0", - "@rollup/rollup-linux-s390x-gnu": "4.63.0", - "@rollup/rollup-linux-x64-gnu": "4.63.0", - "@rollup/rollup-linux-x64-musl": "4.63.0", - "@rollup/rollup-openbsd-x64": "4.63.0", - "@rollup/rollup-openharmony-arm64": "4.63.0", - "@rollup/rollup-win32-arm64-msvc": "4.63.0", - "@rollup/rollup-win32-ia32-msvc": "4.63.0", - "@rollup/rollup-win32-x64-gnu": "4.63.0", - "@rollup/rollup-win32-x64-msvc": "4.63.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tsx": { - "version": "4.23.12", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.12.tgz", - "integrity": "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.28.0" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", - "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/vite": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", - "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.27.0 || ^0.28.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - } - } -} diff --git a/modeling/control-flow/package.json b/modeling/control-flow/package.json deleted file mode 100644 index ae11cb8..0000000 --- a/modeling/control-flow/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "@gamecult/delvehold-control-flow", - "private": true, - "version": "0.1.0", - "type": "module", - "scripts": { - "validate": "tsx verify.ts", - "build": "npm run validate && tsc --noEmit && vite build", - "dev": "vite" - }, - "dependencies": { - "@gamecult/norn-viewer": "file:../../../Norn/web/norn-viewer", - "react": "^19.2.0", - "react-dom": "^19.2.0" - }, - "devDependencies": { - "@types/node": "^24.10.1", - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.2", - "tsx": "^4.21.0", - "typescript": "^5.9.3", - "vite": "^7.2.6" - } -} diff --git a/modeling/control-flow/style.css b/modeling/control-flow/style.css deleted file mode 100644 index a4db184..0000000 --- a/modeling/control-flow/style.css +++ /dev/null @@ -1,18 +0,0 @@ -:root { - color-scheme: dark; - font-family: Inter, ui-sans-serif, system-ui, sans-serif; - background: #080706; -} - -html, -body, -#root { - width: 100%; - height: 100%; - margin: 0; - overflow: hidden; -} - -* { - box-sizing: border-box; -} diff --git a/modeling/control-flow/tsconfig.json b/modeling/control-flow/tsconfig.json deleted file mode 100644 index dc189e9..0000000 --- a/modeling/control-flow/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "useDefineForClassFields": true, - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "allowJs": false, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "ESNext", - "moduleResolution": "Bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx" - }, - "include": ["*.ts", "*.tsx"] -} diff --git a/modeling/control-flow/verify.ts b/modeling/control-flow/verify.ts deleted file mode 100644 index f468661..0000000 --- a/modeling/control-flow/verify.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { createHash } from "node:crypto"; -import { execFileSync } from "node:child_process"; -import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; -import { dirname, relative, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; -import { controlFlowMap } from "./graph"; - -const mapRoot = dirname(fileURLToPath(import.meta.url)); -const repoRoot = resolve(mapRoot, "../.."); -const failures: string[] = []; -const witnessedLocalPaths = new Set(); -const nornRoot = process.env.DELVEHOLD_NORN_ROOT ?? resolve(repoRoot, "../Norn"); -const cultLibRoot = process.env.DELVEHOLD_CULTLIB_ROOT ?? resolve(repoRoot, "../CultLib-aetheria-authority"); - -validateDependency("Norn", nornRoot, controlFlowMap.nornRevision); -validateDependency("CultLib", cultLibRoot, controlFlowMap.cultLibRevision); -const buildProps = readFileSync(resolve(repoRoot, "Directory.Build.props"), "utf8"); -if (!buildProps.includes(`${controlFlowMap.cultLibRevision}`)) { - failures.push("The control-flow CultLib pin disagrees with Directory.Build.props."); -} - -const architectureIds = validateGraph("architecture", controlFlowMap.architecture); -const dataflowIds = validateGraph("dataflow", controlFlowMap.dataflow); -const linkKeys = new Set(); -for (const link of controlFlowMap.links) { - const key = `${link.dataflow_node_id}::${link.architecture_node_id}`; - if (linkKeys.has(key)) failures.push(`Duplicate cross-link '${key}'.`); - linkKeys.add(key); - if (!dataflowIds.has(link.dataflow_node_id)) failures.push(`Cross-link '${key}' has no dataflow node.`); - if (!architectureIds.has(link.architecture_node_id)) failures.push(`Cross-link '${key}' has no architecture node.`); - validateRefs(`cross-link '${key}'`, link.code_refs ?? []); -} - -const expectedSources = collectExecutableSources(); -const inventoriedSources = new Set(controlFlowMap.sourceInventory.map((source) => normalize(source.path))); -for (const path of expectedSources) { - if (!inventoriedSources.has(path)) failures.push(`Executable source '${path}' is missing from sourceInventory.`); -} -for (const path of inventoriedSources) { - if (!expectedSources.has(path)) failures.push(`sourceInventory contains stale or out-of-scope path '${path}'.`); - if (!witnessedLocalPaths.has(path)) failures.push(`Inventoried source '${path}' is not cited by any graph node, edge, or cross-link.`); -} - -for (const source of controlFlowMap.sourceInventory) { - const absolute = resolve(repoRoot, source.path); - if (!existsSync(absolute)) { - failures.push(`Inventoried source '${source.path}' does not exist.`); - continue; - } - const actual = normalizedSha256(readFileSync(absolute, "utf8")); - if (actual !== source.normalizedSha256) { - failures.push(`Source drift '${source.path}': expected ${source.normalizedSha256}, actual ${actual}. Review the graph, then admit the new hash.`); - } -} - -if (failures.length > 0) { - console.error("DELVEHOLD_CONTROL_FLOW_INVALID"); - for (const failure of failures) console.error(`- ${failure}`); - process.exit(1); -} - -console.log(`DELVEHOLD_CONTROL_FLOW_VALID architecture=${architectureIds.size}/${controlFlowMap.architecture.edges.length} dataflow=${dataflowIds.size}/${controlFlowMap.dataflow.edges.length} links=${controlFlowMap.links.length} sources=${expectedSources.size}`); - -function validateGraph( - name: string, - graph: typeof controlFlowMap.architecture, -): Set { - const nodeIds = new Set(); - const edgeIds = new Set(); - for (const node of graph.nodes) { - if (!node.id.trim() || !node.title.trim() || !node.purpose.trim() || !node.owner) { - failures.push(`${name} node '${node.id || ""}' lacks required typed identity, title, purpose, or owner.`); - } - if (nodeIds.has(node.id)) failures.push(`Duplicate ${name} node '${node.id}'.`); - nodeIds.add(node.id); - validateRefs(`${name} node '${node.id}'`, node.code_refs); - } - for (const edge of graph.edges) { - if (edgeIds.has(edge.id)) failures.push(`Duplicate ${name} edge '${edge.id}'.`); - edgeIds.add(edge.id); - if (!nodeIds.has(edge.source_id)) failures.push(`${name} edge '${edge.id}' has missing source '${edge.source_id}'.`); - if (!nodeIds.has(edge.target_id)) failures.push(`${name} edge '${edge.id}' has missing target '${edge.target_id}'.`); - validateRefs(`${name} edge '${edge.id}'`, edge.code_refs); - } - return nodeIds; -} - -function validateRefs(scope: string, refs: readonly { path: string; symbol?: string | null }[]) { - if (refs.length === 0) failures.push(`${scope} has no source witness.`); - for (const ref of refs) { - const normalizedPath = normalize(ref.path); - if (!normalizedPath.startsWith("../") && !normalizedPath.startsWith("@")) witnessedLocalPaths.add(normalizedPath); - const absolute = resolveWitnessPath(normalizedPath); - if (!existsSync(absolute)) { - failures.push(`${scope} references missing source '${ref.path}'.`); - continue; - } - if (ref.symbol && !readFileSync(absolute, "utf8").includes(ref.symbol)) { - failures.push(`${scope} cannot find witness '${ref.symbol}' in '${ref.path}'.`); - } - } -} - -function collectExecutableSources(): Set { - const paths = new Set(); - collect(resolve(repoRoot, "client"), new Set([".cs", ".csproj", ".godot", ".tscn"]), paths); - collect(resolve(repoRoot, "src"), new Set([".cs", ".csproj"]), paths); - collectTopLevel(resolve(repoRoot, "scripts"), new Set([".ps1"]), paths); - collectTopLevel(repoRoot, new Set([".sln", ".props", ".targets"]), paths); - return paths; -} - -function collect(directory: string, extensions: Set, output: Set) { - for (const name of readdirSync(directory)) { - if (["bin", "obj", ".godot"].includes(name)) continue; - const absolute = resolve(directory, name); - if (statSync(absolute).isDirectory()) collect(absolute, extensions, output); - else if (extensions.has(name.slice(name.lastIndexOf(".")))) output.add(normalize(relative(repoRoot, absolute))); - } -} - -function normalizedSha256(content: string): string { - return createHash("sha256").update(content.replace(/\r\n/g, "\n")).digest("hex"); -} - -function collectTopLevel(directory: string, extensions: Set, output: Set) { - for (const name of readdirSync(directory)) { - const absolute = resolve(directory, name); - if (statSync(absolute).isFile() && extensions.has(name.slice(name.lastIndexOf(".")))) { - output.add(normalize(relative(repoRoot, absolute))); - } - } -} - -function resolveWitnessPath(path: string): string { - if (path.startsWith("@cultlib/")) return resolve(cultLibRoot, path.slice("@cultlib/".length)); - if (path.startsWith("@norn/")) return resolve(nornRoot, path.slice("@norn/".length)); - return resolve(repoRoot, path); -} - -function validateDependency(name: string, root: string, expectedRevision: string) { - const git = process.env.DELVEHOLD_GIT ?? "git"; - try { - const actual = execFileSync(git, ["-C", root, "rev-parse", "HEAD"], { encoding: "utf8" }).trim(); - if (actual !== expectedRevision) failures.push(`${name} revision '${actual}' is not admitted '${expectedRevision}'.`); - const status = execFileSync(git, ["-C", root, "status", "--porcelain", "--untracked-files=normal"], { encoding: "utf8" }).trim(); - if (status) failures.push(`${name} worktree '${root}' is dirty.`); - } catch (error) { - failures.push(`Cannot verify ${name} worktree '${root}': ${error instanceof Error ? error.message : String(error)}.`); - } -} - -function normalize(path: string): string { - return path.replaceAll("\\", "/"); -} diff --git a/modeling/control-flow/vite.config.ts b/modeling/control-flow/vite.config.ts deleted file mode 100644 index 542263f..0000000 --- a/modeling/control-flow/vite.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react"; - -export default defineConfig({ - plugins: [react()], - base: "./", - server: { host: "127.0.0.1", port: 4176, strictPort: true }, -}); diff --git a/scripts/control-flow.ps1 b/scripts/control-flow.ps1 deleted file mode 100644 index 5a9140c..0000000 --- a/scripts/control-flow.ps1 +++ /dev/null @@ -1,84 +0,0 @@ -[CmdletBinding()] -param( - [ValidateSet('validate', 'view')] - [string]$Command = 'validate', - [string]$CultLibRoot = 'F:\Projects\CultLib-aetheria-authority' -) - -$ErrorActionPreference = 'Stop' -$repoRoot = Split-Path $PSScriptRoot -Parent -$NornRoot = Join-Path (Split-Path $repoRoot -Parent) 'Norn' -$viewerRoot = Join-Path $repoRoot 'modeling\control-flow' -$nornViewerRoot = Join-Path $NornRoot 'web\norn-viewer' -$graphSource = Get-Content (Join-Path $viewerRoot 'graph.ts') -Raw -if ($graphSource -notmatch 'nornRevision: "([0-9a-f]{40})"') { - throw 'The typed graph does not declare one Norn revision.' -} -$expectedNornRevision = $Matches[1] -$git = (Get-Command git -ErrorAction Stop).Source -$npm = (Get-Command npm.cmd -ErrorAction Stop).Source -$node = (Get-Command node -ErrorAction Stop).Source -$env:PATH = (Split-Path $node -Parent) + ';' + $env:PATH -$env:npm_config_script_shell = (Get-Command pwsh -ErrorAction Stop).Source -$env:DELVEHOLD_NORN_ROOT = $NornRoot -$env:DELVEHOLD_CULTLIB_ROOT = $CultLibRoot -$env:DELVEHOLD_GIT = $git - -if (-not (Test-Path -LiteralPath (Join-Path $nornViewerRoot 'package.json'))) { - throw "Norn was not found at '$NornRoot'. Clone GameCult/Norn beside Delvehold." -} -$actualNornRevision = (& $git -C $NornRoot rev-parse HEAD).Trim() -if ($actualNornRevision -ne $expectedNornRevision) { - throw "Norn must be revision $expectedNornRevision; found $actualNornRevision." -} -$nornStatus = (& $git -C $NornRoot status --porcelain --untracked-files=normal) -join "`n" -if ($nornStatus.Trim()) { throw 'Norn must be clean before Delvehold consumes it.' } - -if (-not (Test-Path -LiteralPath (Join-Path $nornViewerRoot 'node_modules\.bin\tsup.cmd'))) { - & $npm --prefix $nornViewerRoot ci - if ($LASTEXITCODE -ne 0) { throw 'Norn dependency restore failed.' } -} -Push-Location $nornViewerRoot -try { - & $node '.\node_modules\tsup\dist\cli-default.js' 'src\index.ts' '--format' 'esm,cjs' '--dts' '--external' 'react' '--external' 'react-dom' '--clean' - if ($LASTEXITCODE -ne 0) { throw 'Norn library build failed.' } -} finally { - Pop-Location -} - -if (-not (Test-Path -LiteralPath (Join-Path $viewerRoot 'node_modules\.bin\vite.cmd'))) { - & $npm --prefix $viewerRoot install - if ($LASTEXITCODE -ne 0) { throw 'Delvehold control-flow viewer dependency restore failed.' } -} -& $npm --prefix $viewerRoot run build -if ($LASTEXITCODE -ne 0) { throw 'Typed control-flow validation or Norn viewer build failed.' } - -if ($Command -eq 'validate') { return } - -$runRoot = Join-Path $repoRoot 'artifacts\control-flow' -New-Item -ItemType Directory -Path $runRoot -Force | Out-Null -$logPath = Join-Path $runRoot 'norn-viewer.log' -$errorPath = Join-Path $runRoot 'norn-viewer.error.log' -$pidPath = Join-Path $runRoot 'norn-viewer.pid' -$existingPid = if (Test-Path -LiteralPath $pidPath) { [int](Get-Content $pidPath) } else { 0 } -$existing = if ($existingPid -gt 0) { Get-Process -Id $existingPid -ErrorAction SilentlyContinue } else { $null } -if (-not $existing) { - $process = Start-Process -FilePath $npm -WindowStyle Hidden -PassThru ` - -WorkingDirectory $viewerRoot -ArgumentList @('run', 'dev') ` - -RedirectStandardOutput $logPath -RedirectStandardError $errorPath - Set-Content -Path $pidPath -Value $process.Id - $deadline = [DateTime]::UtcNow.AddSeconds(20) - while ([DateTime]::UtcNow -lt $deadline) { - if ($process.HasExited) { throw "Norn viewer exited. See '$errorPath'." } - try { - $null = Invoke-WebRequest -Uri 'http://127.0.0.1:4176' -UseBasicParsing -TimeoutSec 1 - break - } catch { - Start-Sleep -Milliseconds 150 - } - } - if ([DateTime]::UtcNow -ge $deadline) { throw "Norn viewer did not become ready. See '$logPath'." } -} - -Write-Host "NORN_VIEWER_READY http://127.0.0.1:4176 log=$logPath pid=$(Get-Content $pidPath)" -Start-Process 'http://127.0.0.1:4176' diff --git a/scripts/verify-runtime.ps1 b/scripts/verify-runtime.ps1 index cc93135..47e86a4 100644 --- a/scripts/verify-runtime.ps1 +++ b/scripts/verify-runtime.ps1 @@ -11,9 +11,6 @@ if (-not (Test-Path -LiteralPath $godot)) { throw "Godot 4.7.2 Mono console was not found at '$godot'." } -& (Join-Path $PSScriptRoot 'control-flow.ps1') validate -CultLibRoot $CultLibRoot -if ($LASTEXITCODE -ne 0) { throw 'The typed control-flow map is stale or invalid.' } - dotnet build (Join-Path $repoRoot 'Delvehold.sln') -p:CultLibRoot=$CultLibRoot if ($LASTEXITCODE -ne 0) { throw 'The focused .NET/Godot build failed.' } From 2cad7e89f0127a9c65ac232f6a4a51fde944c39a Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 27 Aug 2026 16:02:32 +0200 Subject: [PATCH 08/20] Define Vault-based Hold communities --- AGENTS.md | 3 +- Delvehold/Documentation/Authority Map.md | 8 +++-- Delvehold/Documentation/Implementation Map.md | 2 +- .../Game Design/Foundations/Core Loop.md | 2 +- .../Game Design/Foundations/Design Pillars.md | 2 +- .../Foundations/First Playable World.md | 3 +- .../Game Design/Foundations/Game Overview.md | 2 +- .../Game Design/Foundations/Player Roles.md | 2 +- .../Progression Failure and Time.md | 4 ++- Delvehold/Game Design/HOLD/Civic Play.md | 12 ++++--- .../HOLD/Production and Logistics.md | 8 +++-- Delvehold/Game Design/HOLD/Workshop Play.md | 10 +++--- .../Acceptance Criteria.md | 9 +++++ .../Onboarding and Content Authority.md | 4 ++- .../Shared World/Economy and Contracts.md | 8 +++++ .../Time Causality and Persistence.md | 2 ++ .../Game Design/Shared World/World Graph.md | 34 +++++++++++++------ .../Game Design/Shared World/World Signals.md | 6 ++-- .../Shared World/World State Model.md | 10 +++--- Delvehold/Game Design/Shared World/index.md | 4 +-- Delvehold/Game Design/index.md | 2 +- .../Peoples/Mastership and Citizenship.md | 12 ++++--- Delvehold/Lore/The Greathold.md | 8 +++-- Delvehold/index.md | 2 +- 24 files changed, 107 insertions(+), 52 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6afcd52..ecba1f5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,7 +15,7 @@ - The Godot client and world host communicate through typed CultMesh commands, projections, capabilities, and receipts. Client-local prediction and presentation cannot commit shared truth. - Authoritative dungeon generation runs in the world host using GameCult.Math (CultMath), including `CultMath.Random`. CultGeometry (`GameCult.Geometry`) owns reusable isosurface extraction and neutral mesh artifacts; Godot only adapts committed results into engine objects. - Commands cross authority boundaries as typed intents and produce typed receipts. -- Workshop shards are nodes in a persistent neighborhood graph. Shared edges require explicit ownership, capacity, provenance, and consent. +- The Greathold contains Vault-anchored Holds joined by routes. A workshop has one durable identity and at most one active Hold attachment; dormant workshops create no local pressure. Contracts and other typed relationships preserve ownership, capacity, provenance, and consent without redefining locality. - Dungeon delving is part of a core's lifecycle. Industrial systems must remain legible to dungeon instinct as bounded, contested delving rather than direct tissue damage. - Greathold canon is owned by `Delvehold/Lore/The Greathold.md`: it is a multigenerationally cleared extension of the world-dungeon along the dwarven mountain spine. Its inherited deep routes and institutions created the global adventurer economy and enabled later industrial exploitation; industry did not discover dwarven deep access. @@ -26,6 +26,7 @@ - The masthead owns global section routes. Desktop and mobile within-section navigation are projections of the same Quartz content tree and active-section filters; do not maintain separate route lists. - Shared Quartz machinery remains owned by `GameCult/GameCult-Quartz`; do not vendor it here. - Generated output under `quartz-site/public/` is disposable and must not become source truth. +- Game design, technical documentation, and lore are public project surfaces. Keep their canonical Markdown reachable through the corresponding masthead section and its projected content tree. ## Lore prose diff --git a/Delvehold/Documentation/Authority Map.md b/Delvehold/Documentation/Authority Map.md index 36fd2ee..38ce56e 100644 --- a/Delvehold/Documentation/Authority Map.md +++ b/Delvehold/Documentation/Authority Map.md @@ -5,7 +5,9 @@ description: Canonical owners and projections across the Greathold and external ## Delvehold authority -The Delvehold world organ owns the Greathold and its quantitative economy: players, workshops, parties, civic seals, graph edges, dungeon lifecycle pressure, commodities, resource lots, custody, recipes, facilities, capacity, inventories, orders, prices, contracts, expeditions, and local consequences. It accepts typed intents and emits typed receipts plus updated documents through CultMesh. +The Delvehold world organ owns the Greathold and its quantitative economy: Vaults, Holds, routes, workshop identities, active attachments, presence leases, parties, institutions, civic seals, dungeon lifecycle pressure, commodities, resource lots, custody, recipes, facilities, capacity, inventories, orders, prices, contracts, expeditions, and local consequences. It accepts typed intents and emits typed receipts plus updated documents through CultMesh. + +The world host admits Vault opening, workshop activation, reattachment, and the atomic dormancy transition. The client cannot renew presence through a heartbeat, infer a Hold from infrastructure placement, or keep a dormant workshop in local projections. Hosting and matchmaking may scale independently but cannot create canonical Vaults. ## Ghostlight authority @@ -22,7 +24,7 @@ CultMesh carries two directions of typed exchange. Delvehold publishes a derived ## Game client projection One Godot C# client projects both DELVE and HOLD. DELVE scenes present embodied -expeditions; HOLD scenes present workshops, neighborhoods, contracts, news, and +expeditions; HOLD scenes present workshops, Holds, contracts, news, and civic play. The client's shared session owns its CultMesh connection, local projections, prediction, controls, animation, and presentation. It does not own economic, dungeon-lifecycle, or civic truth. @@ -42,7 +44,7 @@ Factory and delver interfaces expose different projections of that state. Incomp ## Civic handoff -Each franchised workshop owns its seal and the intents it submits: vote, petition, amendment, capacity pledge, refusal, or withdrawal. The Delvehold world organ owns the resulting assembly state, jurisdiction derived from the world graph, ratified common commitments, and institutional consequences. +Each franchised workshop identity owns its seal and the intents it submits while actively attached to a Hold: vote, petition, amendment, capacity pledge, refusal, or withdrawal. The Delvehold world organ owns the resulting assembly state, jurisdiction derived from active Hold membership and realized consequence paths, ratified common commitments, and institutional consequences. A ballot cannot directly mutate a mine, contract market, or military order. The ratified mandate is the input through which the responsible institutions act, and those actions update the same typed world state seen through both modes. diff --git a/Delvehold/Documentation/Implementation Map.md b/Delvehold/Documentation/Implementation Map.md index 17a96c2..cc24e84 100644 --- a/Delvehold/Documentation/Implementation Map.md +++ b/Delvehold/Documentation/Implementation Map.md @@ -33,4 +33,4 @@ The seed state names one workshop, one commons, one dungeon core, one contract, `scripts/verify-runtime.ps1` builds the three projects, starts the host, connects the headless Godot client, rejects an idempotency collision and an invalid projection, restarts the host against the same CultCache, and replays the original enter intent. The smoke compares the complete accepted receipt summary, including its original effective time, across both accepted calls, requires the CultCache state file to remain present, and rejects known canonical-write APIs in client source. -Dungeon generation, CultMath randomness, CultGeometry isosurface extraction, gameplay mutation, and the Ghostlight boundary remain subsequent cuts. +Vault and Hold state, workshop identity and presence, institutions, dungeon generation, CultMath randomness, CultGeometry isosurface extraction, gameplay mutation, and the Ghostlight boundary remain subsequent cuts. diff --git a/Delvehold/Game Design/Foundations/Core Loop.md b/Delvehold/Game Design/Foundations/Core Loop.md index 4e8dd97..6e66346 100644 --- a/Delvehold/Game Design/Foundations/Core Loop.md +++ b/Delvehold/Game Design/Foundations/Core Loop.md @@ -5,7 +5,7 @@ aliases: - /Game-Design/Core-Loop --- -The first playable world contains one workshop, one dungeon core, one contract type, one expedition party, one extracted material, and one persistent expedition history. +The first playable world contains one Vault and its Hold, one active workshop, one baseline institution, one dungeon core, one contract type, one expedition party, one extracted material, and one persistent expedition history. ## HOLD diff --git a/Delvehold/Game Design/Foundations/Design Pillars.md b/Delvehold/Game Design/Foundations/Design Pillars.md index cd22c01..fa5aabc 100644 --- a/Delvehold/Game Design/Foundations/Design Pillars.md +++ b/Delvehold/Game Design/Foundations/Design Pillars.md @@ -29,4 +29,4 @@ Players inhabit delvers and independent workshop managers. They influence civili ## Cozy does not mean consequence-free -Warmth, mastery, neighborhood life, and humane rhythms are the foreground of HOLD. They matter because the workshop is embedded in scarcity, ecology, politics, and mutual dependence. +Warmth, mastery, Hold life, and humane rhythms are the foreground of HOLD. They matter because the workshop is embedded in scarcity, ecology, politics, and mutual dependence. diff --git a/Delvehold/Game Design/Foundations/First Playable World.md b/Delvehold/Game Design/Foundations/First Playable World.md index 4a29966..d112b90 100644 --- a/Delvehold/Game Design/Foundations/First Playable World.md +++ b/Delvehold/Game Design/Foundations/First Playable World.md @@ -3,7 +3,7 @@ title: First playable world description: The smallest vertical slice that can prove the joined design. --- -The first playable proof contains one workshop, one connected commons, one dungeon core, one extracted material family, one contract form, one expedition party, one useful processing chain, and persistent expedition history. +The first playable proof contains one Vault and its Hold, one active workshop attachment, one baseline institution, one dungeon core, one extracted material family, one contract form, one expedition party, one useful processing chain, and persistent expedition history. The slice is complete only when: @@ -15,6 +15,7 @@ The slice is complete only when: 6. the party returns with material, evidence, injury, loss, or some combination; 7. the outcome changes workshop decisions and the next expedition; 8. the history remains after the game client reconnects and the world host restarts. +9. the workshop can pass through 36-hour dormancy and reactivate in the same or another Hold without losing identity, relationships, or receipted history. One institutional contract keeps DELVE playable without a workshop manager online. One public need keeps HOLD meaningful without requiring a delver to accept a personal order immediately. The slice does not need a broad item catalogue, global war, or complete combat metagame. It needs one honest return stroke. diff --git a/Delvehold/Game Design/Foundations/Game Overview.md b/Delvehold/Game Design/Foundations/Game Overview.md index 167b5fe..1745c16 100644 --- a/Delvehold/Game Design/Foundations/Game Overview.md +++ b/Delvehold/Game Design/Foundations/Game Overview.md @@ -13,7 +13,7 @@ The game is for players who enjoy cooperative PvE, expressive preparation, autom ## Product shape -- One Godot C# client projects both embodied dungeon expeditions and workshop, neighborhood, contract, news, and civic play. +- One Godot C# client projects both embodied dungeon expeditions and workshop, Hold, contract, news, and civic play. - One central C# world host owns the canonical Delvehold simulation. Changing modes changes presentation and available interaction, not world authority. - CultMesh carries typed state and capabilities from their owning world organs. - One canonical world history survives player absence. There are no seasonal wipes used to repair inflation or erase consequence. diff --git a/Delvehold/Game Design/Foundations/Player Roles.md b/Delvehold/Game Design/Foundations/Player Roles.md index e331cc1..49733a5 100644 --- a/Delvehold/Game Design/Foundations/Player Roles.md +++ b/Delvehold/Game Design/Foundations/Player Roles.md @@ -13,7 +13,7 @@ A party may contain one or more players where the expedition supports it. Solo a ## Workshop managers -Workshop managers practice magic, store materials, craft, repair, automate, commission, trade, publish contracts, maintain shared infrastructure, and participate in relevant civic bodies. Each player owns one workshop shard and its seal. Scale and wealth do not grant extra votes or authority inside another workshop. +Workshop managers practice magic, store materials, craft, repair, automate, commission, trade, publish contracts, maintain shared infrastructure, and participate in relevant civic bodies. Each player owns one durable workshop identity and its seal, with at most one active Hold attachment. Scale and wealth do not grant extra votes or authority inside another workshop. ## Shared limits diff --git a/Delvehold/Game Design/Foundations/Progression Failure and Time.md b/Delvehold/Game Design/Foundations/Progression Failure and Time.md index 2dff965..7cf9155 100644 --- a/Delvehold/Game Design/Foundations/Progression Failure and Time.md +++ b/Delvehold/Game Design/Foundations/Progression Failure and Time.md @@ -15,8 +15,10 @@ Consequences remain attached to their owners. Equipment left below can become du - A narrow expedition or workshop visit should produce useful progress in a short session. - Deeper expeditions and complex production chains support longer commitments by choice. -- HOLD automation continues bounded work while absent without making constant attendance optimal. +- HOLD automation continues only until its admitted work completes, its inputs run out, or the workshop's 36-hour presence lease expires. - Civic windows are generous, asynchronous, and optional. - Catch-up summarizes what happened; it does not secretly puppet a player. The world advances without requiring every player to be present. No absent player's uncommitted plan becomes an action. + +An authenticated foreground session renews workshop presence; background jobs and client heartbeats do not. Lease expiry checkpoints work once and removes the workshop from live Hold simulation without decay, forfeiture, or relationship loss. Catch-up reports the bounded active interval and dormancy receipt instead of replaying production through the dormant period. diff --git a/Delvehold/Game Design/HOLD/Civic Play.md b/Delvehold/Game Design/HOLD/Civic Play.md index d264548..81c031d 100644 --- a/Delvehold/Game Design/HOLD/Civic Play.md +++ b/Delvehold/Game Design/HOLD/Civic Play.md @@ -7,17 +7,17 @@ aliases: HOLD places workshop managers directly inside the Greathold's political life. The foreground fantasy is civic stewardship, not market speculation: understand an issue, argue with neighbours, commit the workshop's seal and capacity, and live with the resulting common action. -The franchise follows [[Mastership and Citizenship|dwarven Mastership]]. Each independent workshop has one seal and one vote. Wealth, throughput, annexes, and consortium membership do not create additional votes. +The franchise follows [[Mastership and Citizenship|dwarven Mastership]]. Each independent workshop identity has one seal and one vote while actively attached to a Hold. Wealth, throughput, and consortium membership do not create additional votes. ## Local-first issue routing -An issue begins with the smallest body that contains its material consequences. A shared mana well belongs first to its connected workshops. A lift belongs to its passengers, maintainers, and dependent districts. A deep gate implicates its neighbouring holds, delver associations, ecological stewards, and supply network. +An issue begins with the smallest body that contains its material consequences. A Hold question begins among its active workshops, residents, institutions, delvers, and ecological stewards. A route closure also implicates the connected Holds and its passengers. A deep gate implicates its Hold, delver associations, ecological stewards, and supply relationships. -Workshop shards already form a persistent [[Game Design/Shared World/World Graph|neighbourhood graph]]. Civic jurisdiction is projected from the same typed relationships rather than from a separate political map. +Active [[Game Design/Shared World/World Graph|Hold attachments]] establish ordinary local civic relevance. Contracts, routes, infrastructure, core relationships, and realized consequences can widen jurisdiction without redefining where a workshop lives. A proposal can move outward when its consequences do: -> workshop moot → shared junction → subsidiary hold → Greathold-wide commitment +> affected workshop or institution → Hold → connected Holds → Greathold-wide commitment Greathold-wide policy is therefore an assembled common mandate, not an order descending from a permanent capital. The player does not receive an identical national ballot merely because a vote exists somewhere in the world. @@ -69,9 +69,11 @@ Civic play is slow, asynchronous, and optional in ordinary life: Early public motions are authored by the world simulation so that they remain coherent with live events. Player petitions can later gather seals to enter the appropriate assembly without turning the first prototype into an unmoderated parliament. +A dormant workshop enters no future issue routing or quorum. Votes already cast and ratified commitments remain facts. Reactivation grants prospective relevance only in the newly attached Hold. + ## From vote to world -A common commitment acts through institutions and material cooperation. A mining moratorium changes gate access, lift schedules, listed contracts, public provisioning, and workshop participation. It is not an invisible global flag repaired after the fact. +A common commitment acts through institutions and material cooperation. Policy changes institutional mandates, budgets, procurement, access, reserves, safety rules, and service obligations; players do not select each institutional purchase or schedule. A mining moratorium changes gate access, lift schedules, listed contracts, public provisioning, and workshop participation. It is not an invisible global flag repaired after the fact. A defence mobilization authorizes public bodies to issue procurement, redirects shared infrastructure, and creates [[Game Design/Shared World/Economy and Contracts|institutional contracts]]. No individual workshop authors military strategy. Workshops decide how they respond to the collectively formed mandate; foreign states and their militaries remain independent non-player actors. diff --git a/Delvehold/Game Design/HOLD/Production and Logistics.md b/Delvehold/Game Design/HOLD/Production and Logistics.md index 5b20c99..9defb3e 100644 --- a/Delvehold/Game Design/HOLD/Production and Logistics.md +++ b/Delvehold/Game Design/HOLD/Production and Logistics.md @@ -1,12 +1,14 @@ --- title: Production and logistics -description: Facilities, recipes, capacity, custody, and specialization across the workshop graph. +description: Facilities, recipes, capacity, custody, and specialization across Holds. --- Production transforms identified inputs through a facility and practice into owned outputs. Every transformation records recipe or method, consumed and produced lots, operator or automation, facility, time, quality-relevant conditions, and provenance. -Facilities expose bounded capacity. Reservations, maintenance, power, labor, route access, storage, and safety can constrain that capacity without becoming disconnected modifier cards. Logistics moves custody along explicit graph edges; inventory is not globally available because a number exists somewhere. +Facilities expose bounded capacity. Reservations, maintenance, power, labor, route access, storage, and safety can constrain that capacity without becoming disconnected modifier cards. Logistics moves custody through workshops, institutions, contracts, and inter-Hold routes; inventory is not globally available because a number exists somewhere. -No workshop efficiently contains every chain. Players specialize, trade, commission, share commons, or accept slower local substitutes. Orders express desired future exchange; accepted orders become commitments; fulfilled transfers become realized trade. Only realized effects enter the aggregate external-world boundary. +No workshop efficiently contains every chain. Players specialize, trade, commission, use institutional services, or accept less efficient substitutes. Orders express desired future exchange; accepted orders become commitments; fulfilled transfers become realized trade. Only realized effects enter the aggregate external-world boundary. + +Workshop reattachment does not teleport committed material. Private configuration and uncommitted inventory may resume with the workshop identity; escrowed, dispatched, installed, institution-held, or otherwise transferred lots remain under their recorded custody until another receipted transfer moves them. The economy must support ordinary civic life as well as expedition gear: heat, food processing, medicine, construction, transport, maintenance, education, ritual goods, and delightful nonsense all compete for material and capacity. diff --git a/Delvehold/Game Design/HOLD/Workshop Play.md b/Delvehold/Game Design/HOLD/Workshop Play.md index fd49ff4..8bde778 100644 --- a/Delvehold/Game Design/HOLD/Workshop Play.md +++ b/Delvehold/Game Design/HOLD/Workshop Play.md @@ -1,12 +1,14 @@ --- title: Workshop play -description: The daily practice, space, relationships, and responsibilities of one independent workshop. +description: The daily practice, relationships, and responsibilities of one independent workshop. --- -The workshop is a private production shard, a home for useful practice, and the material basis of one civic seal. The player begins by scrying, preparing, crafting, repairing, carrying, and tending processes directly. +The workshop is a durable productive practice and the bearer of one civic seal. The player begins by scrying, preparing, crafting, repairing, carrying, and tending processes directly. -Manual work teaches the material rules that later automation expresses. It should be tactile, readable, and brief enough that learning a process is satisfying without requiring permanent repetition. The workshop grows through new practices, tools, spatial choices, assistants, relationships, and connections to shared infrastructure. +Manual work teaches the material rules that later automation expresses. It should be tactile, readable, and brief enough that learning a process is satisfying without requiring permanent repetition. The workshop grows through new practices, tools, assistants, relationships, automation patterns, and connections to shared institutions. -Space remains scarce. A workshop chooses what to keep near, what to automate, what to commission, and which commons or neighbors to depend upon. Expansion cannot silently annex another shard or multiply political authority. +No workshop efficiently contains every production chain. A player chooses what to automate, commission, trade, and request through a contract. Specialization and recurring collaborators create dependence without placing workshops on a shared city map. + +The workshop identity survives absence. Its active [[Game Design/Shared World/World Graph|Hold attachment]] expires after 36 hours without an authenticated foreground session, stopping bounded automation and removing the workshop from live local circulation. Returning immediately restores the same practice and history in the former Hold or attaches it to another one. Absence does not decay tools, erase relationships, or multiply political authority. The cozy fantasy comes from accumulated familiarity: named stations, remembered materials, recurring customers and delvers, repaired tools, small rituals, and a machine whose history remains visible. Efficiency is useful, but the workshop is not reduced to throughput. diff --git a/Delvehold/Game Design/Narrative and Validation/Acceptance Criteria.md b/Delvehold/Game Design/Narrative and Validation/Acceptance Criteria.md index 5974df0..2082e5d 100644 --- a/Delvehold/Game Design/Narrative and Validation/Acceptance Criteria.md +++ b/Delvehold/Game Design/Narrative and Validation/Acceptance Criteria.md @@ -29,6 +29,15 @@ The design is proven by visible paths, not by the existence of schemas. - Catch-up is bounded, names the interval it summarizes, and never puppets an absent player. - The visible news, price, contract, expedition, and workshop changes can be traced to typed events and receipts at the layer where players experience them. +## Holds and workshop presence + +- A Vault opens through committed survey, route, provisioning, and settlement state inside the Greathold rather than through server selection. +- Only active attachments contribute workshop demand, production, discovery, notification, infrastructure reservation, or future civic routing in a Hold. +- At 36-hour lease expiry, automation checkpoints once, unaccepted listings disappear, accepted obligations settle exactly once under their admitted terms, and the workshop produces no continuing live load. +- Dormancy preserves workshop identity, history, relationships, seal identity, custody, debts, completed acts, and already-cast votes. +- Reactivating the workshop in another Hold leaves no old local pressure or dual civic attachment, replays no dormant production, and moves committed material only through explicit custody receipts. +- Quiet Holds persist, remain recoverable, and are never merged or wiped to repair population balance. + ## Procedural dungeon proof - Identical canonical input, CultMath seed, and generator version reproduce the same dungeon projection after host restart. diff --git a/Delvehold/Game Design/Narrative and Validation/Onboarding and Content Authority.md b/Delvehold/Game Design/Narrative and Validation/Onboarding and Content Authority.md index b01a764..8e6cf8d 100644 --- a/Delvehold/Game Design/Narrative and Validation/Onboarding and Content Authority.md +++ b/Delvehold/Game Design/Narrative and Validation/Onboarding and Content Authority.md @@ -3,7 +3,7 @@ title: Onboarding and content authority description: The first-session reveal and the boundary between authored canon, simulation, and presentation. --- -Onboarding introduces one world through a particular practice. A new player chooses a DELVE or HOLD entry without choosing a separate history, server, or lesser mode. +Onboarding introduces one world through a particular practice. A new player chooses a DELVE or HOLD entry without choosing a separate history, server, or lesser mode. The player joins a viable Hold inside the Greathold and may later reattach a workshop elsewhere without creating another identity. ## First-session sequence @@ -15,6 +15,8 @@ Onboarding introduces one world through a particular practice. A new player choo Tutorial protection may simplify risk and presentation, but it cannot create a private tutorial economy or noncanonical outcome. Any authored starter state enters through the same Delvehold authority as later play. +Settlement recommendations favour Holds with healthy institutions, useful collaborators, or room for active community growth. Friends and established relationships remain visible choices. Recommendations never force relocation, merge quiet Holds, or present a Vault as an infrastructure server. + ## Content authority - **Authored anchors** own canon: Greathold history and institutions, peoples, dungeon principles, named places, core revelations, crisis premises, and the allowed ontology supplied to Ghostlight. diff --git a/Delvehold/Game Design/Shared World/Economy and Contracts.md b/Delvehold/Game Design/Shared World/Economy and Contracts.md index 89429aa..73ae12a 100644 --- a/Delvehold/Game Design/Shared World/Economy and Contracts.md +++ b/Delvehold/Game Design/Shared World/Economy and Contracts.md @@ -23,6 +23,8 @@ Planned production and offered orders remain owned intents. Only completed trans **Institutional contracts** originating inside the Greathold are created by Delvehold on behalf of civic works, guilds, colleges, temples, insurers, commercial bodies, or public defence institutions. Foreign buyers, governments, and militaries remain Ghostlight actors: Ghostlight emits their demand, offer, or decision as an external response, and Delvehold creates a Greathold contract only after admitting that intent. Contracts reflect persistent institutional state and public mandates rather than filling empty matchmaking slots. +Institutions also provide each Hold's baseline supply and demand. Population needs, facilities, inventories, budgets, mandates, and ratified policy determine what they produce, buy, provision, and commission. Players can change those constraints through civic play but do not puppet individual institutional transactions. + Both kinds enter the same legible market with explicit provenance. The interface must never disguise a simulated institution as a human workshop or imply that a player controls an institutional objective. ## Military procurement @@ -37,6 +39,12 @@ Institutional work provides a durable baseline of expedition opportunities. Delv Workshop contracts should remain more personal and expressive: unusual equipment, negotiated priorities, relationships with recurring parties, speculative discoveries, local shortages, and returns that visibly transform a player's machinery. +## Workshop and delver relationships + +Recurring contracts preserve the history between a workshop and a delver or party: terms kept or broken, equipment supplied and returned, substitutions, discoveries, injuries, rescues, losses, payment, and ecological consequences. These relationships belong to stable identities and survive dormancy or movement between Holds. Current Hold membership affects discovery and convenience, not memory. + +Unaccepted workshop listings disappear when its 36-hour presence lease expires. Every accepted order or contract declares what dormancy does: complete before expiry, settle from escrow, transfer to a named institution, or cancel or default under agreed terms. The transition emits one receipt and cannot erase the obligation or leave the workshop active forever. + ## Contract outcomes A canonical contract records issuer class, objective, offered equipment, reward allocation, target region, accepted party, lifecycle status, outcome references, and the authority under which it was published. diff --git a/Delvehold/Game Design/Shared World/Time Causality and Persistence.md b/Delvehold/Game Design/Shared World/Time Causality and Persistence.md index e9c986a..82b2f66 100644 --- a/Delvehold/Game Design/Shared World/Time Causality and Persistence.md +++ b/Delvehold/Game Design/Shared World/Time Causality and Persistence.md @@ -15,6 +15,8 @@ After each accepted boundary update, Ghostlight may run a bounded strategic wave On recovery, the bridge replays at most eight missed hourly epochs. Older time is summarized into one coarse strategic horizon with its covered interval, source aggregates, uncertainty, and provenance. Catch-up cannot invent player actions, silently accept pending intent, or conceal lost intervals. +Workshop presence uses a versioned 36-hour lease measured by world-host time. An authenticated foreground session renews it; client heartbeats, reconnect retries, and background work cannot. Expiry commits one idempotent dormancy transition that checkpoints bounded automation, settles listings and obligations under their admitted terms, and removes the live Hold attachment. Reactivation begins from the committed dormant state and never replays production across the inactive interval. + ## Delivery rules - Stable idempotency keys make retries harmless. diff --git a/Delvehold/Game Design/Shared World/World Graph.md b/Delvehold/Game Design/Shared World/World Graph.md index d5bbc8d..ce63c0b 100644 --- a/Delvehold/Game Design/Shared World/World Graph.md +++ b/Delvehold/Game Design/Shared World/World Graph.md @@ -1,22 +1,34 @@ --- -title: World graph -description: Private workshop shards joined by scarce, social infrastructure. +title: Vaults and holds +description: The shared places, active communities, and routes of the Greathold. aliases: - /Game-Design/World-Graph --- -Every factory player owns a private workshop shard somewhere within the distributed civic body of [[The Greathold]]. The shard is both a production space and the seat of the [[Mastership and Citizenship|workshop franchise]]. Shards are arranged as nodes in a persistent neighbourhood graph rather than literal adjacent rooms in one Euclidean factory. +The Greathold is a connected civilization of **Holds**. Each Hold inhabits a **Vault**: a large dungeon-formed chamber or chamber system whose core maintains a particular biome. Routes join Holds along the mountain spine and lead from them into contested dungeon regions. -The graph contains three initial node kinds: +A Hold is the ordinary unit of local life. Its active workshops, resident and visiting delvers, institutions, population, and local core encounter the same markets, contracts, news, and civic consequences. Cross-Hold effects require a named route, contract, institution, core relationship, or Greathold-wide mandate. -- **workshops**, owned by individual players; -- **commons**, such as mana wells, exchanges, junctions, and guild facilities; -- **dungeon cores**, ecological actors shared by several workshops and delving populations. +The simulation does not place workshops at coordinates on a shared Vault floor. A workshop interior may be a spatial HOLD scene, but the workshop participates in the Greathold through an active Hold attachment. Contracts, friendships, customers, and research partners connect stable identities without making them spatial neighbours. -Edges carry typed relationships such as mana, materials, contracts, research, dungeon pressure, or visiting workers. They expose capacity, ownership, permission, and provenance. A neighbor may create useful pressure but cannot silently acquire authority inside another player's shard. +## Workshops and presence -The graph projects the Greathold's functional geography: ancient galleries, lift routes, mana conduits, commons, guild relationships, and access to deep gates matter more than straight-line distance through stone. Two workshops can be neighbours through a shared junction while occupying chambers far apart along the spine. +A workshop has a durable identity: owner, seal, practice, configuration, history, relationships, and custody. It has at most one live Hold attachment, which supplies local discovery, institutional access, civic relevance, production participation, and current market context. -Workshop space is scarce. Players specialize, trade, and share infrastructure because no shard can efficiently contain every production chain. Arbitrary neighbourhood placement creates local cultures without flattening the world into one global auction house. +An authenticated foreground session renews that attachment's presence lease for 36 hours of world-host time. Background work and client heartbeats cannot renew it. At expiry, one atomic dormancy transition checkpoints bounded automation, removes unaccepted listings, releases live reservations, removes future civic routing, and detaches the workshop from the Hold. The dormant identity retains its history, relationships, seal, configuration, custody, debts, and completed acts while producing no continuing demand, production, discovery, notification, or population pressure. -The graph also routes [[Game Design/HOLD/Civic Play|civic jurisdiction]]. A decision begins among the workshops, residents, infrastructure, and delving populations connected to its consequences. Larger mandates are assembled through those relationships rather than projected from a separate national map. +Accepted contracts and orders state their dormancy terms when accepted. They may complete before expiry, settle from escrow, transfer to a named institution, or default or cancel under agreed terms. Dormancy produces the corresponding receipt and cannot erase an obligation or keep a workshop live indefinitely. + +Returning reactivates the same workshop identity. Its previous Hold is the default suggestion, but the player may attach it to another Hold where friends or collaborators now live. The transition removes any old jurisdiction before admitting the new one. Relationship history follows the identity; committed goods and route-bound capacity follow custody and move only through receipted transfers. + +## Institutions and local circulation + +Each Hold contains simulated civic and commercial institutions that supply ordinary necessities, buy useful output, maintain shared services, and publish baseline contracts. Their facilities, inventories, budgets, and mandates keep DELVE and HOLD playable without pretending to be human workshops. + +Players govern institutions indirectly. Ratified policy changes procurement priorities, reserves, access, safety rules, service guarantees, budgets, and extraction limits. Institutions choose individual purchases, schedules, and contracts within those constraints. + +## Opening and sustaining Holds + +A new Hold begins when delvers survey a Vault, routes and institutions make settlement viable, and players voluntarily attach workshops and characters there. Opening responds to sustained active community pressure, institutional load, or chosen frontier play rather than lifetime account totals. It is a committed event inside the Greathold, never a server or matchmaking shard. + +Holds persist when their population becomes quiet. Institutions preserve minimum circulation while dormant workshops remain absent from live simulation. Settlement recommendations may favour a viable quiet Hold, but relocation is voluntary and no Hold is merged, wiped, or discarded to repair population balance. diff --git a/Delvehold/Game Design/Shared World/World Signals.md b/Delvehold/Game Design/Shared World/World Signals.md index 63db13d..437dc6b 100644 --- a/Delvehold/Game Design/Shared World/World Signals.md +++ b/Delvehold/Game Design/Shared World/World Signals.md @@ -11,7 +11,7 @@ A HOLD player can store, craft, automate, commission, deliberate, vote, and publ ## Economic transmission -Geopolitical change propagates through ordinary work before it resolves into a named historical event. If a civilization stops importing dwarven machinery as the [[International Crisis|international crisis]] approaches, workshop managers first encounter a price shock. Lost export demand changes margins and inventories. Buyers, suppliers, and neighbouring workshops then redirect capacity toward other markets, substitutes, stockpiles, transport, weapons, and strategic dungeon materials. +Geopolitical change propagates through ordinary work before it resolves into a named historical event. If a civilization stops importing dwarven machinery as the [[International Crisis|international crisis]] approaches, workshop managers first encounter a price shock. Lost export demand changes margins and inventories. Buyers, suppliers, active workshops, and institutions then redirect capacity toward other markets, substitutes, stockpiles, transport, weapons, and strategic dungeon materials. The same disturbance reaches DELVE as altered contract volume, equipment shortages, replacement costs, route closures, hazard pay, requisitions, and growing demand for particular extractions. Both player workshops and [[Game Design/Shared World/Economy and Contracts|world institutions]] transmit that demand. One world event therefore crosses the game through a shared chain: @@ -50,6 +50,8 @@ Players who enjoy reading markets may act on those signals. Most players can tre News provides interpretation; the economy provides evidence. Neither is complete alone. -Players should receive warning signals before major consequences, but those signals should preserve uncertainty. A price shock might indicate war, sanctions, a failed route, political theatre, ecological disruption, or an attempt to corner a market. Players create social knowledge by comparing reports with prices, contracts, expedition testimony, and conditions in neighbouring workshop shards. +Players should receive warning signals before major consequences, but those signals should preserve uncertainty. A price shock might indicate war, sanctions, a failed route, political theatre, ecological disruption, or an attempt to corner a market. Players create social knowledge by comparing reports with prices, contracts, expedition testimony, and conditions in their Hold or among known partners. + +Dormant workshops vanish from live Hold discovery, market listings, ordinary news routing, and forecasts. Direct collaborators retain their shared history, which becomes available again when the same workshop identity reactivates. The living world is therefore not delivered as exposition. It is inferred through work, then argued about between players. diff --git a/Delvehold/Game Design/Shared World/World State Model.md b/Delvehold/Game Design/Shared World/World State Model.md index 9863d50..51dfc1e 100644 --- a/Delvehold/Game Design/Shared World/World State Model.md +++ b/Delvehold/Game Design/Shared World/World State Model.md @@ -9,18 +9,20 @@ DELVE/HOLD canonical state is typed CultCache state published through CultMesh. | Family | Canonical contents | | --- | --- | -| People and authority | players, delvers, parties, Masters, workshops, civic seals, institutions, control and consent | -| World graph | workshops, commons, cores, regions, routes, capacity, permissions, provenance and jurisdiction | +| People and authority | players, delvers, parties, Masters, workshop identities, civic seals, institutions, control and consent | +| Holds and presence | Vaults, Holds, routes, active attachments, presence leases, dormancy receipts, capacity, provenance and jurisdiction | | Economy | commodity definitions, resource lots, recipes, facilities, inventories, custody, orders, realized trades and price observations | | Contracts and expeditions | issuers, objectives, kits, rewards, parties, lifecycle, routes, findings, injuries, losses, returns and histories | | Dungeon ecology | cores, lifecycle pressure, populations, adaptations, memory, topology, hazards, responses and extraction provenance | | Civic life | assemblies, proposals, seals, testimony, ratified mandates, capacity commitments, withdrawals and institutional actions | | Information | facts, observations, reports, rumours, sources, confidence, disclosure and knower scope | -| Continuity | world time, revisions, events, commands, receipts, causal references, schema versions, generation seeds, generator versions, input and output digests | +| Continuity | world time, revisions, events, commands, receipts, causal references, schema versions, policy versions, generation seeds, generator versions, input and output digests | ## Derived state -Scenes, dashboards, prices, digests, forecasts, generated dungeon rooms, news layouts, active navigation, and the aggregate Greathold boundary are projections. They may be rebuilt from canonical state and cannot repair it. +Scenes, dashboards, prices, digests, forecasts, generated dungeon rooms, news layouts, active navigation, active Hold populations, local discovery, settlement recommendations, and the aggregate Greathold boundary are projections. They may be rebuilt from canonical state and cannot repair it. + +A workshop identity persists independently of its nullable Hold attachment. The identity owns its seal, practice, cold configuration, history, relationships, and custody. The attachment owns current local participation and its versioned 36-hour presence lease. Dormancy removes the attachment through one receipted transition; a later activation may attach the same identity to another Hold. The world host commits each dungeon generation record and its provenance. CultGeometry mesh artifacts are deterministic outputs of that record. Godot diff --git a/Delvehold/Game Design/Shared World/index.md b/Delvehold/Game Design/Shared World/index.md index 7895a6e..9815581 100644 --- a/Delvehold/Game Design/Shared World/index.md +++ b/Delvehold/Game Design/Shared World/index.md @@ -1,6 +1,6 @@ --- title: Shared world -description: State ownership, simulation, economy, graph, information, and the Ghostlight membrane. +description: State ownership, simulation, economy, Holds, information, and the Ghostlight membrane. --- The shared world is not synchronization between clients. It is typed state owned by the organs that can truthfully decide it. @@ -9,6 +9,6 @@ The shared world is not synchronization between clients. It is typed state owned - [[Simulation Architecture]] - [[Ghostlight Boundary]] - [[Game Design/Shared World/Economy and Contracts|Economy and contracts]] -- [[Game Design/Shared World/World Graph|World graph]] +- [[Game Design/Shared World/World Graph|Vaults and holds]] - [[Game Design/Shared World/World Signals|World signals]] - [[Time Causality and Persistence|Time, causality, and persistence]] diff --git a/Delvehold/Game Design/index.md b/Delvehold/Game Design/index.md index 9d9061f..18e2c6e 100644 --- a/Delvehold/Game Design/index.md +++ b/Delvehold/Game Design/index.md @@ -10,7 +10,7 @@ DELVE/HOLD is one persistent game presented through two modes. This section is t - **[[Game Design/Foundations/index|Foundations]]** — overview, pillars, roles, joined loop, progression, and the first playable proof. - **[[Game Design/DELVE/index|DELVE]]** — expeditions, living dungeon generation, encounters, extraction, preparation, and recovery. - **[[Game Design/HOLD/index|HOLD]]** — workshop practice, runes, automation, production, logistics, and civic stewardship. -- **[[Game Design/Shared World/index|Shared world]]** — canonical state, simulation ownership, Ghostlight, economy, contracts, graph, signals, time, and persistence. +- **[[Game Design/Shared World/index|Shared world]]** — canonical state, simulation ownership, Ghostlight, economy, contracts, Vaults, Holds, signals, time, and persistence. - **[[Game Design/Narrative and Validation/index|Narrative and validation]]** — revelation, crisis progression, and acceptance criteria. ## Design status diff --git a/Delvehold/Lore/Peoples/Mastership and Citizenship.md b/Delvehold/Lore/Peoples/Mastership and Citizenship.md index e6152f1..52ab487 100644 --- a/Delvehold/Lore/Peoples/Mastership and Citizenship.md +++ b/Delvehold/Lore/Peoples/Mastership and Citizenship.md @@ -15,18 +15,20 @@ The expected life passage has six stages: 2. **Apprenticeship.** The apprentice studies and works under one or several Masters. 3. **Journeywork.** The developing craftsperson travels between workshops, gathers techniques, and produces work with increasing independence. 4. **Masterwork.** The candidate demonstrates a durable contribution before a local body of Masters. -5. **Foundation.** The new Master receives or constructs an independent workshop shard. +5. **Foundation.** The new Master establishes an independent practice and marks its work under its own name. 6. **Sealing.** The workshop registers its civic seal and its Master enters the franchise. A workshop need not be a factory. Forges, kitchens, clinics, archives, rune laboratories, architectural practices, schools, and other independently maintained useful institutions can qualify. The disputed boundary of useful practice is one of the Greathold's oldest recurring arguments. -The commons owe an apprentice food, instruction, tools, examination, appeal, and a chance at scarce workshop space. When House children inherit every viable room, Mastership becomes property qualification with a ceremonial hammer attached. Greathold law calls that corruption; powerful Houses call it continuity. +The commons owe an apprentice food, instruction, tools, examination, appeal, and a real chance to establish an independent practice. Houses have often tried to reserve examinations, guild access, and public contracts for their own children. Greathold law calls that corruption; powerful Houses call it continuity. ## The workshop franchise -Each independent workshop carries one civic seal. Annexes, branch facilities, factory floors, and accumulated wealth do not multiply it. A consortium cannot acquire twelve votes by putting twelve doors on one productive body. +Each independent workshop carries one civic seal. Branches, facilities, employees, and accumulated wealth do not multiply it. A consortium cannot multiply its vote by counting production lines. -The workshop stands behind the franchise. It gives neighbours somewhere to inspect the Master's work and bring a complaint. Dwarven political thought makes that public responsibility the price of binding others through a common decision. +The workshop stands behind the franchise. Neighbours can inspect its work, trace its mark, ask for an audit, and bring a complaint. Dwarven political thought makes that answerability the price of binding others through a common decision. + +A workshop may fall dormant or resume in another Hold without becoming a new legal person. Its seal and history travel with the practice. The seal enters new moots only after the workshop wakes in a Hold; old votes, debts, complaints, and ratified commitments remain where their acts placed them. Apprentices, journeyworkers, unrecognized foreign residents, and most itinerant delvers may petition, organize, refuse work, and influence Masters. They cannot apply a civic seal. @@ -46,6 +48,6 @@ The franchise gives workshop owners political standing and withholds the seal fr - whether industrial automation is destroying ordinary paths to Mastership; - whether foreign credentials are being examined or merely excluded; - whether apprentices are being taught or retained as cheap labour; -- whether a Master without a surviving workshop retains the franchise; +- when a dormant workshop may resume the franchise and in which Hold; - whether a sapient spell engine could produce a Masterwork; - whether a dungeon core could. diff --git a/Delvehold/Lore/The Greathold.md b/Delvehold/Lore/The Greathold.md index 88b8c5c..bd533fc 100644 --- a/Delvehold/Lore/The Greathold.md +++ b/Delvehold/Lore/The Greathold.md @@ -5,13 +5,17 @@ description: The dwarven homeland cleared through the living spine of a mountain The Greathold occupies the mountain range's dungeon. Its chambers, galleries, shafts, and older structures have been cleared and inhabited over many dwarven generations. -Most dwarves live somewhere along this immense civic spine. “The Greathold” names the connected whole even where its districts, subsidiary holds, and frontier galleries possess their own governments and identities. +Some dungeon cores open immense chambers and establish a climate, soil, water cycle, and community of organisms inside them. Dwarves call such a chamber or chamber system a **Vault**. Once settled, its workshops, delver halls, homes, markets, and public institutions form a **Hold**. The Greathold is the connected civilization of these Holds along the mountain spine. + +Most dwarves live somewhere along this immense civic spine. “The Greathold” names the connected whole even where individual Holds and frontier galleries possess their own governments and identities. Government begins near the matter under dispute. A broken lift belongs first to its stranded districts; if the closure blocks the spine, their settlement must gather seals from farther holds. No permanent sovereign centre can issue a Greathold-wide order alone. The [[Mastership and Citizenship|workshop franchise]] supplies the characteristic civic unit. ## Built by delving -Expansion came piecemeal. A chamber cleared for settlement still needed its roof shored, its drains defended, and its sealed walls watched for new breeding. Later generations inherited the maintenance with the room. +Expansion came piecemeal. Opening a Vault required survey expeditions, a safe route, provisioning, and some accommodation with its core and biome. A chamber cleared for settlement still needed its roof shored, its drains defended, and its sealed walls watched for new breeding. Later generations inherited the maintenance with the room. + +New Holds still begin this way. A Vault may have existed for millennia before delvers find it. Clearing and provisioning open it to voluntary settlement; no census office manufactures another piece of the mountain because the population ledger grew. Delving predates the adventuring profession. It opened homes and roads, managed dangerous dungeon populations, and carried matter through the dungeon's cycles. A settled district still needed formal expeditions into old drains, closed galleries, and newly restless chambers. diff --git a/Delvehold/index.md b/Delvehold/index.md index 2464273..4327026 100644 --- a/Delvehold/index.md +++ b/Delvehold/index.md @@ -25,7 +25,7 @@ description: One persistent world, experienced from opposite sides of the contra

HOLD

Build the warm machine waiting above.

HOLD is a cozy magical-industry tycoon game. Begin with one mage doing every useful task by hand, encode those gestures into runes, and compose them into spell engines that grow a workshop into shared infrastructure.

-

Your workshop is one shard in a neighborhood graph. Trade with the players packed around you, share scarce infrastructure, and issue the contracts that become somebody else's night underground.

+

Your workshop joins one Hold inside a living Vault. Trade with familiar players, rely upon local institutions, build relationships with the delvers you hire, and issue the contracts that become somebody else's night underground.