How to create a complete original system package
One installable ZIP with working actor/token sheets, item types, stored initiative and three original compendium packs.
What the single system ZIP provides
Build Complete Example Expedition on Gravewright 0.3.0. This is a functional original system package: complete character/NPC sheet replacements on actor.sheet and token.sheet, a saved stamina field, gear/spell item types, native initiative and three installed compendium packs. No separately installed module or server-rules companion is required. It is a small teaching system, not a complete implementation of a published RPG.
The sheet code is reused from the working editable system package, and the original JSON is derived from installed system compendiums. This guide prints every functional file, so you can build from an empty folder. The package is GPL-3.0-only; the MIT notices for the derived original collection remain intact.
1. Create the directory and understand the contract
Download the complete system source ZIP
complete-expedition/
manifest.json
main.mjs
styles.css
compendiums/expedition.json
LICENSE.txt
MIT-COMPENDIUM.txt
THIRD_PARTY_NOTICES.txt
README.mdCreate complete-expedition and its compendiums subdirectory. Save the exact files in section 5 as UTF-8. Copy LICENSE.txt unchanged from the ZIP. Do not replace that GPL license with the separate MIT notice. No npm build, Python app or host source modification is required.
The manifest ID example.complete-expedition is also the systemId of every catalog entry. character and npc must match system.actorTypes; gear and spell must match system.itemTypes. content.compendiums names the physical JSON file. The three permitted installed pack types are actor_pack, item_pack and spell_pack; the spell pack follows item import semantics, without adding spell rules to the core.
2. Package, install and create the campaign
From the source folder, use an existing Python interpreter and explicitly list the package files. On POSIX replace python with python3 or the path to the host virtual environment. Keep the output ZIP outside the source folder.
Set-Location -LiteralPath ./complete-expedition
python -m json.tool manifest.json
python -m json.tool compendiums/expedition.json
python -m zipfile -c ../complete-expedition-source.zip manifest.json main.mjs styles.css compendiums/expedition.json LICENSE.txt MIT-COMPENDIUM.txt THIRD_PARTY_NOTICES.txt README.md
python -m zipfile -l ../complete-expedition-source.zipAs installation owner, open Inside → Systems → Install system → Install from ZIP, review and select complete-expedition-source.zip, then click Install. Use Systems, because this manifest declares system. Local upload needs explicit trust but no remote catalog signature.
Create a new disposable campaign and choose Complete Example Expedition as its system. Enter the table as GM and verify the exact 1.0.0 version in Gravewright → Extensions → Table modules. Installing a system does not switch existing campaigns automatically.
In Actors, create one Character and one NPC, then open both sheets. You should see the real Example Expedition sheet form with Stamina, Save and Reload server value. Native window controls remain supplied by the host.
3. Import and edit the original content
Open Compendiums. Under Complete Expedition Library, inspect Scouts, Equipment and Signals. As GM, read then import Harbor Scout, Signal Rope and Blue Glow. The entry bodies belong to this system; the API returns those installed collection documents, not an invented envelope with a required kind field. Import creates campaign copies only when requested. Reopening the campaign does not silently import duplicates.
Open the imported Harbor Scout actor. Its stored data starts with stamina 8, role Watch keeper and combat.initiative 3. Change Stamina to 17, click Save, close and reopen. The value must remain 17; role and combat must still be present. The sheet copies the existing data and changes only stamina, sending both version and sheetVersion read from the same sheet. Test another session with a stale version; the form reloads on conflict and asks for a review rather than forcing the write.
Create linked and independent tokens for the imported actor. A linked token saves actor data. An independent token uses its own tokenId snapshot and sheetVersion. Changing its stamina must leave the original actor unchanged. With a read-only player, editing and saving are disabled; verify a direct attempted write is also rejected by the server.
The gear and spell are original item data, not implemented equipment or spell automation. This package reuses the host item interface and does not replace item.sheet with an incomplete custom editor. You may add a complete package-owned item editor later using the item surface example after checking the target API.
4. Roll initiative with the actual stored modifier
Open a scene, add Harbor Scout or its token to the native initiative tracker and use Roll initiative. The package provides formula 1d20+@combat.initiative, so this imported actor rolls 1d20+3, a value from 4 through 23. The core resolves this numeric path from the actor or independent snapshot and evaluates the native dice expression. No system-specific calculation derives it from another attribute.
A freshly created actor has no catalog data unless you import it. A missing combat.initiative path resolves to 0, so that actor rolls 1d20. Its form displays stamina 10 as an initial UI default until the user saves. The form does not expose initiative editing; to add that field, implement its input, validation and revision-preserving save inside this package. manualInput number describes numeric manual initiative when applicable; the configured formula selects the native roll input. There is no required external provider.
5. Complete package files
manifest.json
{
"id": "example.complete-expedition",
"name": "Complete Example Expedition",
"version": "1.0.0",
"sdk": {
"requires": ">=1.0.0 <2.0.0",
"tested": "1.0.0"
},
"description": "One original system with editable actor/token sheets, initiative and three installed content packs.",
"author": "Gravewright documentation examples",
"license": "GPL-3.0-only",
"entry": "main.mjs",
"system": {
"actorTypes": [
{
"id": "character",
"label": "Character"
},
{
"id": "npc",
"label": "NPC"
}
],
"itemTypes": [
{
"id": "gear",
"label": "Gear"
},
{
"id": "spell",
"label": "Spell"
}
],
"combat": {
"formula": "1d20+@combat.initiative",
"manualInput": "number",
"showRoll": true
}
},
"content": {
"compendiums": [
"compendiums/expedition.json"
]
}
}main.mjs
export default {
start({styles}) { styles.use('styles.css'); },
register({register}) {
const sheet={
mode:'replace',types:['character','npc'],
async mount({root,context,api,signal,onDispose}) {
const doc=root.ownerDocument;
const form=doc.createElement('form');form.className='example-panel';
form.setAttribute('aria-label','Example Expedition sheet');
const title=doc.createElement('h2');
const label=doc.createElement('label');label.textContent='Stamina (0–100)';
const input=doc.createElement('input');input.type='number';input.min='0';input.max='100';
label.append(input);
const saveButton=doc.createElement('button');saveButton.type='submit';saveButton.textContent='Save';
const reloadButton=doc.createElement('button');reloadButton.type='button';reloadButton.textContent='Reload server value';
input.disabled=true;saveButton.disabled=true;reloadButton.disabled=true;
const status=doc.createElement('p');status.setAttribute('role','status');
form.append(title,label,saveButton,reloadButton,status);root.append(form);
let current,dirty=false,pending;
const token=context.tokenId ? {tokenId:context.tokenId} : {};
async function load() {
input.disabled=true;saveButton.disabled=true;reloadButton.disabled=true;
try {
const sheet=await api.actors.sheet(context.actorId,token,{signal});
if(signal.aborted) return;
current=sheet;title.textContent=sheet.name;
input.value=String(sheet.data.stamina ?? 10);
input.disabled=!sheet.canEdit;saveButton.disabled=!sheet.canEdit;
dirty=false;
} finally { if(!signal.aborted) reloadButton.disabled=false; }
}
function save() {
if(pending) return pending;
if(!dirty || !current?.canEdit) return Promise.resolve();
const stamina=Number(input.value);
if(input.value.trim()==='' || !Number.isInteger(stamina) || stamina<0 || stamina>100) {
status.textContent='Enter an integer from 0 to 100.';
return Promise.reject(new Error('Invalid stamina'));
}
const payload={actorId:context.actorId,name:current.name,version:current.version,
sheetVersion:current.sheetVersion,data:{...current.data,stamina},
...(current.tokenId ? {tokenId:current.tokenId} : {})};
const requestId=crypto.randomUUID();
input.disabled=true;saveButton.disabled=true;reloadButton.disabled=true;
pending=(async()=>{
try {
await api.actors.saveSheet(payload,{requestId,signal});
if(signal.aborted) return;
await load();status.textContent='Saved.';
} catch(error) {
if(!signal.aborted) {
if(error.code==='conflict') {
await load();
status.textContent='Conflict: server value loaded. Review it before editing again.';
} else status.textContent=error.code || 'Save failed. Reload before retrying.';
}
throw error;
} finally {
pending=undefined;
if(!signal.aborted){input.disabled=!current?.canEdit;saveButton.disabled=!current?.canEdit;reloadButton.disabled=false;}
}
})();
return pending;
}
input.addEventListener('input',()=>{dirty=true;},{signal});
form.addEventListener('submit',event=>{event.preventDefault();void save().catch(()=>{});},{signal});
reloadButton.addEventListener('click',()=>{
void load().then(()=>{if(!signal.aborted)status.textContent='Server value loaded.';})
.catch(error=>{if(!signal.aborted)status.textContent=error.code||'unavailable';});
},{signal});
root.closest('.gw-window')?.addEventListener('gravewright:sheet-flush',event=>{
if(pending || dirty) event.detail.waitUntil(save());
},{signal});
onDispose(()=>form.remove());
await load();
}
};
register('actor.sheet',sheet);
register('token.sheet',sheet);
},
stop() {}
};styles.css
.gw-module-root[data-module-id="example.complete-expedition"] .example-panel { box-sizing: border-box; padding: var(--space-4); color: var(--text); background: var(--surface); font: 14px/1.5 system-ui, sans-serif; }
.gw-module-root[data-module-id="example.complete-expedition"] .example-panel h2 { font-size: 18px; margin: var(--space-0) var(--space-0) var(--space-3); }
.gw-module-root[data-module-id="example.complete-expedition"] .example-panel label { display: block; margin-bottom: var(--space-3); }
.gw-module-root[data-module-id="example.complete-expedition"] .example-panel input { display: block; max-width: 100%; box-sizing: border-box; }
.gw-module-root[data-module-id="example.complete-expedition"] .example-panel :is(input,button) { font: inherit; color: inherit; background: var(--surface); padding: var(--space-2); margin: var(--space-1) var(--space-2) var(--space-1) var(--space-0); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.gw-module-root[data-module-id="example.complete-expedition"] .example-panel :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.gw-module-root[data-module-id="example.complete-expedition"] .example-panel [role=status] { overflow-wrap: anywhere; }compendiums/expedition.json
{
"name": "Complete Expedition Library",
"packs": [
{
"id": "scouts",
"label": "Scouts",
"type": "actor_pack",
"entries": [
{
"id": "harbor-scout",
"name": "Harbor Scout",
"type": "character",
"systemId": "example.complete-expedition",
"data": {
"stamina": 8,
"role": "Watch keeper",
"combat": {
"initiative": 3
}
}
}
]
},
{
"id": "equipment",
"label": "Equipment",
"type": "item_pack",
"entries": [
{
"id": "signal-rope",
"name": "Signal Rope",
"type": "gear",
"systemId": "example.complete-expedition",
"data": {
"length": 12,
"description": "An original training rope with blue knots."
}
}
]
},
{
"id": "signals",
"label": "Signals",
"type": "spell_pack",
"entries": [
{
"id": "blue-glow",
"name": "Blue Glow",
"type": "spell",
"systemId": "example.complete-expedition",
"data": {
"color": "blue",
"duration": 1,
"description": "An original fictional signal, with no host automation."
}
}
]
}
]
}MIT-COMPENDIUM.txt
MIT License
Copyright (c) 2026 Gravewright contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.THIRD_PARTY_NOTICES.txt
Complete Example Expedition notices
main.mjs derives from the Gravewright documentation Example Expedition sheet.
The system package and this derivative sheet are GPL-3.0-only. The complete GPL
license is in LICENSE.txt. Preserve source availability and applicable notices.
compendiums/expedition.json derives from the original Beacon Expedition Library
example in tutorial-compendiums.mjs. Its original content was published under
the MIT License, Copyright (c) 2026 Gravewright contributors. The full original
MIT notice is preserved in MIT-COMPENDIUM.txt. Changes here update systemId and
the collection name and add the original numeric combat.initiative example.
No rulebook, proprietary catalog, licensed game branding or external artwork is
included. Gear and spell entries are original fictional data, without automated
game rules. English interface labels remain those of the demonstrated sheet.README.md
# Complete Example Expedition
Target: Gravewright 0.3.0, SDK 1.x. Package ID: example.complete-expedition.
This single system ZIP provides working actor/token sheet replacements, character
and NPC actor types, gear and spell item types, native initiative configuration,
and actor_pack/item_pack/spell_pack content in compendiums/expedition.json.
As owner, review and install through Systems, Install system, Install from ZIP.
Create a disposable campaign with Complete Example Expedition as its system.
As GM, open Compendiums and import Harbor Scout, Signal Rope and Blue Glow.
The imported actor has stamina 8 and combat.initiative 3. Open its sheet, save
Stamina 17, close and reopen. Add it or a token to a scene's initiative tracker
and roll initiative: 1d20+3 yields 4 to 23. A new empty character has no stored
initiative modifier, so the native resolver uses zero. The displayed stamina
default of 10 is only a form default until saved. No derived game rules run.
Linked token sheets share actor data; independent tokens use their snapshots.
Item/spell content imports through native items; this package does not provide
a custom item editor, spell rules or equipment automation. Languages use the
separate installation-wide language-package workflow, not system.locales.
For changes, increment version, build a new ZIP, install it and explicitly select
the new release. Never replace installed bytes. Imported documents remain copies.
See THIRD_PARTY_NOTICES.txt, MIT-COMPENDIUM.txt and LICENSE.txt before reuse.LICENSE.txt
Copy the complete unmodified GNU GPL version 3 license included in the ZIP. The full separate MIT grant for the JSON source is printed in MIT-COMPENDIUM.txt above and must remain with the derived collection.
6. Verify upgrades, language choices and deactivation
Keep the system ID and pack/entry IDs stable. For a change, increment version to 1.0.1, rebuild, install through Systems and select the exact release for the campaign. Test old characters, both token modes, saved stamina and initiative. Already imported documents are copies and are not rewritten by catalog updates. Never edit installed extracted files or replace the same version with different bytes.
Language selection is an installation-wide package plus a per-account preference; do not add locales to this system manifest. Follow the language tutorial separately. Close the sheet while saving, test invalid and stale drafts, deactivate or change the selected system only on a disposable campaign, and verify disposal and retained documents. A system without an active compatible sheet may leave its documents without a usable editor until reactivated; deactivation is not a data migration.
How to use AI to extend this complete system
Extend the complete example.complete-expedition package on Gravewright 0.3.0 with [one original rule or field]. Preserve the functional actor/token replacements, typed item registrations, formula 1d20+@combat.initiative, installed actor/item/spell packs and their matching systemId. Explain stored modifiers versus derived automation. Keep one installable system package and GPL/MIT notices. Deliver all changed files, exact versions, ZIP instructions and create/import/edit/save/reopen/token/initiative tests. Do not invent unsupported host APIs, add a separately installed Server Rules companion or ship proprietary rules/catalogs.