document the writing pipeline and record Phase 2
CLAUDE.md gains the writing/ layout, the voice-sheet pipeline and its direction of truth, the sync commands, and a pointer that Ground Truth must never be copied into this repo. The writer-owned/code-owned table now covers writing/. The restructure plan records Phase 2 as executed, and closes its first open question: the writing handbook does exist, in the Obsidian vault, so STYLE.md became a digest with pointers rather than the reconstruction the plan assumed. Also noted there: the vault's opening-scene spec records SC-101's tone check as BLOCKED on a one-page Voice Sheet that was never written. STYLE.md now holds that sheet with four fields explicitly unset. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -17,7 +17,9 @@ to fix — it is the checkout path). The Unity project is a **subdirectory**:
|
|||||||
/Users/lennart/Dev/nightlcub-arcadia/ # git root
|
/Users/lennart/Dev/nightlcub-arcadia/ # git root
|
||||||
├── CLAUDE.md, AGENTS.md # this reference
|
├── CLAUDE.md, AGENTS.md # this reference
|
||||||
├── docs/ # design + plan docs (markdown)
|
├── docs/ # design + plan docs (markdown)
|
||||||
|
├── writing/ # writer-facing context — see §4
|
||||||
├── tools/YarnCheck/ # .NET 9 Yarn compiler/player, no Unity needed
|
├── tools/YarnCheck/ # .NET 9 Yarn compiler/player, no Unity needed
|
||||||
|
├── tools/writing/ # voice-sheet sync (md <-> skill_bible.json)
|
||||||
├── .gitignore # macOS / IDE / toolchain rules
|
├── .gitignore # macOS / IDE / toolchain rules
|
||||||
└── NightclubArcadia/ # ← the Unity project (-projectPath)
|
└── NightclubArcadia/ # ← the Unity project (-projectPath)
|
||||||
├── .gitignore # Unity rules (anchored, must stay here)
|
├── .gitignore # Unity rules (anchored, must stay here)
|
||||||
@@ -49,7 +51,7 @@ AI Navigation 2.0.14, ProBuilder 6.1.2, Test Framework 1.7.0. Yarn Spinner is an
|
|||||||
mode while the Editor has it open — a single instance at a time. Always check before
|
mode while the Editor has it open — a single instance at a time. Always check before
|
||||||
any `-batchmode` invocation (§3.1).
|
any `-batchmode` invocation (§3.1).
|
||||||
4. **Prefer YarnCheck over Unity for dialogue validation.** It is seconds, not minutes,
|
4. **Prefer YarnCheck over Unity for dialogue validation.** It is seconds, not minutes,
|
||||||
and needs no Editor lock (§5.3).
|
and needs no Editor lock (§5.4).
|
||||||
5. **Do not commit on the user's behalf** unless asked. Unity writes to tracked files
|
5. **Do not commit on the user's behalf** unless asked. Unity writes to tracked files
|
||||||
(`ProjectSettings/*`, `.meta`, scenes) as a side effect of simply being open, so
|
(`ProjectSettings/*`, `.meta`, scenes) as a side effect of simply being open, so
|
||||||
`git status` noise is expected and is not always yours.
|
`git status` noise is expected and is not always yours.
|
||||||
@@ -174,6 +176,25 @@ NightclubArcadia/Assets/Dialogue/
|
|||||||
└── Objects/ Chair
|
└── Objects/ Chair
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The writer-facing context sits at the repo root, outside `Assets/` so it gets no `.meta` files:
|
||||||
|
|
||||||
|
```
|
||||||
|
writing/
|
||||||
|
├── README.md session entry point
|
||||||
|
├── STYLE.md register, tone moves, ban list, branching rules ← read first
|
||||||
|
├── YARN-PRIMER.md the one page of syntax a writer needs
|
||||||
|
├── GLOSSARY.md in-world and project vocabulary
|
||||||
|
├── voices/ the eleven skill voices, one sheet each (generated — see §4.7)
|
||||||
|
├── characters/ named NPCs, dialogue-facing
|
||||||
|
├── scenes/ per-scene briefs: intent, beats, checks, constraints
|
||||||
|
└── lore/ candidates.md — the three identity readings and their hard rules
|
||||||
|
```
|
||||||
|
|
||||||
|
Design material deeper than this lives in Obsidian, not the repo:
|
||||||
|
`~/Obsidian-Vaults/obsidian-vault/Projects/Active/NightclubArcadia/`. **`Ground Truth.md` there
|
||||||
|
is the solution document — never copy, quote, or summarise it into this repo**, including into
|
||||||
|
commit messages. The game's premise is that the protagonist's identity never resolves.
|
||||||
|
|
||||||
**Dialogue is already plain text in the repo.** It is authored in `.yarn` files, not inside
|
**Dialogue is already plain text in the repo.** It is authored in `.yarn` files, not inside
|
||||||
the Unity Editor, and any new `.yarn` file dropped anywhere under `Assets/Dialogue/` is picked
|
the Unity Editor, and any new `.yarn` file dropped anywhere under `Assets/Dialogue/` is picked
|
||||||
up automatically by the `**/*.yarn` glob in the `.yarnproject`. Adding a scene or a character
|
up automatically by the `**/*.yarn` glob in the `.yarnproject`. Adding a scene or a character
|
||||||
@@ -249,13 +270,41 @@ candidates.json → Tools ▸ Nightclub Arcadia ▸ (CandidateSystemSetup)
|
|||||||
Those setup scripts also register a `[DidReloadScripts]` callback, so in practice the assets
|
Those setup scripts also register a `[DidReloadScripts]` callback, so in practice the assets
|
||||||
regenerate on script reload too. The JSON is the source of truth either way.
|
regenerate on script reload too. The JSON is the source of truth either way.
|
||||||
|
|
||||||
### 4.6 Code-owned vs writer-owned
|
### 4.6 The voice-sheet pipeline
|
||||||
|
|
||||||
|
The Skill Bible prose used to live as a `\n`-escaped string inside `skill_bible.json`, which no
|
||||||
|
writer could comfortably read or revise. It now lives in `writing/voices/*.md`, and truth flows
|
||||||
|
in opposite directions for the two halves of each sheet:
|
||||||
|
|
||||||
|
```
|
||||||
|
PROSE writing/voices/<id>.md -> skill_bible.json -> Skills/Definitions/*.asset
|
||||||
|
MACHINE skill_bible.json -> the "Machine fields" table in the .md
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/writing/sync_voices.py --check # verify; exit 1 on drift
|
||||||
|
python3 tools/writing/sync_voices.py --to-json # fold edited prose into the bible
|
||||||
|
python3 tools/writing/sync_voices.py --to-md # refresh the machine table from the bible
|
||||||
|
```
|
||||||
|
|
||||||
|
`tools/writing/voicelib.py` does the parsing and guarantees `notes -> dict -> notes` is identity
|
||||||
|
for all eleven skills. `--to-json` on unchanged sheets is byte-identical, so it is safe to run
|
||||||
|
at any time.
|
||||||
|
|
||||||
|
`VoiceSheetSyncTests` (EditMode) enforces the same invariant, so drift fails the build instead
|
||||||
|
of silently shipping a voice that no longer matches the sheet the next scene is written against.
|
||||||
|
It deliberately does not reimplement the markdown parser in C# — it asserts the shipped prose
|
||||||
|
appears verbatim in the sheet, which catches drift in either direction with no second parser to
|
||||||
|
keep in step.
|
||||||
|
|
||||||
|
### 4.7 Code-owned vs writer-owned
|
||||||
|
|
||||||
| Writer-owned (edit freely, no Unity) | Code-owned (needs an engineer) |
|
| Writer-owned (edit freely, no Unity) | Code-owned (needs an engineer) |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `Assets/Dialogue/**/*.yarn` | `Assets/Scripts/**/*.cs` |
|
| `writing/**` | `Assets/Scripts/**/*.cs` |
|
||||||
| `<<declare>>` lines in `Common.yarn` | `Assets/Editor/**/*.cs` |
|
| `Assets/Dialogue/**/*.yarn` | `Assets/Editor/**/*.cs` |
|
||||||
| `Assets/Skills/skill_bible.json` (prose fields) | Yarn command/function *implementations* |
|
| `<<declare>>` lines in `Common.yarn` | Yarn command/function *implementations* |
|
||||||
|
| `writing/voices/*.md` (prose → the bible, §4.6) | `Assets/Skills/skill_bible.json` (machine fields) |
|
||||||
| `Assets/Candidates/candidates.json` (`status`, `sceneId`, `notes`, `unlockFlag`) | `.asset`, `.prefab`, `.unity`, `.meta` |
|
| `Assets/Candidates/candidates.json` (`status`, `sceneId`, `notes`, `unlockFlag`) | `.asset`, `.prefab`, `.unity`, `.meta` |
|
||||||
| `docs/` writing docs | `ProjectSettings/**` |
|
| `docs/` writing docs | `ProjectSettings/**` |
|
||||||
|
|
||||||
@@ -278,11 +327,19 @@ or a branch.** If a writing task requires either, that is a pipeline bug — fil
|
|||||||
`$self_lean_*` / `$found_*` in any `.yarn` file resolves to a real id in `CandidateDatabase`.
|
`$self_lean_*` / `$found_*` in any `.yarn` file resolves to a real id in `CandidateDatabase`.
|
||||||
It is a tripwire, not a proof.
|
It is a tripwire, not a proof.
|
||||||
|
|
||||||
### 5.2 One-click in-Editor run
|
### 5.2 Voice-sheet sync
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/writing/sync_voices.py --check
|
||||||
|
```
|
||||||
|
|
||||||
|
Seconds, no Unity. Run it after touching `writing/voices/*.md` or `skill_bible.json` (§4.6).
|
||||||
|
|
||||||
|
### 5.3 One-click in-Editor run
|
||||||
|
|
||||||
`Tools ▸ Nightclub Arcadia ▸ Run Skill EditMode Tests` (`Assets/Editor/SkillSystemTestRunner.cs`).
|
`Tools ▸ Nightclub Arcadia ▸ Run Skill EditMode Tests` (`Assets/Editor/SkillSystemTestRunner.cs`).
|
||||||
|
|
||||||
### 5.3 YarnCheck — validate dialogue without Unity ⭐
|
### 5.4 YarnCheck — validate dialogue without Unity ⭐
|
||||||
|
|
||||||
`tools/YarnCheck` is a .NET 9 console app that compiles **and plays** the Yarn scripts using
|
`tools/YarnCheck` is a .NET 9 console app that compiles **and plays** the Yarn scripts using
|
||||||
the exact Yarn Spinner DLLs shipped in `Packages/dev.yarnspinner.unity/Runtime/DLLs`, so it
|
the exact Yarn Spinner DLLs shipped in `Packages/dev.yarnspinner.unity/Runtime/DLLs`, so it
|
||||||
|
|||||||
+39
-17
@@ -10,7 +10,7 @@ Companion reading: `docs/skill-system-refactor-plan.md`, `docs/candidate-system-
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 0. Status — Phase 0 is complete
|
## 0. Status — Phases 0 and 2 are complete
|
||||||
|
|
||||||
Executed 2026-08-25. The working tree is clean and tagged `pre-restructure`.
|
Executed 2026-08-25. The working tree is clean and tagged `pre-restructure`.
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ the UI layer, player control setup, the dialogue-vs-menu interaction fix, and th
|
|||||||
A full pre-flight backup was taken first (`.tar.gz` of the tree excluding `Library/`, `Temp/`,
|
A full pre-flight backup was taken first (`.tar.gz` of the tree excluding `Library/`, `Temp/`,
|
||||||
`Logs/`, and build output) plus a separate copy of the scene file.
|
`Logs/`, and build output) plus a separate copy of the scene file.
|
||||||
|
|
||||||
Everything below from §1 onward is unchanged proposal. Nothing in §2–§5 has been applied.
|
Phase 2 (the writing pipeline) is also done — see §4. Phases 1, 3, 4 and 5 remain proposal.
|
||||||
|
|
||||||
### 0.1 The binary scene — diagnosis corrected
|
### 0.1 The binary scene — diagnosis corrected
|
||||||
|
|
||||||
@@ -405,20 +405,41 @@ Effort marks are rough: **S** ≲1h, **M** a half day, **L** a day or more.
|
|||||||
`.gitattributes` before any restructuring is deliberate — the moment work happens on a branch,
|
`.gitattributes` before any restructuring is deliberate — the moment work happens on a branch,
|
||||||
scene merges without a merge driver will corrupt files.
|
scene merges without a merge driver will corrupt files.
|
||||||
|
|
||||||
### Phase 2 — writing pipeline (no Unity changes, fully parallel to Phase 3+)
|
### Phase 2 — writing pipeline ✅ DONE (2026-08-25)
|
||||||
|
|
||||||
| # | Step | Detail | Effort |
|
| # | Step | Status |
|
||||||
|---|---|---|---|
|
|---|---|---|
|
||||||
| 2.1 | Create `writing/` skeleton + templates | §2.2, §2.3 | S |
|
| 2.1 | `writing/` skeleton + templates | done |
|
||||||
| 2.2 | **Write `writing/STYLE.md`** | The missing handbook. Recover the rules from `.yarn` header comments and the three `docs/` plans. Highest-value single item in this plan. | M |
|
| 2.2 | `writing/STYLE.md` | done — **digested from the vault handbook, not reconstructed** (below) |
|
||||||
| 2.3 | Extract `skill_bible.json` `notes` → `writing/voices/*.md` | one-shot script, 11 files | M |
|
| 2.3 | Extract `skill_bible.json` prose → `writing/voices/*.md` | done — 11 sheets, lossless round-trip |
|
||||||
| 2.4 | Write the sync script md → JSON + an EditMode drift test | §2.4 | M |
|
| 2.4 | Sync script + EditMode drift test | done — `tools/writing/sync_voices.py`, `VoiceSheetSyncTests` |
|
||||||
| 2.5 | Author the three character sheets | Bartender, Bradford Kane, Chevalier Cassian Thal | M |
|
| 2.5 | Character sheets | done — Bartender, Chevalier Cassian Thal, Bradford Kane |
|
||||||
| 2.6 | Write `writing/README.md` + `YARN-PRIMER.md` | The Cowork session entry point, incl. the YarnCheck caveat (§2.6) | S |
|
| 2.6 | `README.md` + `YARN-PRIMER.md` | done, plus `GLOSSARY.md` and `lore/candidates.md` |
|
||||||
| 2.7 | Move `SC101.yarn`'s header rules into `writing/scenes/sc101-*.md` | leave a one-line pointer in the `.yarn` | S |
|
| 2.7 | SC-101 header rules → scene brief | done — header trimmed to the six rules that bind while editing, rationale moved to the brief |
|
||||||
|
|
||||||
Phase 2 touches **no Unity asset at all**. It can proceed while the Editor is open and while
|
Validated: EditMode **42/42** (three new), YarnCheck compiles 9 files / 35 nodes, sheets in sync.
|
||||||
Phase 3 is in flight, and it is what unblocks Cowork. Consider doing it first.
|
The drift test was verified to actually fail on drift, not merely to pass.
|
||||||
|
|
||||||
|
#### Phase 2 — what changed against the plan
|
||||||
|
|
||||||
|
Two things, both discovered during execution.
|
||||||
|
|
||||||
|
**The handbook exists.** `docs/skill-system-refactor-plan.md` §2 names it, and it is at
|
||||||
|
`~/Obsidian-Vaults/obsidian-vault/Projects/Active/NightclubArcadia/` — a 874-line
|
||||||
|
`[[Mystery Writing Handbook]]`, plus `[[Character Sheet Handbook]]`, `[[Skill System Draft]]`,
|
||||||
|
`[[Lorebook]]`, `[[Candidate System — Design Explanation]]`, scene specs, and `[[Ground Truth]]`.
|
||||||
|
So §2.2 became a **digest with pointers** rather than a reconstruction, which is both more
|
||||||
|
accurate and honest about where the authoring home is. The vault remains that home.
|
||||||
|
|
||||||
|
**`[[Ground Truth]]` must never enter this repo.** It is the solution document. The premise of
|
||||||
|
the game is that the protagonist's identity never resolves, and a copy of the answer in a git
|
||||||
|
history is exactly how that leaks. This is now stated in `writing/README.md` and `CLAUDE.md` §4.1.
|
||||||
|
|
||||||
|
One thing surfaced that is worth acting on independently: `[[000_OpeningSceneTemplate]]` records
|
||||||
|
SC-101's tone check as **BLOCKED** because the one-page Voice Sheet has never been written — the
|
||||||
|
scene spec could not honestly certify its own tone field. `writing/STYLE.md` §9 now holds that
|
||||||
|
sheet with four fields explicitly unset. Filling them is about half an hour of decisions and it
|
||||||
|
unblocks a check that has been stuck for a while.
|
||||||
|
|
||||||
### Phase 3 — scene restructure (the risky part)
|
### Phase 3 — scene restructure (the risky part)
|
||||||
|
|
||||||
@@ -530,9 +551,10 @@ the world every session.
|
|||||||
|
|
||||||
## 7. Open questions
|
## 7. Open questions
|
||||||
|
|
||||||
1. **Does a writing handbook exist outside the repo?** `docs/candidate-system-implementation-plan.md`
|
1. ~~Does a writing handbook exist outside the repo?~~ **Answered — yes** (Phase 2 notes). It is in the
|
||||||
says it exists but is not checked in. If there is a document somewhere, importing it beats
|
Obsidian vault and is now pointed at rather than duplicated. The open part is whether any of
|
||||||
reconstructing it (Phase 2.2 assumes reconstruction).
|
it should eventually move into the repo wholesale; the current answer is no, because the vault
|
||||||
|
is reachable from writing sessions and duplication would drift.
|
||||||
2. ~~How did the scene become binary?~~ **Answered** (§0.1): Force Text only converts when the
|
2. ~~How did the scene become binary?~~ **Answered** (§0.1): Force Text only converts when the
|
||||||
setting changes, so anything that entered binary stays binary. The open part is *how* it
|
setting changes, so anything that entered binary stays binary. The open part is *how* it
|
||||||
first entered binary — most likely created or imported during a Force Binary window. Adding a
|
first entered binary — most likely created or imported during a Force Binary window. Adding a
|
||||||
|
|||||||
Reference in New Issue
Block a user