Build vault, memory, and Quartz operating surfaces

This commit is contained in:
Meta Vens 2026-08-22 12:36:50 +02:00
commit 6b62f17ccf
28 changed files with 1383 additions and 0 deletions

45
.epiphany/README.md Normal file
View 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.

View 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.

View 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

View 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
View 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
View 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
View 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
View file

@ -0,0 +1,4 @@
{
"alwaysUpdateLinks": true,
"promptDelete": true
}

1
.obsidian/appearance.json vendored Normal file
View file

@ -0,0 +1 @@
{}

1
.obsidian/community-plugins.json vendored Normal file
View file

@ -0,0 +1 @@
[]

24
.obsidian/core-plugins.json vendored Normal file
View 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
View 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
View 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
View 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.

View 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."

View 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."

View 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."

View 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
}
}

View 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
View 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
View 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
View 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: [],
};

View 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;
}
}

View 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.

View 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
```

View 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.

View 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:**

View 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.