Build vault, memory, and Quartz operating surfaces
This commit is contained in:
parent
36783c14be
commit
6b62f17ccf
28 changed files with 1383 additions and 0 deletions
45
.epiphany/README.md
Normal file
45
.epiphany/README.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Kalsa Project Memory
|
||||
|
||||
This directory is Kalsa's bounded persistent-memory surface. It is deliberately
|
||||
smaller than a Persona or a full autonomous swarm workspace.
|
||||
|
||||
## Ownership
|
||||
|
||||
- `project-memory.md` is the human-inspectable source for durable project
|
||||
judgment and unresolved design pressure.
|
||||
- `state/project-memory.cc` is a typed CultCache state-ledger projection of that
|
||||
source for runtime inspection. It is generated, not hand-edited.
|
||||
- `sync-project-memory.ps1` rebuilds the projection from the Markdown source.
|
||||
|
||||
Neither surface owns lore truth. Canon remains in owner notes under `Kalsa/`,
|
||||
and project doctrine remains in `workshop/Direction and Constraints.md`.
|
||||
|
||||
## What Belongs Here
|
||||
|
||||
- decisions that should steer later work;
|
||||
- architectural or editorial lessons that prevent repeated mistakes;
|
||||
- unresolved questions that materially change future choices;
|
||||
- provenance anchors for those judgments;
|
||||
- explicit retirement of stale guidance.
|
||||
|
||||
Raw lore claims, generated prose, transcripts, task logs, pass-by-pass trivia,
|
||||
and site build state do not belong here. Pass details live under
|
||||
`workshop/deepening/`; only their durable lesson may be promoted.
|
||||
|
||||
## Updating Memory
|
||||
|
||||
1. Edit `project-memory.md`.
|
||||
2. Keep each active or open entry on one parseable list line using the existing
|
||||
identifier format.
|
||||
3. Update the `snapshot_at` timestamp.
|
||||
4. Rebuild and inspect the typed projection:
|
||||
|
||||
```powershell
|
||||
.\.epiphany\sync-project-memory.ps1
|
||||
& "F:\Projects\Epiphany\target\debug\epiphany-state-ledger-store.exe" status --store .\.epiphany\state\project-memory.cc
|
||||
```
|
||||
|
||||
The synchronizer uses Epiphany's typed `epiphany.state_ledger` CultCache entry.
|
||||
It does not invent a public Persona or a competing project-memory schema. If a
|
||||
dedicated shared project-memory contract later exists, migrate the projection
|
||||
through a reviewed tool and keep this source's identifiers and provenance.
|
||||
31
.epiphany/project-memory.md
Normal file
31
.epiphany/project-memory.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
title: Kalsa Project Memory
|
||||
status: active
|
||||
snapshot_at: 2026-08-22T10:35:00Z
|
||||
---
|
||||
|
||||
# Kalsa Project Memory
|
||||
|
||||
This file preserves judgment that should bend future work. It is not a lore
|
||||
database and does not grant canon status to any setting claim.
|
||||
|
||||
## Durable judgments
|
||||
|
||||
- `memory-authority-boundary` — Canonical notes under `Kalsa/` own setting truth; memory, workshop records, retrieval indexes, and Quartz output are evidence or projections only.
|
||||
- `memory-seed-witness` — The teenage export must remain byte-identical evidence. Repair happens in new canonical owner notes, never by laundering edits back into the witnesses.
|
||||
- `memory-depth-standard` — Institutional parity with AetheriaLore means comparable causal density across authority, reproduction, material support, contested legitimacy, dependencies, history, and playable consequences; page and word counts are diagnostics only.
|
||||
- `memory-language-history` — Prometheus's imposed logical forms should become living culture through inheritance, misuse, repair, ritual, class performance, and regional change rather than remaining frozen design specifications.
|
||||
- `memory-pass-discipline` — Deepening work uses one bounded target, one explicit owner map, one hostile or marginal perspective, one historical stress case, and one verified repair before moving on.
|
||||
- `memory-no-persona-yet` — Kalsa currently needs project memory and work discipline, not an invented public repo Persona. Persona birth requires a separate operator-approved identity and jurisdiction.
|
||||
|
||||
## Open questions
|
||||
|
||||
- `question-public-boundary` — Which Ghostlight-sensitive material belongs outside the public `Kalsa/` projection?
|
||||
- `question-present-date` — What is the canonical present date, and how far is it from the Fall?
|
||||
- `question-seed-contradictions` — Which seed contradictions should become in-world disagreement rather than resolved design?
|
||||
- `question-hosting-authority` — What custom domain and deployment authority should the Quartz site use?
|
||||
|
||||
## Retired judgments
|
||||
|
||||
None yet. Move an entry here when it stops steering work and say what replaced
|
||||
it; do not silently delete a once-load-bearing decision.
|
||||
1
.epiphany/state/project-memory.cc
Normal file
1
.epiphany/state/project-memory.cc
Normal file
|
|
@ -0,0 +1 @@
|
|||
搻cultcache.store.v1<EFBFBD>畫piphany.state_ledger畫piphany.state_ledger粳piphany.state_ledger.v1畫piphany.state_ledger揶{"schemaName":"epiphany.state_ledger","schemaVersion":"epiphany.state_ledger.v1","members":[]}㴝epiphany.state_ledger<EFBFBD>圲default畫piphany.state_ledger<EFBFBD>2026-08-22T10:35:08Z<EFBFBD> q𤦉epiphany.state_ledger.v0<EFBFBD>㟲2026-08-22T10:35:00Z搆roject-memory-judgment地ctive棜memory-authority-boundary: Canonical notes under `Kalsa/` own setting truth; memory, workshop records, retrieval indexes, and Quartz output are evidence or projections only.惰alsa-project-memory㟲2026-08-22T10:35:00Z搆roject-memory-judgment地ctive椄memory-seed-witness: The teenage export must remain byte-identical evidence. Repair happens in new canonical owner notes, never by laundering edits back into the witnesses.惰alsa-project-memory㟲2026-08-22T10:35:00Z搆roject-memory-judgment地ctive<EFBFBD>memory-depth-standard: Institutional parity with AetheriaLore means comparable causal density across authority, reproduction, material support, contested legitimacy, dependencies, history, and playable consequences; page and word counts are diagnostics only.惰alsa-project-memory㟲2026-08-22T10:35:00Z搆roject-memory-judgment地ctive殙memory-language-history: Prometheus's imposed logical forms should become living culture through inheritance, misuse, repair, ritual, class performance, and regional change rather than remaining frozen design specifications.惰alsa-project-memory㟲2026-08-22T10:35:00Z搆roject-memory-judgment地ctive棯memory-pass-discipline: Deepening work uses one bounded target, one explicit owner map, one hostile or marginal perspective, one historical stress case, and one verified repair before moving on.惰alsa-project-memory㟲2026-08-22T10:35:00Z搆roject-memory-judgment地ctive椔memory-no-persona-yet: Kalsa currently needs project memory and work discipline, not an invented public repo Persona. Persona birth requires a separate operator-approved identity and jurisdiction.惰alsa-project-memory㟲2026-08-22T10:35:00Z搆roject-memory-question寸pen揋question-public-boundary: Which Ghostlight-sensitive material belongs outside the public `Kalsa/` projection?惰alsa-project-memory㟲2026-08-22T10:35:00Z搆roject-memory-question寸pen揳question-present-date: What is the canonical present date, and how far is it from the Fall?惰alsa-project-memory㟲2026-08-22T10:35:00Z搆roject-memory-question寸pen敥question-seed-contradictions: Which seed contradictions should become in-world disagreement rather than resolved design?惰alsa-project-memory㟲2026-08-22T10:35:00Z搆roject-memory-question寸pen揟question-hosting-authority: What custom domain and deployment authority should the Quartz site use?惰alsa-project-memory
|
||||
109
.epiphany/sync-project-memory.ps1
Normal file
109
.epiphany/sync-project-memory.ps1
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
param(
|
||||
[string]$MemorySource,
|
||||
[string]$StatePath,
|
||||
[string]$EpiphanyRoot
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
if (-not $MemorySource) {
|
||||
$MemorySource = Join-Path $PSScriptRoot "project-memory.md"
|
||||
}
|
||||
if (-not $StatePath) {
|
||||
$StatePath = Join-Path $PSScriptRoot "state\project-memory.cc"
|
||||
}
|
||||
if (-not $EpiphanyRoot) {
|
||||
$EpiphanyRoot = Join-Path (Split-Path -Parent $repoRoot) "Epiphany"
|
||||
}
|
||||
|
||||
$sourceFull = [System.IO.Path]::GetFullPath($MemorySource)
|
||||
$stateFull = [System.IO.Path]::GetFullPath($StatePath)
|
||||
$epiphanyFull = [System.IO.Path]::GetFullPath($EpiphanyRoot)
|
||||
$stateHome = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "state"))
|
||||
|
||||
if (-not (Test-Path -LiteralPath $sourceFull -PathType Leaf)) {
|
||||
throw "Project memory source not found: $sourceFull"
|
||||
}
|
||||
if (-not $stateFull.StartsWith($stateHome + [System.IO.Path]::DirectorySeparatorChar, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "State path must remain inside $stateHome"
|
||||
}
|
||||
|
||||
$storeTool = Join-Path $epiphanyFull "target\debug\epiphany-state-ledger-store.exe"
|
||||
if (-not (Test-Path -LiteralPath $storeTool -PathType Leaf)) {
|
||||
throw "Epiphany state-ledger tool not found: $storeTool"
|
||||
}
|
||||
|
||||
$text = Get-Content -Raw -LiteralPath $sourceFull -Encoding utf8
|
||||
$timestampMatch = [regex]::Match($text, '(?m)^snapshot_at:\s*(?<timestamp>\S+)\s*$')
|
||||
if (-not $timestampMatch.Success) {
|
||||
throw "project-memory.md must contain a snapshot_at timestamp"
|
||||
}
|
||||
$timestamp = $timestampMatch.Groups['timestamp'].Value
|
||||
|
||||
$section = $null
|
||||
$records = @()
|
||||
foreach ($line in ($text -split "`r?`n")) {
|
||||
if ($line -eq "## Durable judgments") {
|
||||
$section = "judgment"
|
||||
continue
|
||||
}
|
||||
if ($line -eq "## Open questions") {
|
||||
$section = "question"
|
||||
continue
|
||||
}
|
||||
if ($line -match '^## ') {
|
||||
$section = $null
|
||||
continue
|
||||
}
|
||||
$entry = [regex]::Match($line, '^- `(?<id>[^`]+)` — (?<summary>.+)$')
|
||||
if ($entry.Success -and $section) {
|
||||
$records += [pscustomobject]@{
|
||||
Id = $entry.Groups['id'].Value
|
||||
Kind = $section
|
||||
Summary = $entry.Groups['summary'].Value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($records.Count -eq 0) {
|
||||
throw "No parseable project-memory entries found"
|
||||
}
|
||||
if (($records.Id | Sort-Object -Unique).Count -ne $records.Count) {
|
||||
throw "Project-memory identifiers must be unique"
|
||||
}
|
||||
|
||||
$stateDir = Split-Path -Parent $stateFull
|
||||
New-Item -ItemType Directory -Force -Path $stateDir | Out-Null
|
||||
$stageName = ([System.IO.Path]::GetFileNameWithoutExtension($stateFull) + "." + [guid]::NewGuid().ToString() + ".cc")
|
||||
$stagePath = Join-Path $stateDir $stageName
|
||||
|
||||
try {
|
||||
foreach ($record in $records) {
|
||||
$type = if ($record.Kind -eq "judgment") { "project-memory-judgment" } else { "project-memory-question" }
|
||||
$status = if ($record.Kind -eq "judgment") { "active" } else { "open" }
|
||||
$note = $record.Id + ": " + $record.Summary
|
||||
& $storeTool add-evidence --store $stagePath --ts $timestamp --type $type --status $status --note $note --branch "kalsa-project-memory" | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "State-ledger write failed for $($record.Id)"
|
||||
}
|
||||
}
|
||||
|
||||
& $storeTool status --store $stagePath | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Generated project-memory state failed inspection"
|
||||
}
|
||||
|
||||
Move-Item -LiteralPath $stagePath -Destination $stateFull -Force
|
||||
$stageLock = $stagePath + ".lock"
|
||||
if (Test-Path -LiteralPath $stageLock) {
|
||||
Remove-Item -LiteralPath $stageLock -Force
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (Test-Path -LiteralPath $stagePath) {
|
||||
Remove-Item -LiteralPath $stagePath -Force
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output "Projected $($records.Count) memory records to $stateFull"
|
||||
25
.gitattributes
vendored
Normal file
25
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
* text=auto
|
||||
|
||||
*.md text eol=lf
|
||||
*.json text eol=lf
|
||||
*.yml text eol=lf
|
||||
*.yaml text eol=lf
|
||||
*.ts text eol=lf
|
||||
*.tsx text eol=lf
|
||||
*.scss text eol=lf
|
||||
*.js text eol=lf
|
||||
*.mjs text eol=lf
|
||||
*.ps1 text eol=crlf
|
||||
*.cc binary
|
||||
*.msgpack binary
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.gif binary
|
||||
*.webp binary
|
||||
*.ico binary
|
||||
*.woff binary
|
||||
*.woff2 binary
|
||||
*.mp3 binary
|
||||
*.ogg binary
|
||||
*.wav binary
|
||||
24
.github/workflows/deploy-quartz.yml
vendored
Normal file
24
.github/workflows/deploy-quartz.yml
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
name: Deploy Quartz
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
uses: GameCult/GameCult-Quartz/.github/workflows/quartz-pages.yml@main
|
||||
with:
|
||||
content-dir: Kalsa
|
||||
overlay-dir: site
|
||||
output-dir: quartz-site/public
|
||||
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Obsidian session and cache state
|
||||
.obsidian/workspace.json
|
||||
.obsidian/workspace-mobile.json
|
||||
.obsidian/graph.json
|
||||
.obsidian/cache/
|
||||
.trash/
|
||||
.obsidian-git-data/
|
||||
.smart-connections/
|
||||
|
||||
# Local runtimes and build output
|
||||
.tools/
|
||||
.npm-cache/
|
||||
.quartz-build/
|
||||
quartz-site/node_modules/
|
||||
quartz-site/public/
|
||||
quartz-site/*.log
|
||||
|
||||
# Runtime-generated agent state; committed typed state remains visible
|
||||
.epiphany/artifacts/
|
||||
.epiphany/**/*.lock
|
||||
.voidbot/birth/
|
||||
.voidbot/logs/
|
||||
.voidbot/status/
|
||||
.voidbot/**/*.lock
|
||||
|
||||
# Local environment
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
4
.obsidian/app.json
vendored
Normal file
4
.obsidian/app.json
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"alwaysUpdateLinks": true,
|
||||
"promptDelete": true
|
||||
}
|
||||
1
.obsidian/appearance.json
vendored
Normal file
1
.obsidian/appearance.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
1
.obsidian/community-plugins.json
vendored
Normal file
1
.obsidian/community-plugins.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
24
.obsidian/core-plugins.json
vendored
Normal file
24
.obsidian/core-plugins.json
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"file-explorer": true,
|
||||
"global-search": true,
|
||||
"switcher": true,
|
||||
"graph": true,
|
||||
"backlink": true,
|
||||
"canvas": true,
|
||||
"outgoing-link": true,
|
||||
"tag-pane": true,
|
||||
"properties": true,
|
||||
"page-preview": true,
|
||||
"daily-notes": false,
|
||||
"templates": true,
|
||||
"note-composer": true,
|
||||
"command-palette": true,
|
||||
"editor-status": true,
|
||||
"bookmarks": true,
|
||||
"outline": true,
|
||||
"word-count": true,
|
||||
"file-recovery": true,
|
||||
"publish": false,
|
||||
"sync": false,
|
||||
"bases": true
|
||||
}
|
||||
116
AGENTS.md
Normal file
116
AGENTS.md
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
# Kalsa Vault Operating Contract
|
||||
|
||||
## Purpose
|
||||
|
||||
This repository is the canonical authoring vault and Quartz source for Kalsa, a
|
||||
high-fantasy setting built for Ghostlight Dungeon. The work is to turn the
|
||||
teenage seed into a causally dense setting without sanding away its peculiar
|
||||
bones.
|
||||
|
||||
## Authority Map
|
||||
|
||||
- **Owner:** canonical notes under `Kalsa/` own shared setting claims within
|
||||
their stated subject. `workshop/Direction and Constraints.md` owns project
|
||||
doctrine and canon rules.
|
||||
- **Inputs:** immutable files under `seed/original/`, the seed manifest, current
|
||||
canonical notes, explicit operator decisions, Ghostlight play requirements,
|
||||
and source-grounded comparison with AetheriaLore.
|
||||
- **Outputs:** linked canonical Markdown, index notes, bounded critique records,
|
||||
verified Quartz projections, and reviewed project-memory judgments.
|
||||
- **Derived state:** Quartz output, link graphs, word/link/heading diagnostics,
|
||||
benchmark comparisons, queues, pass reports, and `.cc` projections. Derived
|
||||
state may report on canon; it may not decide canon.
|
||||
- **Forbidden writers:** seed witnesses, project memory, workshop notes,
|
||||
generated site output, retrieval indexes, and site configuration may not
|
||||
silently create or override lore truth.
|
||||
- **Shared path:** human and agent changes use the same source inventory,
|
||||
critique, repair, falsification, verification, and record path described in
|
||||
`workshop/deepening/README.md`.
|
||||
- **Cut line:** do not revive timestamp export files as live canonical pages,
|
||||
create a second lore database, invent a public repo Persona, or copy
|
||||
Aetheria-specific site/lore machinery into Kalsa.
|
||||
|
||||
## Repository Boundaries
|
||||
|
||||
- `Kalsa/` is the publishable canonical content root.
|
||||
- `seed/original/` is immutable evidence. Never edit, normalize, rename, or
|
||||
re-encode these files. Verify them with `scripts/lore/verify-seed.ps1`.
|
||||
- `seed/manifest.md` owns witness filenames, sizes, hashes, and subject labels.
|
||||
- `workshop/` holds doctrine, critique packets, queues, and pass records. It is
|
||||
not lore and must remain outside `Kalsa/`.
|
||||
- `.epiphany/project-memory.md` is the human-inspectable project-memory source.
|
||||
Its `.cc` projection is runtime state, not canon.
|
||||
- `site/` is a Quartz overlay. `.quartz-build/` and `quartz-site/public/` are
|
||||
generated projections and are never edited directly.
|
||||
|
||||
## Canon And Provenance
|
||||
|
||||
- A seed claim becomes shared canon only when a canonical owner note adopts it.
|
||||
- Distinguish seed evidence, adopted canon, provisional design, contested
|
||||
in-world claims, rumor, and deprecated material. Do not use publication state
|
||||
as a substitute for canon state.
|
||||
- Put proposals and unresolved design in `workshop/`. Put genuinely contested
|
||||
setting facts in canonical notes only with clear in-world attribution.
|
||||
- Prefer one owner note plus links over repeated explanations. If two notes
|
||||
appear to own the same claim, stop and resolve the boundary before expanding
|
||||
either.
|
||||
- Preserve the source's strange specificity. Repair causal gaps; do not replace
|
||||
them with generic medieval-Europe defaults wearing renamed hats.
|
||||
|
||||
## Deepening Work
|
||||
|
||||
Before editing an institution, region, practice, or cross-cutting system:
|
||||
|
||||
1. Create a pass from `workshop/deepening/pass-template.md`.
|
||||
2. Inventory seed statements, adopted facts, contradictions, and unknowns.
|
||||
3. Name the canonical owner and model its inputs, outputs, dependencies,
|
||||
beneficiaries, costs, failure modes, and neighboring interfaces.
|
||||
4. Critique generic defaults, unsupported consensus, missing material support,
|
||||
absent subordinate perspectives, and consequences that stop at the page.
|
||||
5. Repair the smallest coherent set of owner notes.
|
||||
6. Falsify the result with one hostile or marginal perspective and one
|
||||
historical stress case.
|
||||
7. Verify seed integrity, links, publication boundaries, and the Quartz build.
|
||||
8. Record the pass in the ledger and promote only durable judgment into project
|
||||
memory.
|
||||
|
||||
Passing prose is not enough. A mature institution must explain authority,
|
||||
reproduction, material support, doctrine versus practice, distribution of
|
||||
benefit and harm, dependencies, historical formation, internal contradiction,
|
||||
and consequences for play. Use `workshop/deepening/benchmark.md` as the parity
|
||||
standard; do not turn word count into a victory condition.
|
||||
|
||||
## Project Memory
|
||||
|
||||
- Project memory records durable judgment, operating lessons, adopted
|
||||
decisions, open design pressure, and provenance.
|
||||
- Project memory does not store lore facts as an alternate canon, raw
|
||||
transcripts, generated prose, task chatter, or a duplicate roadmap.
|
||||
- Update `.epiphany/project-memory.md` first. Regenerate the derived CultCache
|
||||
state with `.epiphany/sync-project-memory.ps1`.
|
||||
- Retire stale judgments explicitly instead of leaving contradictory active
|
||||
entries. Canonical decisions must also be visible in the owning Markdown
|
||||
surface.
|
||||
|
||||
## Retrieval And Inspection
|
||||
|
||||
- Once Kalsa is indexed, use the global VoidBot source search for semantic
|
||||
discovery and then open the returned source notes before editing.
|
||||
- Use `rg` for exact filenames, exact claims, links, and repository-wide
|
||||
patterns. Do not build a repo-local semantic index.
|
||||
- Read and write Markdown explicitly as UTF-8 in Windows PowerShell.
|
||||
|
||||
## Verification
|
||||
|
||||
Run from the repository root:
|
||||
|
||||
```powershell
|
||||
.\scripts\lore\verify-seed.ps1
|
||||
.\scripts\lore\check-wikilinks.ps1
|
||||
.\scripts\lore\check-publication-boundary.ps1
|
||||
.\scripts\lore\measure-depth.ps1
|
||||
.\scripts\lore\test-lore-tools.ps1
|
||||
```
|
||||
|
||||
For a deepening pass, also run the production Quartz build and inspect affected
|
||||
rendered pages. Diagnostics observe the setting; they do not award canon.
|
||||
22
LICENSE
Normal file
22
LICENSE
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 Meta Vens
|
||||
|
||||
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.
|
||||
|
||||
47
README.md
Normal file
47
README.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Kalsa
|
||||
|
||||
Kalsa is a high-fantasy setting built on the wreckage of an argument with causality. Eight habitat rings fell onto a tidally locked world. Their cities inherited seven engineered forms of prescience, one Null control lineage, failing Ark machinery, hungry upper-dimensional gods, and the political problem of deciding who gets to call maintenance a miracle.
|
||||
|
||||
This repository is the canonical Obsidian vault and Quartz source for Kalsa's use in Ghostlight Dungeon. The old WordPress export remains intact under `seed/original/`; it is evidence, not an automatic veto and not an invitation to sand away everything peculiar.
|
||||
|
||||
## Start here
|
||||
|
||||
- Readers: begin with [`Kalsa/index.md`](Kalsa/index.md).
|
||||
- Writers: read [`workshop/Direction and Constraints.md`](workshop/Direction%20and%20Constraints.md), then the canon/provenance guide under `Kalsa/Reference/`.
|
||||
- Maintainers: the iterative repair system lives under `workshop/deepening/`; site-specific Quartz configuration lives under `site/`.
|
||||
|
||||
## Authority map
|
||||
|
||||
- `Kalsa/`: publishable canonical owner notes and their indexes
|
||||
- `seed/original/`: immutable source witnesses
|
||||
- `seed/manifest.md`: source hashes and witness map
|
||||
- `workshop/`: critique, proposals, contradictions, and repair evidence; never automatic canon
|
||||
- `.epiphany/`: durable agent judgment and continuity; never setting truth
|
||||
- `site/`: Kalsa-specific Quartz projection
|
||||
- `scripts/`: build and verification tools
|
||||
- `quartz-site/public/`: generated output, ignored by Git
|
||||
|
||||
Obsidian is the authoring interface. Quartz is the publication interface. Neither owns lore. A claim enters shared canon only when the relevant note under `Kalsa/` adopts it.
|
||||
|
||||
## Local site
|
||||
|
||||
The shared engine is the sibling [`GameCult-Quartz`](https://github.com/GameCult/GameCult-Quartz) repository. Install that engine's dependencies once, then run:
|
||||
|
||||
```powershell
|
||||
.\scripts\quartz\quartz.ps1 build
|
||||
```
|
||||
|
||||
For local development:
|
||||
|
||||
```powershell
|
||||
.\scripts\quartz\quartz.ps1 dev
|
||||
```
|
||||
|
||||
Set `GAMECULT_QUARTZ_ROOT` when the shared engine is not checked out beside this repository. The launcher stages the engine under `.quartz-build/engine` and writes the static site to `quartz-site/public`.
|
||||
|
||||
The intended custom domain is `kalsa.gamecult.org`. DNS and the GitHub Pages domain binding are an external deployment step; repository configuration does not pretend that step has already happened.
|
||||
|
||||
## Deepening standard
|
||||
|
||||
The target is causal density comparable to AetheriaLore, not a matching heap of nouns. A mature institution exposes its origin, authority, material base, recruitment and succession, ordinary procedure, beneficiaries and costs, dependencies, internal contradiction, organized resistance, failure modes, and consequences for play. Each repair wave is modeled, challenged by an unfamiliar reader, grounded against named sources, changed in a bounded diff, and verified before its ledger entry closes.
|
||||
|
||||
69
scripts/lore/check-publication-boundary.ps1
Normal file
69
scripts/lore/check-publication-boundary.ps1
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
param(
|
||||
[string]$RepositoryRoot,
|
||||
[string]$ContentRoot,
|
||||
[switch]$SkipEntrypointCheck
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
if (-not $RepositoryRoot) {
|
||||
$RepositoryRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
||||
}
|
||||
$repoFull = [System.IO.Path]::GetFullPath($RepositoryRoot).TrimEnd('\', '/')
|
||||
if (-not $ContentRoot) {
|
||||
$ContentRoot = Join-Path $repoFull "Kalsa"
|
||||
}
|
||||
$contentFull = [System.IO.Path]::GetFullPath($ContentRoot).TrimEnd('\', '/')
|
||||
|
||||
if (-not (Test-Path -LiteralPath $repoFull -PathType Container)) {
|
||||
throw "Repository root not found: $repoFull"
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $contentFull -PathType Container)) {
|
||||
throw "Canonical publication root not found: $contentFull"
|
||||
}
|
||||
if (-not $contentFull.StartsWith($repoFull + [System.IO.Path]::DirectorySeparatorChar, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "Publication root must remain inside the repository root"
|
||||
}
|
||||
|
||||
$errors = @()
|
||||
$reserved = @('seed', 'workshop', '.epiphany', '.voidbot', 'site', '.github', 'scripts', 'quartz-site', '.quartz-build')
|
||||
foreach ($directory in Get-ChildItem -LiteralPath $contentFull -Recurse -Directory -Force) {
|
||||
if ($directory.Name.ToLowerInvariant() -in $reserved) {
|
||||
$errors += "reserved project directory nested in publication root: $($directory.FullName)"
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($item in Get-ChildItem -LiteralPath $contentFull -Recurse -Force) {
|
||||
if (($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) {
|
||||
$errors += "reparse point may escape publication ownership: $($item.FullName)"
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $SkipEntrypointCheck) {
|
||||
$launcher = Join-Path $repoFull "scripts\quartz\quartz.ps1"
|
||||
$workflow = Join-Path $repoFull ".github\workflows\deploy-quartz.yml"
|
||||
if (-not (Test-Path -LiteralPath $launcher -PathType Leaf)) {
|
||||
$errors += "Quartz launcher missing: $launcher"
|
||||
}
|
||||
else {
|
||||
$launcherText = Get-Content -Raw -LiteralPath $launcher -Encoding utf8
|
||||
if ($launcherText -notmatch '(?s)--contentDir.{0,80}["'']Kalsa["'']') {
|
||||
$errors += "Quartz launcher does not bind --contentDir to Kalsa"
|
||||
}
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $workflow -PathType Leaf)) {
|
||||
$errors += "Quartz deployment workflow missing: $workflow"
|
||||
}
|
||||
else {
|
||||
$workflowText = Get-Content -Raw -LiteralPath $workflow -Encoding utf8
|
||||
if ($workflowText -notmatch '(?m)^\s*content-dir:\s*Kalsa\s*$') {
|
||||
$errors += "Quartz deployment workflow does not bind content-dir to Kalsa"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors.Count -gt 0) {
|
||||
throw "Publication-boundary verification failed:`n- $($errors -join "`n- ")"
|
||||
}
|
||||
|
||||
Write-Output "Publication boundary passed: only $contentFull is admitted as canonical site input."
|
||||
81
scripts/lore/check-wikilinks.ps1
Normal file
81
scripts/lore/check-wikilinks.ps1
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
param(
|
||||
[string]$ContentRoot
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
||||
if (-not $ContentRoot) {
|
||||
$ContentRoot = Join-Path $repoRoot "Kalsa"
|
||||
}
|
||||
$contentFull = [System.IO.Path]::GetFullPath($ContentRoot)
|
||||
if (-not (Test-Path -LiteralPath $contentFull -PathType Container)) {
|
||||
throw "Canonical content root not found: $contentFull"
|
||||
}
|
||||
|
||||
$notes = @(Get-ChildItem -LiteralPath $contentFull -Recurse -File -Filter '*.md')
|
||||
$allFiles = @(Get-ChildItem -LiteralPath $contentFull -Recurse -File)
|
||||
$noteByStem = @{}
|
||||
$noteByRelative = @{}
|
||||
$fileByName = @{}
|
||||
|
||||
foreach ($note in $notes) {
|
||||
$stem = [System.IO.Path]::GetFileNameWithoutExtension($note.Name).ToLowerInvariant()
|
||||
if (-not $noteByStem.ContainsKey($stem)) { $noteByStem[$stem] = @() }
|
||||
$noteByStem[$stem] += $note.FullName
|
||||
$relative = $note.FullName.Substring($contentFull.Length).TrimStart('\', '/') -replace '\\', '/'
|
||||
$noteByRelative[([System.IO.Path]::ChangeExtension($relative, $null).TrimEnd('.').ToLowerInvariant())] = $note.FullName
|
||||
}
|
||||
foreach ($file in $allFiles) {
|
||||
$key = $file.Name.ToLowerInvariant()
|
||||
if (-not $fileByName.ContainsKey($key)) { $fileByName[$key] = @() }
|
||||
$fileByName[$key] += $file.FullName
|
||||
}
|
||||
|
||||
$errors = @()
|
||||
$linkCount = 0
|
||||
foreach ($note in $notes) {
|
||||
$text = Get-Content -Raw -LiteralPath $note.FullName -Encoding utf8
|
||||
foreach ($match in [regex]::Matches($text, '!??\[\[(?<target>[^\]]+)\]\]')) {
|
||||
$linkCount += 1
|
||||
$target = ($match.Groups['target'].Value -split '\|', 2)[0]
|
||||
$target = ($target -split '#', 2)[0].Trim()
|
||||
if (-not $target) { continue }
|
||||
$target = $target -replace '\\', '/'
|
||||
$resolved = @()
|
||||
$extension = [System.IO.Path]::GetExtension($target)
|
||||
if ($extension -and $extension.ToLowerInvariant() -ne '.md') {
|
||||
$relativeAsset = [System.IO.Path]::GetFullPath((Join-Path $note.DirectoryName $target))
|
||||
$rootAsset = [System.IO.Path]::GetFullPath((Join-Path $contentFull $target))
|
||||
if (Test-Path -LiteralPath $relativeAsset -PathType Leaf) { $resolved += $relativeAsset }
|
||||
if (Test-Path -LiteralPath $rootAsset -PathType Leaf) { $resolved += $rootAsset }
|
||||
$assetName = [System.IO.Path]::GetFileName($target).ToLowerInvariant()
|
||||
if ($fileByName.ContainsKey($assetName)) { $resolved += $fileByName[$assetName] }
|
||||
}
|
||||
else {
|
||||
$noteTarget = if ($extension) { [System.IO.Path]::ChangeExtension($target, $null).TrimEnd('.') } else { $target }
|
||||
$relativeKey = $noteTarget.TrimStart('/')
|
||||
if ($noteByRelative.ContainsKey($relativeKey.ToLowerInvariant())) {
|
||||
$resolved += $noteByRelative[$relativeKey.ToLowerInvariant()]
|
||||
}
|
||||
$relativeCandidate = [System.IO.Path]::GetFullPath((Join-Path $note.DirectoryName ($noteTarget + '.md')))
|
||||
if (Test-Path -LiteralPath $relativeCandidate -PathType Leaf) { $resolved += $relativeCandidate }
|
||||
$stem = [System.IO.Path]::GetFileName($noteTarget).ToLowerInvariant()
|
||||
if ($noteByStem.ContainsKey($stem)) { $resolved += $noteByStem[$stem] }
|
||||
}
|
||||
$resolved = @($resolved | Sort-Object -Unique)
|
||||
$sourceRelative = $note.FullName.Substring($contentFull.Length).TrimStart('\', '/')
|
||||
if ($resolved.Count -eq 0) {
|
||||
$errors += "$sourceRelative -> [[$($match.Groups['target'].Value)]] is unresolved"
|
||||
}
|
||||
elseif ($resolved.Count -gt 1 -and $target -notmatch '[/\\]') {
|
||||
$errors += "$sourceRelative -> [[$($match.Groups['target'].Value)]] is ambiguous across $($resolved.Count) files"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors.Count -gt 0) {
|
||||
throw "Wikilink verification failed:`n- $($errors -join "`n- ")"
|
||||
}
|
||||
|
||||
Write-Output "Wikilink verification passed: $linkCount links across $($notes.Count) canonical notes."
|
||||
72
scripts/lore/measure-depth.ps1
Normal file
72
scripts/lore/measure-depth.ps1
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
param(
|
||||
[string]$ContentRoot,
|
||||
[string]$CompareRoot = "F:\Projects\AetheriaLore\Aetheria\Worldbuilding"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
||||
if (-not $ContentRoot) {
|
||||
$ContentRoot = Join-Path $repoRoot "Kalsa"
|
||||
}
|
||||
|
||||
function Measure-Branch {
|
||||
param(
|
||||
[string]$Root,
|
||||
[string]$Label
|
||||
)
|
||||
|
||||
$full = [System.IO.Path]::GetFullPath($Root)
|
||||
if (-not (Test-Path -LiteralPath $full -PathType Container)) {
|
||||
throw "Depth input root not found: $full"
|
||||
}
|
||||
$rows = @()
|
||||
foreach ($file in Get-ChildItem -LiteralPath $full -Recurse -File -Filter '*.md') {
|
||||
$text = Get-Content -Raw -LiteralPath $file.FullName -Encoding utf8
|
||||
$relative = $file.FullName.Substring($full.Length).TrimStart('\', '/')
|
||||
$rows += [pscustomobject]@{
|
||||
Path = $relative
|
||||
IsIndex = $file.Name -ieq 'index.md'
|
||||
Words = ([regex]::Matches($text, "\b[\p{L}\p{N}'-]+\b")).Count
|
||||
Wikilinks = ([regex]::Matches($text, '\[\[')).Count
|
||||
Headings = ([regex]::Matches($text, '(?m)^#{1,6}\s')).Count
|
||||
}
|
||||
}
|
||||
|
||||
$nonIndex = @($rows | Where-Object { -not $_.IsIndex })
|
||||
$wordValues = @($nonIndex.Words | Sort-Object)
|
||||
$median = if ($wordValues.Count -eq 0) { 0 } else { $wordValues[[math]::Floor($wordValues.Count / 2)] }
|
||||
$summary = [pscustomobject]@{
|
||||
Branch = $Label
|
||||
Notes = $rows.Count
|
||||
NonIndexNotes = $nonIndex.Count
|
||||
Words = ($rows | Measure-Object Words -Sum).Sum
|
||||
Wikilinks = ($rows | Measure-Object Wikilinks -Sum).Sum
|
||||
Headings = ($rows | Measure-Object Headings -Sum).Sum
|
||||
MedianNonIndexWords = $median
|
||||
NotesWithoutWikilinks = @($nonIndex | Where-Object { $_.Wikilinks -eq 0 }).Count
|
||||
}
|
||||
return [pscustomobject]@{ Summary = $summary; Rows = $rows }
|
||||
}
|
||||
|
||||
$kalsa = Measure-Branch -Root $ContentRoot -Label "Kalsa canonical"
|
||||
$summaries = @($kalsa.Summary)
|
||||
if (Test-Path -LiteralPath $CompareRoot -PathType Container) {
|
||||
$comparison = Measure-Branch -Root $CompareRoot -Label "Aetheria worldbuilding"
|
||||
$summaries += $comparison.Summary
|
||||
}
|
||||
else {
|
||||
Write-Warning "Comparison root not available: $CompareRoot"
|
||||
}
|
||||
|
||||
Write-Output "Structural diagnostics (counts are pressure signals, not a depth verdict):"
|
||||
$summaries | Format-Table -AutoSize
|
||||
|
||||
Write-Output "Kalsa notes with the least connective structure:"
|
||||
$kalsa.Rows |
|
||||
Where-Object { -not $_.IsIndex } |
|
||||
Sort-Object @{ Expression = 'Wikilinks'; Ascending = $true }, @{ Expression = 'Words'; Ascending = $true } |
|
||||
Select-Object -First 20 Path, Words, Wikilinks, Headings |
|
||||
Format-Table -AutoSize
|
||||
|
||||
Write-Output "Qualitative parity still requires the review in workshop/deepening/benchmark.md."
|
||||
59
scripts/lore/test-lore-tools.ps1
Normal file
59
scripts/lore/test-lore-tools.ps1
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
||||
$tempBase = [System.IO.Path]::GetFullPath([System.IO.Path]::GetTempPath()).TrimEnd('\', '/')
|
||||
$fixture = Join-Path $tempBase ("kalsa-lore-tools-" + [guid]::NewGuid().ToString())
|
||||
New-Item -ItemType Directory -Path $fixture | Out-Null
|
||||
$fixtureFull = [System.IO.Path]::GetFullPath($fixture)
|
||||
if (-not $fixtureFull.StartsWith($tempBase + [System.IO.Path]::DirectorySeparatorChar, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "Refusing to create test fixture outside the system temporary directory"
|
||||
}
|
||||
|
||||
try {
|
||||
$content = Join-Path $fixtureFull "Kalsa"
|
||||
New-Item -ItemType Directory -Path $content | Out-Null
|
||||
Set-Content -LiteralPath (Join-Path $content "index.md") -Encoding utf8 -Value "# Fixture`n`nSee [[Institution]]."
|
||||
Set-Content -LiteralPath (Join-Path $content "Institution.md") -Encoding utf8 -Value "# Institution`n`nA bounded test note."
|
||||
|
||||
& (Join-Path $PSScriptRoot "verify-seed.ps1")
|
||||
& (Join-Path $PSScriptRoot "check-wikilinks.ps1") -ContentRoot $content
|
||||
& (Join-Path $PSScriptRoot "check-publication-boundary.ps1") -RepositoryRoot $fixtureFull -ContentRoot $content -SkipEntrypointCheck
|
||||
& (Join-Path $PSScriptRoot "measure-depth.ps1") -ContentRoot $content -CompareRoot (Join-Path $fixtureFull "missing-comparison") | Out-Null
|
||||
|
||||
$broken = Join-Path $content "Broken.md"
|
||||
Set-Content -LiteralPath $broken -Encoding utf8 -Value "# Broken`n`nSee [[No Such Note]]."
|
||||
$caughtBrokenLink = $false
|
||||
try {
|
||||
& (Join-Path $PSScriptRoot "check-wikilinks.ps1") -ContentRoot $content | Out-Null
|
||||
}
|
||||
catch {
|
||||
$caughtBrokenLink = $true
|
||||
}
|
||||
if (-not $caughtBrokenLink) {
|
||||
throw "Wikilink checker failed to reject an unresolved link"
|
||||
}
|
||||
Remove-Item -LiteralPath $broken -Force
|
||||
|
||||
$forbidden = Join-Path $content "workshop"
|
||||
New-Item -ItemType Directory -Path $forbidden | Out-Null
|
||||
$caughtBoundary = $false
|
||||
try {
|
||||
& (Join-Path $PSScriptRoot "check-publication-boundary.ps1") -RepositoryRoot $fixtureFull -ContentRoot $content -SkipEntrypointCheck | Out-Null
|
||||
}
|
||||
catch {
|
||||
$caughtBoundary = $true
|
||||
}
|
||||
if (-not $caughtBoundary) {
|
||||
throw "Publication checker failed to reject a nested workshop directory"
|
||||
}
|
||||
|
||||
Write-Output "Lore tool tests passed, including broken-link and publication-boundary negative checks."
|
||||
}
|
||||
finally {
|
||||
$resolvedFixture = [System.IO.Path]::GetFullPath($fixtureFull)
|
||||
if ($resolvedFixture.StartsWith($tempBase + [System.IO.Path]::DirectorySeparatorChar, [System.StringComparison]::OrdinalIgnoreCase) -and
|
||||
([System.IO.Path]::GetFileName($resolvedFixture) -like 'kalsa-lore-tools-*') -and
|
||||
(Test-Path -LiteralPath $resolvedFixture)) {
|
||||
Remove-Item -LiteralPath $resolvedFixture -Recurse -Force
|
||||
}
|
||||
}
|
||||
73
scripts/lore/verify-seed.ps1
Normal file
73
scripts/lore/verify-seed.ps1
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
param(
|
||||
[string]$ManifestPath,
|
||||
[string]$SeedRoot
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
||||
if (-not $ManifestPath) {
|
||||
$ManifestPath = Join-Path $repoRoot "seed\manifest.md"
|
||||
}
|
||||
if (-not $SeedRoot) {
|
||||
$SeedRoot = Join-Path $repoRoot "seed\original"
|
||||
}
|
||||
|
||||
$manifestFull = [System.IO.Path]::GetFullPath($ManifestPath)
|
||||
$seedFull = [System.IO.Path]::GetFullPath($SeedRoot)
|
||||
if (-not (Test-Path -LiteralPath $manifestFull -PathType Leaf)) {
|
||||
throw "Seed manifest not found: $manifestFull"
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $seedFull -PathType Container)) {
|
||||
throw "Seed directory not found: $seedFull"
|
||||
}
|
||||
|
||||
$manifest = Get-Content -LiteralPath $manifestFull -Encoding utf8
|
||||
$entries = @()
|
||||
foreach ($line in $manifest) {
|
||||
$match = [regex]::Match($line, '^\|\s*`(?<file>[^`]+)`\s*\|\s*(?<bytes>[\d,]+)\s*\|\s*`(?<hash>[0-9a-fA-F]{64})`\s*\|')
|
||||
if ($match.Success) {
|
||||
$entries += [pscustomobject]@{
|
||||
File = $match.Groups['file'].Value
|
||||
Bytes = [int64]($match.Groups['bytes'].Value -replace ',', '')
|
||||
Hash = $match.Groups['hash'].Value.ToLowerInvariant()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($entries.Count -eq 0) {
|
||||
throw "No seed entries were parsed from $manifestFull"
|
||||
}
|
||||
if (($entries.File | Sort-Object -Unique).Count -ne $entries.Count) {
|
||||
throw "Seed manifest contains duplicate filenames"
|
||||
}
|
||||
|
||||
$errors = @()
|
||||
foreach ($entry in $entries) {
|
||||
$path = Join-Path $seedFull $entry.File
|
||||
if (-not (Test-Path -LiteralPath $path -PathType Leaf)) {
|
||||
$errors += "missing: $($entry.File)"
|
||||
continue
|
||||
}
|
||||
$item = Get-Item -LiteralPath $path
|
||||
if ($item.Length -ne $entry.Bytes) {
|
||||
$errors += "size mismatch: $($entry.File) expected $($entry.Bytes), found $($item.Length)"
|
||||
}
|
||||
$actualHash = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
if ($actualHash -ne $entry.Hash) {
|
||||
$errors += "hash mismatch: $($entry.File) expected $($entry.Hash), found $actualHash"
|
||||
}
|
||||
}
|
||||
|
||||
$expected = @($entries.File | Sort-Object)
|
||||
$actual = @(Get-ChildItem -LiteralPath $seedFull -File | Select-Object -ExpandProperty Name | Sort-Object)
|
||||
$unexpected = @($actual | Where-Object { $_ -notin $expected })
|
||||
if ($unexpected.Count -gt 0) {
|
||||
$errors += "unmanifested seed files: $($unexpected -join ', ')"
|
||||
}
|
||||
|
||||
if ($errors.Count -gt 0) {
|
||||
throw "Seed verification failed:`n- $($errors -join "`n- ")"
|
||||
}
|
||||
|
||||
Write-Output "Seed verification passed: $($entries.Count) immutable witnesses match size and SHA-256."
|
||||
51
scripts/quartz/quartz.ps1
Normal file
51
scripts/quartz/quartz.ps1
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
param(
|
||||
[ValidateSet("build", "dev")]
|
||||
[string]$Command = "dev"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
||||
$sharedQuartzRoot = if ($env:GAMECULT_QUARTZ_ROOT) {
|
||||
$env:GAMECULT_QUARTZ_ROOT
|
||||
} else {
|
||||
Join-Path (Split-Path -Parent $repoRoot) "GameCult-Quartz"
|
||||
}
|
||||
$portableNodeRoot = Join-Path $repoRoot ".tools\node-v24.15.0-win-x64"
|
||||
$portableNode = Join-Path $portableNodeRoot "node.exe"
|
||||
|
||||
# Prefer the bundled Windows runtime when present, then fall back to Node on PATH.
|
||||
if (Test-Path $portableNode) {
|
||||
$node = $portableNode
|
||||
$env:PATH = "$portableNodeRoot;$env:PATH"
|
||||
} else {
|
||||
$nodeCommand = Get-Command node -ErrorAction SilentlyContinue
|
||||
if (-not $nodeCommand) {
|
||||
throw "node was not found. Install Node.js or restore the portable runtime under .tools."
|
||||
}
|
||||
|
||||
$node = $nodeCommand.Source
|
||||
}
|
||||
|
||||
$env:npm_config_cache = Join-Path $repoRoot ".npm-cache"
|
||||
|
||||
if (-not (Test-Path $sharedQuartzRoot)) {
|
||||
throw "GameCult-Quartz was not found at '$sharedQuartzRoot'. Clone it beside this repo or set GAMECULT_QUARTZ_ROOT."
|
||||
}
|
||||
|
||||
$buildScript = Join-Path $sharedQuartzRoot "scripts\build-site.mjs"
|
||||
|
||||
if (-not (Test-Path $buildScript)) {
|
||||
throw "GameCult-Quartz build script was not found at '$buildScript'."
|
||||
}
|
||||
|
||||
$scriptArgs = @(
|
||||
$buildScript,
|
||||
$Command,
|
||||
"--siteRoot", $repoRoot,
|
||||
"--overlayDir", "site",
|
||||
"--contentDir", "Kalsa",
|
||||
"--outputDir", "quartz-site/public"
|
||||
)
|
||||
|
||||
& $node @scriptArgs
|
||||
110
site/quartz.config.ts
Normal file
110
site/quartz.config.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
import { QuartzConfig } from "./quartz/cfg";
|
||||
import * as Plugin from "./quartz/plugins";
|
||||
|
||||
const config: QuartzConfig = {
|
||||
configuration: {
|
||||
pageTitle: "Kalsa",
|
||||
pageTitleSuffix: "",
|
||||
enableSPA: true,
|
||||
enablePopovers: true,
|
||||
analytics: null,
|
||||
locale: "en-GB",
|
||||
// Provisional until the kalsa.gamecult.org DNS and GitHub Pages binding exist.
|
||||
baseUrl: "kalsa.gamecult.org",
|
||||
ignorePatterns: [
|
||||
"private",
|
||||
"templates",
|
||||
".obsidian",
|
||||
"Brainstorming",
|
||||
"Brainstorming/**",
|
||||
"Inspiration",
|
||||
"Inspiration/**",
|
||||
],
|
||||
defaultDateType: "modified",
|
||||
theme: {
|
||||
fontOrigin: "googleFonts",
|
||||
cdnCaching: true,
|
||||
typography: {
|
||||
header: {
|
||||
name: "Cormorant Garamond",
|
||||
weights: [400, 500, 600, 700],
|
||||
includeItalic: true,
|
||||
},
|
||||
title: {
|
||||
name: "Cormorant Garamond",
|
||||
weights: [500, 600, 700],
|
||||
includeItalic: true,
|
||||
},
|
||||
body: {
|
||||
name: "Source Sans 3",
|
||||
weights: [300, 400, 500, 600, 700],
|
||||
includeItalic: true,
|
||||
},
|
||||
code: "IBM Plex Mono",
|
||||
},
|
||||
colors: {
|
||||
lightMode: {
|
||||
light: "#f4efe2",
|
||||
lightgray: "#ded5c2",
|
||||
gray: "#8d826f",
|
||||
darkgray: "#4a4034",
|
||||
dark: "#1f1a16",
|
||||
secondary: "#7c3f2b",
|
||||
tertiary: "#47614b",
|
||||
highlight: "rgba(71, 97, 75, 0.13)",
|
||||
textHighlight: "#d1a85a66",
|
||||
},
|
||||
darkMode: {
|
||||
light: "#111713",
|
||||
lightgray: "#1c261f",
|
||||
gray: "#667567",
|
||||
darkgray: "#c3c7b7",
|
||||
dark: "#f1ecdd",
|
||||
secondary: "#d1a85a",
|
||||
tertiary: "#8eb69b",
|
||||
highlight: "rgba(142, 182, 155, 0.14)",
|
||||
textHighlight: "#d1a85a55",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
transformers: [
|
||||
Plugin.FrontMatter(),
|
||||
Plugin.CreatedModifiedDate({
|
||||
priority: ["frontmatter", "git", "filesystem"],
|
||||
}),
|
||||
Plugin.SyntaxHighlighting({
|
||||
theme: {
|
||||
light: "github-light",
|
||||
dark: "github-dark",
|
||||
},
|
||||
keepBackground: false,
|
||||
}),
|
||||
Plugin.ObsidianFlavoredMarkdown({ enableInHtmlEmbed: false }),
|
||||
Plugin.GitHubFlavoredMarkdown(),
|
||||
Plugin.TableOfContents(),
|
||||
Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }),
|
||||
Plugin.Description(),
|
||||
Plugin.Latex({ renderEngine: "katex" }),
|
||||
],
|
||||
filters: [Plugin.RemoveDrafts()],
|
||||
emitters: [
|
||||
Plugin.AliasRedirects(),
|
||||
Plugin.ComponentResources(),
|
||||
Plugin.ContentPage(),
|
||||
Plugin.FolderPage(),
|
||||
Plugin.ContentIndex({
|
||||
enableSiteMap: true,
|
||||
enableRSS: true,
|
||||
}),
|
||||
Plugin.Assets(),
|
||||
Plugin.Static(),
|
||||
Plugin.Favicon(),
|
||||
Plugin.NotFoundPage(),
|
||||
Plugin.CustomOgImages(),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
50
site/quartz.layout.ts
Normal file
50
site/quartz.layout.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import { PageLayout, SharedLayout } from "./quartz/cfg";
|
||||
import * as Component from "./quartz/components";
|
||||
|
||||
export const sharedPageComponents: SharedLayout = {
|
||||
head: Component.Head(),
|
||||
header: [
|
||||
Component.PageTitle(),
|
||||
Component.Spacer(),
|
||||
Component.Search(),
|
||||
Component.Darkmode(),
|
||||
],
|
||||
afterBody: [],
|
||||
footer: Component.Footer({
|
||||
links: {
|
||||
GameCult: "https://gamecult.org",
|
||||
GitHub: "https://github.com/GameCult",
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
export const defaultContentPageLayout: PageLayout = {
|
||||
beforeBody: [
|
||||
Component.Breadcrumbs({
|
||||
rootName: "Kalsa",
|
||||
showCurrentPage: false,
|
||||
showRoot: false,
|
||||
}),
|
||||
Component.ArticleTitle(),
|
||||
Component.ContentMeta(),
|
||||
],
|
||||
left: [Component.DesktopOnly(Component.Explorer())],
|
||||
right: [
|
||||
Component.DesktopOnly(Component.TableOfContents()),
|
||||
Component.Backlinks(),
|
||||
],
|
||||
};
|
||||
|
||||
export const defaultListPageLayout: PageLayout = {
|
||||
beforeBody: [
|
||||
Component.Breadcrumbs({
|
||||
rootName: "Kalsa",
|
||||
showCurrentPage: false,
|
||||
showRoot: false,
|
||||
}),
|
||||
Component.ArticleTitle(),
|
||||
Component.ContentMeta(),
|
||||
],
|
||||
left: [Component.DesktopOnly(Component.Explorer())],
|
||||
right: [],
|
||||
};
|
||||
60
site/quartz/styles/custom.scss
Normal file
60
site/quartz/styles/custom.scss
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
:root {
|
||||
--kalsa-rule: color-mix(in srgb, var(--secondary) 42%, transparent);
|
||||
}
|
||||
|
||||
body {
|
||||
background-image:
|
||||
radial-gradient(
|
||||
circle at 18% 0%,
|
||||
color-mix(in srgb, var(--tertiary) 8%, transparent),
|
||||
transparent 32rem
|
||||
),
|
||||
linear-gradient(
|
||||
180deg,
|
||||
color-mix(in srgb, var(--light) 96%, var(--dark) 4%),
|
||||
var(--light) 22rem
|
||||
);
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
border-bottom: 1px solid var(--kalsa-rule);
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.page-title a {
|
||||
letter-spacing: 0.045em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
article h1,
|
||||
article h2,
|
||||
article h3 {
|
||||
letter-spacing: 0.012em;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
article h2 {
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--gray) 35%, transparent);
|
||||
padding-bottom: 0.18em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-inline-start-color: var(--secondary);
|
||||
background: color-mix(in srgb, var(--lightgray) 42%, transparent);
|
||||
}
|
||||
|
||||
.explorer .folder-title,
|
||||
.toc .toc-header {
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
footer {
|
||||
border-top: 1px solid var(--kalsa-rule);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
body {
|
||||
background-attachment: scroll;
|
||||
}
|
||||
}
|
||||
56
workshop/deepening/README.md
Normal file
56
workshop/deepening/README.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Institutional Deepening Contract
|
||||
|
||||
This directory owns the critique-and-repair process. It does not own lore.
|
||||
Canonical claims live under `Kalsa/`; these records explain why and how an owner
|
||||
note changed.
|
||||
|
||||
## Pass Contract
|
||||
|
||||
One pass addresses one bounded institution, region, practice, or cross-cutting
|
||||
system. Copy `pass-template.md` into a dated pass file before editing canon.
|
||||
|
||||
A pass must:
|
||||
|
||||
1. **Select** a target with a named canonical owner or explicitly record that
|
||||
the missing owner is the defect.
|
||||
2. **Inventory** seed evidence, adopted claims, provisional design,
|
||||
contradictions, rumors, deprecated material, and unknowns without blending
|
||||
their authority.
|
||||
3. **Model** owner, allowed inputs, outputs, dependencies, beneficiaries,
|
||||
costs, failure modes, enforcement limits, and interfaces.
|
||||
4. **Critique** generic fantasy defaults, missing material constraints, false
|
||||
consensus, absent subordinate perspectives, and consequences that stop at
|
||||
the page edge.
|
||||
5. **Repair** the smallest coherent set of canonical owner notes. Delete or
|
||||
demote duplicate owners before adding compensating prose.
|
||||
6. **Connect** the repair to people, places, events, resources, language,
|
||||
magic, and playable pressure.
|
||||
7. **Falsify** the result through at least one hostile or marginal perspective
|
||||
and one historical stress case.
|
||||
8. **Verify** seed hashes, wikilinks, publication boundaries, the Quartz build,
|
||||
affected rendered pages, and the actual git diff.
|
||||
9. **Record** the result in `ledger.md`, update `queue.md`, and promote only a
|
||||
durable operating lesson or decision into `.epiphany/project-memory.md`.
|
||||
|
||||
## Completion Standard
|
||||
|
||||
A pass is complete when the repaired system produces more specific downstream
|
||||
consequences without leaving split authority, erasing the seed witness, or
|
||||
requiring workshop prose to explain the canonical note.
|
||||
|
||||
Word count, heading count, and link density are useful pressure gauges. They do
|
||||
not certify depth. The qualitative parity standard lives in `benchmark.md`.
|
||||
|
||||
## Required Evidence
|
||||
|
||||
- source paths and exact seed witnesses read;
|
||||
- canonical owner notes changed;
|
||||
- authority map before and after;
|
||||
- unresolved questions and explicit non-decisions;
|
||||
- hostile/marginal and historical stress findings;
|
||||
- verification commands and results;
|
||||
- rendered-page inspection when published content changed.
|
||||
|
||||
If timing, interaction, or runtime behavior matters to a Ghostlight contract,
|
||||
verify that path in Ghostlight's owning repository rather than inventing a lore
|
||||
proxy here.
|
||||
69
workshop/deepening/benchmark.md
Normal file
69
workshop/deepening/benchmark.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# Institutional Depth Benchmark
|
||||
|
||||
Kalsa's target is causal parity with AetheriaLore's strongest institutional
|
||||
work, not imitation of Aetheria's setting, voice, ontology, or page count.
|
||||
|
||||
## Qualitative Dimensions
|
||||
|
||||
Review each major institution or system across these dimensions:
|
||||
|
||||
1. material or social function;
|
||||
2. legible authority and enforcement limits;
|
||||
3. recruitment, training, rank, compensation, discipline, succession, and exit;
|
||||
4. resources, records, places, tools, maintenance, and supply chains;
|
||||
5. formal doctrine versus governing practice;
|
||||
6. beneficiaries, payers, excluded people, and refusal costs;
|
||||
7. dependencies, clients, rivals, funders, imitators, and parasites;
|
||||
8. variation by class, region, lineage, language, and magical capacity;
|
||||
9. historical crises and path dependence;
|
||||
10. internal contradiction and active factions;
|
||||
11. consequences when players help, harm, capture, expose, or reform it;
|
||||
12. provenance: adopted fact, attributed belief, inference, or unresolved design.
|
||||
|
||||
Use the following review scale as a discussion aid:
|
||||
|
||||
- **0 — absent:** the note supplies no usable answer.
|
||||
- **1 — asserted:** an answer is named but its mechanism or consequence is
|
||||
unsupported.
|
||||
- **2 — causal:** actors, resources, limits, and downstream effects are clear
|
||||
enough to reason from.
|
||||
- **3 — stress-tested:** the answer survives conflict, subordinate experience,
|
||||
historical pressure, and interaction with neighboring institutions.
|
||||
|
||||
The numbers are derived review state. They are not lore, and their sum is not a
|
||||
depth score.
|
||||
|
||||
## Parity Condition
|
||||
|
||||
Kalsa reaches institutional parity when:
|
||||
|
||||
- every high-impact institution is at least causal in every critical dimension;
|
||||
- its defining contradictions and player-facing consequences are stress-tested;
|
||||
- cross-institution dependencies have clear owner notes instead of duplicated
|
||||
summaries;
|
||||
- a hostile or marginal reading exposes perspective and conflict rather than a
|
||||
missing material mechanism;
|
||||
- new events can be derived from the system without workshop explanation;
|
||||
- comparison against representative Aetheria notes finds comparable causal
|
||||
density even when Kalsa uses fewer or shorter pages.
|
||||
|
||||
## Aetheria Reference Sample
|
||||
|
||||
Observed on 2026-08-22 in `F:\Projects\AetheriaLore\Aetheria\Worldbuilding`:
|
||||
|
||||
- 146 non-index Markdown notes;
|
||||
- about 58,919 words and 849 Obsidian wikilinks;
|
||||
- representative deep institutional notes include:
|
||||
- `Post-Elysium/Factions/Death Monkey Explosives.md`;
|
||||
- `Post-Elysium/Factions/Megiddo.md`;
|
||||
- `Post-Elysium/Factions/Wavecrafters.md`;
|
||||
- `Post-Elysium/Factions/House Valence.md`;
|
||||
- `Post-Elysium/Factions/Daedal Houses.md`;
|
||||
- `Pre-Elysium/Factions/Powers/Minor/Rossum & Douglas.md`.
|
||||
|
||||
Those metrics establish scale and supply comparison candidates. They are not
|
||||
quotas. Recalculate structural diagnostics with:
|
||||
|
||||
```powershell
|
||||
.\scripts\lore\measure-depth.ps1
|
||||
```
|
||||
22
workshop/deepening/ledger.md
Normal file
22
workshop/deepening/ledger.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Deepening Ledger
|
||||
|
||||
This ledger records completed or abandoned passes. It is an audit surface, not
|
||||
a canon changelog.
|
||||
|
||||
## 2026-08-22 — Operating surface initialization
|
||||
|
||||
- **Scope:** critique/repair contract, benchmark, queue, pass template,
|
||||
project-memory boundary, and read-only lore diagnostics.
|
||||
- **Canonical lore changed:** none.
|
||||
- **Seed changed:** none; integrity remains governed by `seed/manifest.md`.
|
||||
- **Durable judgment:** canon, workshop process, site projection, and project
|
||||
memory require separate owners.
|
||||
- **Verification:** seed integrity, publication-boundary checks, structural
|
||||
diagnostics, and positive/negative lore-tool tests passed. The canonical
|
||||
wikilink check correctly reported unresolved planned `Polities/` targets;
|
||||
those links remain open content work rather than being suppressed here.
|
||||
- **Next pressure:** KALSA-001, the seed claim inventory and canonical owner map.
|
||||
|
||||
Future entries should link their pass file and name the canonical owners changed,
|
||||
verification evidence, unresolved pressure, and whether any memory judgment was
|
||||
promoted or retired.
|
||||
111
workshop/deepening/pass-template.md
Normal file
111
workshop/deepening/pass-template.md
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
---
|
||||
pass_id: KALSA-000
|
||||
status: draft
|
||||
target: replace-me
|
||||
canonical_owner: unresolved
|
||||
started: YYYY-MM-DD
|
||||
completed:
|
||||
---
|
||||
|
||||
# KALSA-000 — Pass Title
|
||||
|
||||
## Objective
|
||||
|
||||
What real setting and play outcome does this pass serve?
|
||||
|
||||
## Source inventory
|
||||
|
||||
### Seed evidence
|
||||
|
||||
- Exact paths and claims.
|
||||
|
||||
### Adopted canon
|
||||
|
||||
- Current owner notes and claims.
|
||||
|
||||
### Provisional, contested, rumored, or deprecated material
|
||||
|
||||
- Keep authority states separate.
|
||||
|
||||
### Contradictions and unknowns
|
||||
|
||||
- What cannot yet be reconciled or assumed?
|
||||
|
||||
## Authority map before repair
|
||||
|
||||
- **Owner:**
|
||||
- **Inputs:**
|
||||
- **Outputs:**
|
||||
- **Derived state:**
|
||||
- **Forbidden writers:**
|
||||
- **Shared paths:**
|
||||
- **Cut line:**
|
||||
|
||||
## Institutional model
|
||||
|
||||
- **Function:**
|
||||
- **Authority and enforcement limits:**
|
||||
- **Recruitment, training, rank, compensation, discipline, succession, exit:**
|
||||
- **Material support, records, places, tools, maintenance, supply:**
|
||||
- **Doctrine versus practice:**
|
||||
- **Beneficiaries, costs, exclusion, refusal:**
|
||||
- **Dependencies and neighboring institutions:**
|
||||
- **Variation by class, region, lineage, language, magic:**
|
||||
- **Historical formation and path dependence:**
|
||||
- **Internal contradiction and factions:**
|
||||
- **Player-facing consequences:**
|
||||
|
||||
## Critique
|
||||
|
||||
- Generic fantasy defaults:
|
||||
- Missing material constraints:
|
||||
- False consensus or absent subordinate perspectives:
|
||||
- Consequences that stop at the page edge:
|
||||
- Duplicate or ambiguous owners:
|
||||
|
||||
## Repair
|
||||
|
||||
- Canonical files to change:
|
||||
- Obsolete authority to delete or demote first:
|
||||
- Smallest coherent new model:
|
||||
- Links and downstream consequences to add:
|
||||
|
||||
## Falsification
|
||||
|
||||
### Hostile or marginal perspective
|
||||
|
||||
Who sees the institution from below, outside, or under coercion, and what does
|
||||
their experience reveal?
|
||||
|
||||
### Historical stress case
|
||||
|
||||
What crisis, scarcity, succession, war, migration, or magical failure tests the
|
||||
claimed mechanism?
|
||||
|
||||
## Authority map after repair
|
||||
|
||||
- **Owner:**
|
||||
- **Inputs:**
|
||||
- **Outputs:**
|
||||
- **Derived state:**
|
||||
- **Forbidden writers:**
|
||||
- **Shared paths:**
|
||||
- **Deleted or demoted authority:**
|
||||
|
||||
## Verification
|
||||
|
||||
- [ ] Seed hashes unchanged.
|
||||
- [ ] Wikilinks resolve without new ambiguity.
|
||||
- [ ] Workshop, seed, and memory remain outside the publication root.
|
||||
- [ ] Production Quartz build succeeds.
|
||||
- [ ] Affected rendered pages were inspected.
|
||||
- [ ] Git diff changes only intended owners and supporting indexes.
|
||||
- [ ] Benchmark review completed without treating counts as proof.
|
||||
|
||||
## Decision record
|
||||
|
||||
- **Adopted:**
|
||||
- **Explicitly not adopted:**
|
||||
- **Unresolved:**
|
||||
- **Project-memory judgment promoted or retired:**
|
||||
- **Next highest-value pressure:**
|
||||
17
workshop/deepening/queue.md
Normal file
17
workshop/deepening/queue.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Deepening Queue
|
||||
|
||||
The queue orders pressure; it does not grant canon authority. Re-rank it after
|
||||
each pass according to downstream consequence, ownership risk, and Ghostlight
|
||||
play value.
|
||||
|
||||
| ID | Target | Why now | State | Blocker or required input |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| KALSA-001 | Seed claim inventory and canonical owner map | Every later repair depends on knowing which timestamp witness supports which live subject and where duplicate ownership could form. | ready | Canonical `Kalsa/` skeleton and index paths |
|
||||
| KALSA-002 | Present date and distance from the Fall | Institutional reproduction, language change, technological decay, and living memory cannot be stressed coherently without temporal scale. | blocked | Operator decision or bounded options pass |
|
||||
| KALSA-003 | Promethean inheritance as institutions | Measures, High Lojban, machine interfaces, law, and priestly custody are the connective tissue between formal imposition and lived culture. | ready after KALSA-001 | Owner map for history, language, and technical custody |
|
||||
| KALSA-004 | Magic-to-institution dependency map | The seed describes magical populations and cultural tendencies, but labor, law, kinship, education, disability, trade, ecology, and state capacity need causal owners. | ready after KALSA-001 | Canonical magic and society owner notes |
|
||||
| KALSA-005 | Public versus Ghostlight-sensitive boundary | Publication must not leak workshop material or secrets whose discovery drives play. | blocked | Operator and Ghostlight ownership decision |
|
||||
| KALSA-006 | First society deepening pilot | One complete society pass will test the rubric, provenance scheme, and gameplay handoff before the loop scales. | queued | KALSA-001 and selection of pilot society |
|
||||
|
||||
Do not fill the queue with every imaginable fantasy topic. Add an item when a
|
||||
real owner, dependency, contradiction, or play requirement creates pressure.
|
||||
Loading…
Reference in a new issue