From 888095890b026fceece3298f2fc052a332eb9e30 Mon Sep 17 00:00:00 2001 From: Mathih13 Date: Sat, 10 Oct 2026 14:33:59 +0200 Subject: [PATCH 1/5] feat(packages): move the Reference Packages to the collection --- .gitignore | 12 +- .lyracore-cli-rev | 2 +- CONTEXT.md | 14 +- CONTRIBUTING.md | 2 +- .../lyracore-test-support/src/module_wasm.rs | 2 +- datascripts/tests/emit.test.ts | 4 +- .../tests/fixtures/fire-nova.expected.json | 2 +- .../spells.ts => tests/fixtures/fire_nova.ts} | 19 +-- docs/development-cli.md | 61 ++++---- docs/package-api.md | 2 +- docs/schema.md | 2 +- module/Cargo.toml | 2 + module/build.rs | 44 +++--- module/src/actor.rs | 8 +- module/src/group.rs | 5 +- module/src/lib.rs | 5 +- module/src/test_scan.rs | 8 +- module/tests/fixtures/test_fixture.rs | 1 + module/tests/package_account.rs | 8 +- module/tests/package_runtime_scripts.rs | 61 ++++---- module/tests/package_teardown.rs | 4 +- packages/README.md | 130 +++++++++--------- packages/example/src/mod.rs | 23 ---- packages/fire_nova/README.md | 23 ---- packages/fire_nova/data/.gitkeep | 0 packages/fire_nova/scripts/ember_echo.ts | 25 ---- 26 files changed, 189 insertions(+), 280 deletions(-) rename datascripts/{src/fire_nova/spells.ts => tests/fixtures/fire_nova.ts} (70%) create mode 100644 module/tests/fixtures/test_fixture.rs delete mode 100644 packages/example/src/mod.rs delete mode 100644 packages/fire_nova/README.md delete mode 100644 packages/fire_nova/data/.gitkeep delete mode 100644 packages/fire_nova/scripts/ember_echo.ts diff --git a/.gitignore b/.gitignore index b5a77ab6b..0500ffdef 100644 --- a/.gitignore +++ b/.gitignore @@ -55,15 +55,9 @@ __pycache__/ /datascripts/node_modules/ /datascripts/runtime-scripts/node_modules/ -# Packages installed on a host (`lyracore packages add`) are local to that checkout. Unknown -# Package directories stay out of Git status. Source files in the committed first-party Packages -# remain visible. +# Installed Packages are local to this checkout. Reference Packages live in the collection. /packages/* -!/packages/example/ -!/packages/fire_nova/ +!/packages/README.md -# What `lyracore packages build` writes into a first-party Package: Package Deltas, Script Artifacts -# and their Build Identity sidecars. Here they are output the sources next to them reproduce, so -# none is committed. A Package Delta is never committed anywhere (licensing firewall); the Official -# Package Collection does commit Script Artifacts, so an Operator installs prebuilt Lua without Bun. +# Generated Package output stays local. The collection may commit Script Artifacts, never Deltas. /packages/*/data/.generated/ diff --git a/.lyracore-cli-rev b/.lyracore-cli-rev index 5c13cdf7a..094bc10d5 100644 --- a/.lyracore-cli-rev +++ b/.lyracore-cli-rev @@ -1 +1 @@ -091b502a1020e483df4322881bd808e0e6c958db +950a03ff593722109482ab6dd496e94148ecf709 diff --git a/CONTEXT.md b/CONTEXT.md index 5032a3fe2..d7c38ebf5 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -961,15 +961,15 @@ rows, so the Package's tables must be empty first. `lyracore packages disable` r _Avoid_: uninstall, package wipe, cleanup **Reference Package**: -The maintained, minimal Package at `packages/example/`, committed to the LyraCore repo and present in every checkout. It doubles as living documentation for a Package's shape and is the template `lyracore packages new` copies and renames. It is deliberately inert: Rust-only, one commented hook pattern, no gameplay behavior. +One rung of the maintained example ladder in the [Official Package Collection](https://github.com/LyraCoreProject/packages#packages): `example-script`, `example-client`, `example-data`, `example-rust` or `example-all`. Each adds a small welcome at one level of the Package API. `lyracore packages new NAME [--from RUNG]` copies and renames one at the collection tag matching the checkout's Package API version. The default is `example-script`. Core ships no enabled Reference Package. _Avoid_: template package, sample package **Package Source**: -Where an installed Package was copied from: a local folder on the Operator's machine, a Git Package Source, or an Official Package Source. A scaffolded Package (`lyracore packages new`) has none. It was not copied from anywhere the Operator chose, so its Provenance Stamp records a scaffold origin instead. +Where an installed Package was copied from: a local folder on the Operator's machine, a Git Package Source, or an Official Package Source. A scaffolded Package records a scaffold origin, its Reference Package rung and the collection revision. The author owns that copy; `packages update` does not replace it. _Avoid_: origin, upstream, repo **Git Package Source**: -A repository whose root is one Package, named by a URL. `lyracore packages add ` clones it and installs a copy of its tree, without the `.git`. An installed Package is never a working copy, so `lyracore packages update` re-clones rather than pulling. A Package installed this way is Git-backed, and it is the only kind `update` can advance. +A repository whose root is one Package, named by a URL. `lyracore packages add ` clones it and installs a copy of its tree, without the `.git`. An installed Package is never a working copy, so `lyracore packages update` re-clones rather than pulling. A Package installed this way is Git-backed, and `packages update` can advance it. _Avoid_: git remote, upstream repo, checkout **Official Package Collection**: @@ -977,15 +977,15 @@ The one repository, `LyraCoreProject/packages`, that holds several first-party P _Avoid_: registry, package repository, marketplace **Official Package Source**: -The top-level directory of the Official Package Collection that a bare `lyracore packages add ` installed. Its Provenance Stamp records the collection's URL and the Recorded Revision the directory was resolved at, the same way a Git Package Source records its repository and commit. The commit is pinned at install time: `lyracore packages update` refuses this kind by name, so a later commit to the collection cannot silently change what is installed. +The top-level directory of the Official Package Collection that a bare `lyracore packages add ` installed. Its Provenance Stamp records the collection's URL and the Recorded Revision the directory was resolved at, the same way a Git Package Source records its repository and commit. The checkout's Package API version selects a collection tag. `packages update` resolves that tag again and records the new commit after the update passes its checks. _Avoid_: registry entry, published package **Recorded Revision**: -The exact commit a Git Package Source or an Official Package Source was installed at, held in its Provenance Stamp. `lyracore packages update` advances a Git Package Source from this rather than from whatever the repository's branch points at now, so it can name both commits when it reports or restores. +The exact commit a Git Package Source, Official Package Source or Reference Package was copied from, held in its Provenance Stamp. `lyracore packages update` advances a Git Package Source from this rather than from whatever the repository's branch points at now, so it can name both commits when it reports or restores. _Avoid_: version, tag, pin **Provenance Stamp**: -The record `lyracore packages add` writes inside an installed Package: its Package Source, its Content Identity at install time, when it was installed, and, for a Git or Official Package Source, its Recorded Revision. A Package without one is still a Package; only its history is unknown. +The record `lyracore packages add` writes inside an installed Package: its Package Source, its Content Identity at install time, when it was installed, and, for a Git or Official Package Source or a scaffold, its Recorded Revision. A scaffold also records its Reference Package rung. A Package without one is still a Package; only its history is unknown. _Avoid_: manifest, lockfile, metadata **Content Identity**: @@ -1005,7 +1005,7 @@ The maintained, minimal Datascript at `datascripts/src/reference.ts`. It names r _Avoid_: sample script, test script **Authoring Library**: -The typed API at `datascripts/lib/` that a Datascript writes against: `data.spell(id)`, `.clone(newId)`, `.set(field, value)`, `.effect(0 | 1 | 2)`, and a `run(package, script)` that emits one Package Delta. It reads the Base Snapshot and refuses exactly what the artifact parser refuses, so a Datascript fails at author time rather than at import. Its source lives outside the Package folder; only the generated artifact goes inside. +The typed API at `datascripts/lib/` that a Datascript writes against: `data.spell(id)`, `.clone(newId)`, `.set(field, value)`, `.effect(0 | 1 | 2)`, and a `run(package, script)` that emits one Package Delta. It reads the Base Snapshot and refuses exactly what the artifact parser refuses, so a Datascript fails at author time rather than at import. The library lives in Core. A Package carries its Datascripts in `datascripts/`; Core's legacy `datascripts/src//` path also works. _Avoid_: SDK, framework, DSL, builder API **Base Snapshot**: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c1fabde0c..31d8848c6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,7 +42,7 @@ first commit. A maintainer then marks the issue `in progress`. ## Building on top New content and behaviour beyond 1.12.1 live in a Package. `lyracore packages new ` -scaffolds one from the reference Package. [`docs/package-api.md`](docs/package-api.md) lists what +scaffolds one from the Reference Package ladder in the collection. Use `--from` to choose a rung. [`docs/package-api.md`](docs/package-api.md) lists what a Package may call, and the build fails on anything else. The Official Package Collection, [LyraCoreProject/packages](https://github.com/LyraCoreProject/packages), takes pull requests under its own contributing guide. diff --git a/crates/lyracore-test-support/src/module_wasm.rs b/crates/lyracore-test-support/src/module_wasm.rs index 58f16eda5..68a5196a7 100644 --- a/crates/lyracore-test-support/src/module_wasm.rs +++ b/crates/lyracore-test-support/src/module_wasm.rs @@ -17,7 +17,7 @@ fn build_module_bytes() -> Vec { "--release", "--target", "wasm32-unknown-unknown", - "--features=debug_reducers", + "--features=debug_reducers,package_test_fixture", ], ); let bytes = std::fs::read(&wasm).expect("the test Module output is missing"); diff --git a/datascripts/tests/emit.test.ts b/datascripts/tests/emit.test.ts index 18b1fd210..9477498b3 100644 --- a/datascripts/tests/emit.test.ts +++ b/datascripts/tests/emit.test.ts @@ -7,7 +7,7 @@ // // LYRACORE_BASE_SNAPSHOT=datascripts/tests/fixtures/base-snapshot.json \ // LYRACORE_PACKAGES_ROOT= \ -// bun run datascripts/src/fire_nova/spells.ts +// bun run datascripts/tests/fixtures/fire_nova.ts // // The Datascript is run as a SUBPROCESS rather than imported, because the source hash it records is // the entry script's own digest — which only means what it says when the entry script is the @@ -19,7 +19,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; const REPO = join(import.meta.dir, "..", ".."); -const DATASCRIPT = join(REPO, "datascripts", "src", "fire_nova", "spells.ts"); +const DATASCRIPT = join(import.meta.dir, "fixtures", "fire_nova.ts"); const SNAPSHOT = join(import.meta.dir, "fixtures", "base-snapshot.json"); const FROZEN = join(import.meta.dir, "fixtures", "fire-nova.expected.json"); diff --git a/datascripts/tests/fixtures/fire-nova.expected.json b/datascripts/tests/fixtures/fire-nova.expected.json index 72a86e879..d13f8bf09 100644 --- a/datascripts/tests/fixtures/fire-nova.expected.json +++ b/datascripts/tests/fixtures/fire-nova.expected.json @@ -1 +1 @@ -{"version":1,"package":"fire_nova","source_hash":"f3dd07e4c214bbe5384623b1b0be7674c6cea25ae986c639d76ff64bd98df64d","claims":[{"table":"game_spell","key":{"spell_id":6000100},"operation":"insert","fields":{"attributes":{"type":"u32","value":0},"aura_interrupt":{"type":"u16","value":1},"cast_flags":{"type":"u32","value":0},"cast_time_ms":{"type":"u32","value":0},"cooldown_ms":{"type":"u32","value":8000},"cost":{"type":"u32","value":40},"dispel_type":{"type":"u8","value":1},"duration_ms":{"type":"u32","value":6000},"family_flags":{"type":"u64","value":"1024"},"family_name":{"type":"u8","value":3},"gcd_ms":{"type":"u32","value":1500},"is_negative":{"type":"bool","value":true},"max_level":{"type":"u8","value":0},"max_stacks":{"type":"u8","value":0},"mechanic":{"type":"u8","value":0},"name":{"type":"string","value":"Fire Nova (Rank 1)"},"power_type":{"type":"u8","value":0},"proc_chance":{"type":"u8","value":0},"proc_charges":{"type":"u8","value":0},"proc_flags":{"type":"u32","value":0},"range_yd":{"type":"u32","value":0},"school_mask":{"type":"u8","value":4},"spell_level":{"type":"u8","value":12},"stances":{"type":"u8","value":0}}},{"table":"game_spell","key":{"spell_id":6000101},"operation":"insert","fields":{"attributes":{"type":"u32","value":0},"aura_interrupt":{"type":"u16","value":1},"cast_flags":{"type":"u32","value":0},"cast_time_ms":{"type":"u32","value":0},"cooldown_ms":{"type":"u32","value":8000},"cost":{"type":"u32","value":80},"dispel_type":{"type":"u8","value":1},"duration_ms":{"type":"u32","value":6000},"family_flags":{"type":"u64","value":"1024"},"family_name":{"type":"u8","value":3},"gcd_ms":{"type":"u32","value":1500},"is_negative":{"type":"bool","value":true},"max_level":{"type":"u8","value":0},"max_stacks":{"type":"u8","value":0},"mechanic":{"type":"u8","value":0},"name":{"type":"string","value":"Fire Nova (Rank 2)"},"power_type":{"type":"u8","value":0},"proc_chance":{"type":"u8","value":0},"proc_charges":{"type":"u8","value":0},"proc_flags":{"type":"u32","value":0},"range_yd":{"type":"u32","value":0},"school_mask":{"type":"u8","value":4},"spell_level":{"type":"u8","value":22},"stances":{"type":"u8","value":0}}},{"table":"game_spell","key":{"spell_id":6000102},"operation":"insert","fields":{"attributes":{"type":"u32","value":0},"aura_interrupt":{"type":"u16","value":1},"cast_flags":{"type":"u32","value":0},"cast_time_ms":{"type":"u32","value":0},"cooldown_ms":{"type":"u32","value":8000},"cost":{"type":"u32","value":145},"dispel_type":{"type":"u8","value":1},"duration_ms":{"type":"u32","value":6000},"family_flags":{"type":"u64","value":"1024"},"family_name":{"type":"u8","value":3},"gcd_ms":{"type":"u32","value":1500},"is_negative":{"type":"bool","value":true},"max_level":{"type":"u8","value":0},"max_stacks":{"type":"u8","value":0},"mechanic":{"type":"u8","value":0},"name":{"type":"string","value":"Fire Nova (Rank 3)"},"power_type":{"type":"u8","value":0},"proc_chance":{"type":"u8","value":0},"proc_charges":{"type":"u8","value":0},"proc_flags":{"type":"u32","value":0},"range_yd":{"type":"u32","value":0},"school_mask":{"type":"u8","value":4},"spell_level":{"type":"u8","value":32},"stances":{"type":"u8","value":0}}},{"table":"game_spell","key":{"spell_id":6000103},"operation":"insert","fields":{"attributes":{"type":"u32","value":0},"aura_interrupt":{"type":"u16","value":1},"cast_flags":{"type":"u32","value":0},"cast_time_ms":{"type":"u32","value":0},"cooldown_ms":{"type":"u32","value":8000},"cost":{"type":"u32","value":240},"dispel_type":{"type":"u8","value":1},"duration_ms":{"type":"u32","value":6000},"family_flags":{"type":"u64","value":"1024"},"family_name":{"type":"u8","value":3},"gcd_ms":{"type":"u32","value":1500},"is_negative":{"type":"bool","value":true},"max_level":{"type":"u8","value":0},"max_stacks":{"type":"u8","value":0},"mechanic":{"type":"u8","value":0},"name":{"type":"string","value":"Fire Nova (Rank 4)"},"power_type":{"type":"u8","value":0},"proc_chance":{"type":"u8","value":0},"proc_charges":{"type":"u8","value":0},"proc_flags":{"type":"u32","value":0},"range_yd":{"type":"u32","value":0},"school_mask":{"type":"u8","value":4},"spell_level":{"type":"u8","value":42},"stances":{"type":"u8","value":0}}},{"table":"game_spell","key":{"spell_id":6000104},"operation":"insert","fields":{"attributes":{"type":"u32","value":0},"aura_interrupt":{"type":"u16","value":1},"cast_flags":{"type":"u32","value":0},"cast_time_ms":{"type":"u32","value":0},"cooldown_ms":{"type":"u32","value":8000},"cost":{"type":"u32","value":355},"dispel_type":{"type":"u8","value":1},"duration_ms":{"type":"u32","value":6000},"family_flags":{"type":"u64","value":"1024"},"family_name":{"type":"u8","value":3},"gcd_ms":{"type":"u32","value":1500},"is_negative":{"type":"bool","value":true},"max_level":{"type":"u8","value":0},"max_stacks":{"type":"u8","value":0},"mechanic":{"type":"u8","value":0},"name":{"type":"string","value":"Fire Nova (Rank 5)"},"power_type":{"type":"u8","value":0},"proc_chance":{"type":"u8","value":0},"proc_charges":{"type":"u8","value":0},"proc_flags":{"type":"u32","value":0},"range_yd":{"type":"u32","value":0},"school_mask":{"type":"u8","value":4},"spell_level":{"type":"u8","value":52},"stances":{"type":"u8","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000100,"effect_index":0},"operation":"insert","fields":{"base_points":{"type":"i32","value":33},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":1},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.5},"period_ms":{"type":"u32","value":0},"radius_yd":{"type":"f32","value":8.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":4},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000100,"effect_index":1},"operation":"insert","fields":{"base_points":{"type":"i32","value":4},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":129},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.0},"period_ms":{"type":"u32","value":3000},"radius_yd":{"type":"f32","value":0.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":1},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000101,"effect_index":0},"operation":"insert","fields":{"base_points":{"type":"i32","value":71},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":1},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.5},"period_ms":{"type":"u32","value":0},"radius_yd":{"type":"f32","value":8.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":4},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000101,"effect_index":1},"operation":"insert","fields":{"base_points":{"type":"i32","value":4},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":129},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.0},"period_ms":{"type":"u32","value":3000},"radius_yd":{"type":"f32","value":0.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":1},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000102,"effect_index":0},"operation":"insert","fields":{"base_points":{"type":"i32","value":129},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":1},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.5},"period_ms":{"type":"u32","value":0},"radius_yd":{"type":"f32","value":8.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":4},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000102,"effect_index":1},"operation":"insert","fields":{"base_points":{"type":"i32","value":4},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":129},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.0},"period_ms":{"type":"u32","value":3000},"radius_yd":{"type":"f32","value":0.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":1},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000103,"effect_index":0},"operation":"insert","fields":{"base_points":{"type":"i32","value":224},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":1},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.5},"period_ms":{"type":"u32","value":0},"radius_yd":{"type":"f32","value":8.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":4},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000103,"effect_index":1},"operation":"insert","fields":{"base_points":{"type":"i32","value":4},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":129},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.0},"period_ms":{"type":"u32","value":3000},"radius_yd":{"type":"f32","value":0.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":1},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000104,"effect_index":0},"operation":"insert","fields":{"base_points":{"type":"i32","value":340},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":1},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.5},"period_ms":{"type":"u32","value":0},"radius_yd":{"type":"f32","value":8.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":4},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000104,"effect_index":1},"operation":"insert","fields":{"base_points":{"type":"i32","value":4},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":129},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.0},"period_ms":{"type":"u32","value":3000},"radius_yd":{"type":"f32","value":0.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":1},"trigger_spell":{"type":"u32","value":0}}}]} \ No newline at end of file +{"version":1,"package":"fire_nova","source_hash":"e84139afd0f845c0c590983687564f8116889e7151fc1a52616192e11c806bd5","claims":[{"table":"game_spell","key":{"spell_id":6000100},"operation":"insert","fields":{"attributes":{"type":"u32","value":0},"aura_interrupt":{"type":"u16","value":1},"cast_flags":{"type":"u32","value":0},"cast_time_ms":{"type":"u32","value":0},"cooldown_ms":{"type":"u32","value":8000},"cost":{"type":"u32","value":40},"dispel_type":{"type":"u8","value":1},"duration_ms":{"type":"u32","value":6000},"family_flags":{"type":"u64","value":"1024"},"family_name":{"type":"u8","value":3},"gcd_ms":{"type":"u32","value":1500},"is_negative":{"type":"bool","value":true},"max_level":{"type":"u8","value":0},"max_stacks":{"type":"u8","value":0},"mechanic":{"type":"u8","value":0},"name":{"type":"string","value":"Fire Nova (Rank 1)"},"power_type":{"type":"u8","value":0},"proc_chance":{"type":"u8","value":0},"proc_charges":{"type":"u8","value":0},"proc_flags":{"type":"u32","value":0},"range_yd":{"type":"u32","value":0},"school_mask":{"type":"u8","value":4},"spell_level":{"type":"u8","value":12},"stances":{"type":"u8","value":0}}},{"table":"game_spell","key":{"spell_id":6000101},"operation":"insert","fields":{"attributes":{"type":"u32","value":0},"aura_interrupt":{"type":"u16","value":1},"cast_flags":{"type":"u32","value":0},"cast_time_ms":{"type":"u32","value":0},"cooldown_ms":{"type":"u32","value":8000},"cost":{"type":"u32","value":80},"dispel_type":{"type":"u8","value":1},"duration_ms":{"type":"u32","value":6000},"family_flags":{"type":"u64","value":"1024"},"family_name":{"type":"u8","value":3},"gcd_ms":{"type":"u32","value":1500},"is_negative":{"type":"bool","value":true},"max_level":{"type":"u8","value":0},"max_stacks":{"type":"u8","value":0},"mechanic":{"type":"u8","value":0},"name":{"type":"string","value":"Fire Nova (Rank 2)"},"power_type":{"type":"u8","value":0},"proc_chance":{"type":"u8","value":0},"proc_charges":{"type":"u8","value":0},"proc_flags":{"type":"u32","value":0},"range_yd":{"type":"u32","value":0},"school_mask":{"type":"u8","value":4},"spell_level":{"type":"u8","value":22},"stances":{"type":"u8","value":0}}},{"table":"game_spell","key":{"spell_id":6000102},"operation":"insert","fields":{"attributes":{"type":"u32","value":0},"aura_interrupt":{"type":"u16","value":1},"cast_flags":{"type":"u32","value":0},"cast_time_ms":{"type":"u32","value":0},"cooldown_ms":{"type":"u32","value":8000},"cost":{"type":"u32","value":145},"dispel_type":{"type":"u8","value":1},"duration_ms":{"type":"u32","value":6000},"family_flags":{"type":"u64","value":"1024"},"family_name":{"type":"u8","value":3},"gcd_ms":{"type":"u32","value":1500},"is_negative":{"type":"bool","value":true},"max_level":{"type":"u8","value":0},"max_stacks":{"type":"u8","value":0},"mechanic":{"type":"u8","value":0},"name":{"type":"string","value":"Fire Nova (Rank 3)"},"power_type":{"type":"u8","value":0},"proc_chance":{"type":"u8","value":0},"proc_charges":{"type":"u8","value":0},"proc_flags":{"type":"u32","value":0},"range_yd":{"type":"u32","value":0},"school_mask":{"type":"u8","value":4},"spell_level":{"type":"u8","value":32},"stances":{"type":"u8","value":0}}},{"table":"game_spell","key":{"spell_id":6000103},"operation":"insert","fields":{"attributes":{"type":"u32","value":0},"aura_interrupt":{"type":"u16","value":1},"cast_flags":{"type":"u32","value":0},"cast_time_ms":{"type":"u32","value":0},"cooldown_ms":{"type":"u32","value":8000},"cost":{"type":"u32","value":240},"dispel_type":{"type":"u8","value":1},"duration_ms":{"type":"u32","value":6000},"family_flags":{"type":"u64","value":"1024"},"family_name":{"type":"u8","value":3},"gcd_ms":{"type":"u32","value":1500},"is_negative":{"type":"bool","value":true},"max_level":{"type":"u8","value":0},"max_stacks":{"type":"u8","value":0},"mechanic":{"type":"u8","value":0},"name":{"type":"string","value":"Fire Nova (Rank 4)"},"power_type":{"type":"u8","value":0},"proc_chance":{"type":"u8","value":0},"proc_charges":{"type":"u8","value":0},"proc_flags":{"type":"u32","value":0},"range_yd":{"type":"u32","value":0},"school_mask":{"type":"u8","value":4},"spell_level":{"type":"u8","value":42},"stances":{"type":"u8","value":0}}},{"table":"game_spell","key":{"spell_id":6000104},"operation":"insert","fields":{"attributes":{"type":"u32","value":0},"aura_interrupt":{"type":"u16","value":1},"cast_flags":{"type":"u32","value":0},"cast_time_ms":{"type":"u32","value":0},"cooldown_ms":{"type":"u32","value":8000},"cost":{"type":"u32","value":355},"dispel_type":{"type":"u8","value":1},"duration_ms":{"type":"u32","value":6000},"family_flags":{"type":"u64","value":"1024"},"family_name":{"type":"u8","value":3},"gcd_ms":{"type":"u32","value":1500},"is_negative":{"type":"bool","value":true},"max_level":{"type":"u8","value":0},"max_stacks":{"type":"u8","value":0},"mechanic":{"type":"u8","value":0},"name":{"type":"string","value":"Fire Nova (Rank 5)"},"power_type":{"type":"u8","value":0},"proc_chance":{"type":"u8","value":0},"proc_charges":{"type":"u8","value":0},"proc_flags":{"type":"u32","value":0},"range_yd":{"type":"u32","value":0},"school_mask":{"type":"u8","value":4},"spell_level":{"type":"u8","value":52},"stances":{"type":"u8","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000100,"effect_index":0},"operation":"insert","fields":{"base_points":{"type":"i32","value":33},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":1},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.5},"period_ms":{"type":"u32","value":0},"radius_yd":{"type":"f32","value":8.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":4},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000100,"effect_index":1},"operation":"insert","fields":{"base_points":{"type":"i32","value":4},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":129},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.0},"period_ms":{"type":"u32","value":3000},"radius_yd":{"type":"f32","value":0.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":1},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000101,"effect_index":0},"operation":"insert","fields":{"base_points":{"type":"i32","value":71},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":1},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.5},"period_ms":{"type":"u32","value":0},"radius_yd":{"type":"f32","value":8.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":4},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000101,"effect_index":1},"operation":"insert","fields":{"base_points":{"type":"i32","value":4},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":129},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.0},"period_ms":{"type":"u32","value":3000},"radius_yd":{"type":"f32","value":0.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":1},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000102,"effect_index":0},"operation":"insert","fields":{"base_points":{"type":"i32","value":129},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":1},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.5},"period_ms":{"type":"u32","value":0},"radius_yd":{"type":"f32","value":8.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":4},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000102,"effect_index":1},"operation":"insert","fields":{"base_points":{"type":"i32","value":4},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":129},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.0},"period_ms":{"type":"u32","value":3000},"radius_yd":{"type":"f32","value":0.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":1},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000103,"effect_index":0},"operation":"insert","fields":{"base_points":{"type":"i32","value":224},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":1},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.5},"period_ms":{"type":"u32","value":0},"radius_yd":{"type":"f32","value":8.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":4},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000103,"effect_index":1},"operation":"insert","fields":{"base_points":{"type":"i32","value":4},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":129},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.0},"period_ms":{"type":"u32","value":3000},"radius_yd":{"type":"f32","value":0.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":1},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000104,"effect_index":0},"operation":"insert","fields":{"base_points":{"type":"i32","value":340},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":1},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.5},"period_ms":{"type":"u32","value":0},"radius_yd":{"type":"f32","value":8.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":4},"trigger_spell":{"type":"u32","value":0}}},{"table":"game_spell_effect","key":{"spell_id":6000104,"effect_index":1},"operation":"insert","fields":{"base_points":{"type":"i32","value":4},"chain_targets":{"type":"u8","value":0},"die_sides":{"type":"i32","value":0},"effect_mechanic":{"type":"u8","value":0},"enters_combat":{"type":"bool","value":false},"kind":{"type":"u8","value":129},"p0":{"type":"i32","value":0},"p0_kind":{"type":"u8","value":255},"p1":{"type":"i32","value":0},"per_level":{"type":"f32","value":0.0},"period_ms":{"type":"u32","value":3000},"radius_yd":{"type":"f32","value":0.0},"script_id":{"type":"u32","value":0},"target":{"type":"u8","value":1},"trigger_spell":{"type":"u32","value":0}}}]} \ No newline at end of file diff --git a/datascripts/src/fire_nova/spells.ts b/datascripts/tests/fixtures/fire_nova.ts similarity index 70% rename from datascripts/src/fire_nova/spells.ts rename to datascripts/tests/fixtures/fire_nova.ts index 9307245d3..813d5cdbf 100644 --- a/datascripts/src/fire_nova/spells.ts +++ b/datascripts/tests/fixtures/fire_nova.ts @@ -1,21 +1,4 @@ -// Fire Nova: the worked example of authoring spells as a Package Delta. -// -// It clones a real spell, changes what it needs, loops a rank ladder, and emits one artifact. Every -// column below is named the way the Module declares it, so a schema move fails the typecheck here -// rather than at import. -// -// Run it with `lyracore packages build`, or directly: -// -// bun run datascripts/src/fire_nova/spells.ts -// -// The artifact lands at `packages/fire_nova/data/.generated/spell.json`. Only THIS file is -// committed: the artifact is derived, and regenerating it is one command. -// -// # Why a clone -// -// A Package may not invent a spell out of nothing — an insert has to carry every column, and half a -// row is not a spell. Cloning a real one gives a complete, coherent starting point, and every column -// the author does not name keeps the source's value. +// Five spell ranks for the Datascript emission and Rust interop tests. Uses a synthetic Base Snapshot. import { run } from "../../lib/index.ts"; diff --git a/docs/development-cli.md b/docs/development-cli.md index 2ddc2f6f3..e0fe343bb 100644 --- a/docs/development-cli.md +++ b/docs/development-cli.md @@ -66,7 +66,7 @@ lyracore packages config NAME [KEY [VALUE]] [--new] lyracore packages disable NAME [--yes] lyracore packages enable NAME lyracore packages list -lyracore packages new NAME +lyracore packages new NAME [--from RUNG] lyracore packages remove NAME [--yes] lyracore packages replay [DATABASE ...] [--check] [--yes] [--force-all] [--client-data PATH] lyracore packages update [NAME] [--yes] @@ -99,7 +99,7 @@ lyracore update | `packages disable` | run Package Teardown on every Shard, then move the Package out of the build's sight, keeping it on disk | | `packages enable` | move a disabled Package back into the build | | `packages list` | every installed Package: enabled or disabled, where it came from, and whether it has drifted | -| `packages new` | scaffold a new Package offline, by copying and renaming the reference Package this checkout ships | +| `packages new` | copy a Reference Package from the collection tag matching this checkout's Package API | | `packages remove` | delete a disabled Package, after a confirmation and a check for local changes | | `packages replay` | reapply every enabled Package's claims and Runtime Scripts onto the named Shards, or the whole recorded fixture topology by default | | `packages update` | advance a Git-backed Package, or every one of them, to the repository's current commit | @@ -225,7 +225,8 @@ for you**, so a plain `./lyracore import` never asks twice. ./lyracore packages add https://host/greeter.git # clone a repository whose root is one Package ./lyracore packages add greeter # bare name: resolve from the Official Package Collection ./lyracore packages list -./lyracore packages new my-package # scaffold one from nothing but this checkout +./lyracore packages new my-package # start from example-script +./lyracore packages new my-package --from example-rust ``` A Package is a drop-in folder under `packages//`. `module/build.rs` discovers it and compiles @@ -295,18 +296,24 @@ Identity, whether the tree on disk still matches it (`clean` or `LOCALLY DRIFTED registers. A Package with no stamp — dropped into `packages/` by hand, or installed before this command existed — renders as unrecorded rather than failing the listing. -**`packages new NAME` scaffolds a Package with no network access and nothing external to review.** -It copies `packages/example/` — the maintained reference Package every LyraCore checkout ships, -including a fresh public clone — to `packages/NAME/`, renaming the reference's own identifiers into -the new name, then writes a Provenance Stamp recording a **scaffold** origin rather than a Package -Source (nothing external was installed, so there is nothing to name) and runs `preflight`. The same -name and shape refusals as `add` apply before anything is written. The reference Package is -Rust-only and inert. Its `src/mod.rs` carries one commented hook pattern, and the scaffold has no -`client/` directory. The printed next steps say to add `client/addons//` for addons or `client/mpq/` -for client-file overrides, and that `client sync` will pack them in once you do. Growing the Rust -half means wiring more hooks from the catalogue in `module/src/hooks.rs`, following the pattern -`packages/NAME/src/mod.rs` already shows. A scaffold ships no Datascript: the authoring toolchain -in `datascripts/` is checkout-wide today, not per-Package. +`packages new NAME [--from RUNG]` fetches a Reference Package from the Official Package Collection. +The checkout's Package API version selects its tag, as with `packages add`. The default is +`example-script`, which needs no Rust. Other rungs are `example-client`, `example-data`, +`example-rust` and `example-all`. A missing tag or rung leaves the Package Inventory unchanged. + +The command copies the source into `packages/NAME/`, renames Package names, Rust identifiers and +client filenames, prints its Trust Review and runs `preflight`. The Provenance Stamp keeps the +`scaffold` kind and records the chosen rung and exact collection revision. `packages update` +does not replace scaffolded code. + +Rungs with Runtime Scripts or Datascripts need a lowercase Package name of at most 64 characters. +Each `.