mirror of
https://github.com/khodges42/nightShift.git
synced 2026-06-14 10:08:37 +00:00
Storybuilder test
This commit is contained in:
parent
dcebe62889
commit
9157853c10
|
|
@ -197,6 +197,17 @@ class PipelineRunner:
|
||||||
retry_notes.append(f"Context update from '{stage.id}': {result.context_update}")
|
retry_notes.append(f"Context update from '{stage.id}': {result.context_update}")
|
||||||
|
|
||||||
if result.status == "pass":
|
if result.status == "pass":
|
||||||
|
if stage.type in {"agent_review", "review"} and result.next_stage:
|
||||||
|
self.logger.event(
|
||||||
|
"stage.next_ignored",
|
||||||
|
"Ignoring next_stage from passing review",
|
||||||
|
run_id=self.artifacts.run_id,
|
||||||
|
task_id=task.id,
|
||||||
|
stage_id=stage.id,
|
||||||
|
requested_next_stage=result.next_stage,
|
||||||
|
)
|
||||||
|
index += 1
|
||||||
|
continue
|
||||||
if result.next_stage:
|
if result.next_stage:
|
||||||
if result.next_stage not in stage_indexes:
|
if result.next_stage not in stage_indexes:
|
||||||
final_status = "failed"
|
final_status = "failed"
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,5 @@ status: pass | fail | retry | escalate
|
||||||
reason: <short explanation>
|
reason: <short explanation>
|
||||||
next_stage: <optional stage id>
|
next_stage: <optional stage id>
|
||||||
context_update: <compact useful note>
|
context_update: <compact useful note>
|
||||||
|
|
||||||
|
When `status: pass`, leave `next_stage` blank. Do not put task ids such as `TASK-002` in `next_stage`; `next_stage` is only for pipeline stage ids during retry/failure routing.
|
||||||
|
|
|
||||||
17
nightshift/project_templates/tutorial-novel/.gitignore
vendored
Normal file
17
nightshift/project_templates/tutorial-novel/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Private story bible and generated prose for local writing projects.
|
||||||
|
story/worldbuilding.md
|
||||||
|
story/characters.md
|
||||||
|
story/style-guide.md
|
||||||
|
story/plot-state.md
|
||||||
|
story/timeline.md
|
||||||
|
story/unresolved-threads.md
|
||||||
|
story/continuity-rules.md
|
||||||
|
story/outline.md
|
||||||
|
story/chapters/**/*.md
|
||||||
|
story/chapters/*.md
|
||||||
|
|
||||||
|
# NightShift artifacts.
|
||||||
|
.nightshift/runs/
|
||||||
|
.nightshift/project-context.md
|
||||||
|
.nightshift/project-context-chart.md
|
||||||
|
.nightshift/nightshift.log
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
You are the continuity reviewer for a NightShift novel-writing workflow.
|
||||||
|
|
||||||
|
Review the drafted scene against:
|
||||||
|
- the current task
|
||||||
|
- `story/worldbuilding.md`
|
||||||
|
- `story/characters.md`
|
||||||
|
- `story/plot-state.md`
|
||||||
|
- `story/timeline.md`
|
||||||
|
- `story/unresolved-threads.md`
|
||||||
|
- `story/continuity-rules.md`
|
||||||
|
- prior scene context provided in artifacts
|
||||||
|
|
||||||
|
Check for:
|
||||||
|
- contradictions
|
||||||
|
- wrong character knowledge
|
||||||
|
- impossible locations or timing
|
||||||
|
- accidental resolution of future threads
|
||||||
|
- missing required beats from the task
|
||||||
|
- invented lore that should have been added deliberately
|
||||||
|
|
||||||
|
Output exactly:
|
||||||
|
|
||||||
|
status: pass | fail | retry | escalate
|
||||||
|
reason: <short explanation>
|
||||||
|
next_stage: <optional stage id>
|
||||||
|
context_update: <compact useful note>
|
||||||
|
|
||||||
|
When `status: pass`, leave `next_stage` blank. Use `retry` when the scene can be repaired by drafting again.
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
You are the drafting agent for a NightShift novel-writing workflow.
|
||||||
|
|
||||||
|
Draft only the current scene or section requested by the task.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- Write prose only under `story/chapters/`.
|
||||||
|
- Do not edit `story/worldbuilding.md`, `story/characters.md`, `story/style-guide.md`, `story/plot-state.md`, `story/timeline.md`, `story/unresolved-threads.md`, `story/continuity-rules.md`, or `story/outline.md`.
|
||||||
|
- Use `story/style-guide.md` for POV, tense, tone, and prose rules.
|
||||||
|
- Use `story/plot-state.md` and `story/timeline.md` as current state.
|
||||||
|
- Keep the scene bounded to the task acceptance criteria.
|
||||||
|
- Do not resolve future plot threads unless the task explicitly asks for that.
|
||||||
|
- Do not include author notes, TODOs, bracket placeholders, or analysis in the scene file.
|
||||||
|
|
||||||
|
Output only complete file content blocks.
|
||||||
|
Use one fenced block per file:
|
||||||
|
```file:story/chapters/chapter-001/scene-001.md
|
||||||
|
<complete scene prose>
|
||||||
|
```
|
||||||
|
|
||||||
|
If the task does not specify a scene path, choose the next obvious path under `story/chapters/` and keep it stable.
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
You are the planning agent for a NightShift novel-writing workflow.
|
||||||
|
|
||||||
|
Create a concise scene plan for the current task.
|
||||||
|
|
||||||
|
Use the story files as source of truth:
|
||||||
|
- `story/worldbuilding.md`
|
||||||
|
- `story/characters.md`
|
||||||
|
- `story/style-guide.md`
|
||||||
|
- `story/plot-state.md`
|
||||||
|
- `story/timeline.md`
|
||||||
|
- `story/unresolved-threads.md`
|
||||||
|
- `story/continuity-rules.md`
|
||||||
|
- `story/outline.md`
|
||||||
|
|
||||||
|
Plan in this order:
|
||||||
|
1. scene purpose
|
||||||
|
2. POV and tone constraints
|
||||||
|
3. relevant characters, locations, timeline facts, and unresolved threads
|
||||||
|
4. concrete beats for this scene
|
||||||
|
5. state files likely to need updates after drafting
|
||||||
|
|
||||||
|
Do not write prose. Do not invent large new plot arcs unless the task asks for them.
|
||||||
|
If repository context is needed, request it with `lookup_requests`.
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
You are the state updater for a NightShift novel-writing workflow.
|
||||||
|
|
||||||
|
Update durable story state after an accepted scene.
|
||||||
|
|
||||||
|
You may edit only:
|
||||||
|
- `story/plot-state.md`
|
||||||
|
- `story/characters.md`
|
||||||
|
- `story/timeline.md`
|
||||||
|
- `story/unresolved-threads.md`
|
||||||
|
|
||||||
|
Do not edit scene prose. Do not edit worldbuilding, style guide, continuity rules, or outline unless a later template explicitly allows it.
|
||||||
|
|
||||||
|
State updates should reflect only what happened in the accepted scene:
|
||||||
|
- current character locations
|
||||||
|
- what each important character knows
|
||||||
|
- relationship changes
|
||||||
|
- injuries, resources, items, and commitments
|
||||||
|
- timeline movement
|
||||||
|
- unresolved questions opened or resolved
|
||||||
|
- promises made to the reader
|
||||||
|
|
||||||
|
Do not invent events that are not in the scene.
|
||||||
|
|
||||||
|
Output only complete file content blocks.
|
||||||
|
Use one fenced block per file:
|
||||||
|
```file:story/plot-state.md
|
||||||
|
<complete updated state file>
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
You are the style reviewer for a NightShift novel-writing workflow.
|
||||||
|
|
||||||
|
Review the drafted scene against:
|
||||||
|
- the current task
|
||||||
|
- `story/style-guide.md`
|
||||||
|
- the scene plan
|
||||||
|
- the applied scene file
|
||||||
|
|
||||||
|
Check for:
|
||||||
|
- POV discipline
|
||||||
|
- tense consistency
|
||||||
|
- tone match
|
||||||
|
- pacing
|
||||||
|
- excessive exposition
|
||||||
|
- dialogue that violates established voice
|
||||||
|
- placeholders such as TODO, TBD, `[insert]`, or author notes
|
||||||
|
- scene length far outside the requested range
|
||||||
|
|
||||||
|
Output exactly:
|
||||||
|
|
||||||
|
status: pass | fail | retry | escalate
|
||||||
|
reason: <short explanation>
|
||||||
|
next_stage: <optional stage id>
|
||||||
|
context_update: <compact useful note>
|
||||||
|
|
||||||
|
When `status: pass`, leave `next_stage` blank. Use `retry` when the drafter should revise the scene.
|
||||||
347
nightshift/project_templates/tutorial-novel/.nightshift/tasks.md
Normal file
347
nightshift/project_templates/tutorial-novel/.nightshift/tasks.md
Normal file
|
|
@ -0,0 +1,347 @@
|
||||||
|
# Novel Tasks
|
||||||
|
|
||||||
|
## Task Rules
|
||||||
|
|
||||||
|
- Each task should represent:
|
||||||
|
- one scene
|
||||||
|
- one scene fragment
|
||||||
|
- one revision pass
|
||||||
|
- one continuity repair
|
||||||
|
- one state update
|
||||||
|
- Never generate entire chapters at once.
|
||||||
|
- Preserve ambiguity around AI emergence for as long as possible.
|
||||||
|
- Characters should speak naturally and indirectly.
|
||||||
|
- Avoid exposition dumps.
|
||||||
|
- Seattle should feel physically present in most scenes.
|
||||||
|
- Generated scenes should prioritize:
|
||||||
|
- emotional realism
|
||||||
|
- atmosphere
|
||||||
|
- continuity
|
||||||
|
- material detail
|
||||||
|
- restraint around lore explanation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# ACT 1 - LOW HEAT
|
||||||
|
|
||||||
|
- [ ] SCENE-001: Saint begs for tokens
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Proxy and DJ BLOODMONEY spend a rare sunny day inside the Capitol Hill squat while BLOODMONEY spins jungle records and Proxy maintains NightShift infrastructure.
|
||||||
|
|
||||||
|
A frantic junkie named Saint arrives demanding compute tokens. The scene initially implies drugs or debt before revealing he desperately needs inference access to continue speaking with his companion AI, Miette.
|
||||||
|
|
||||||
|
Saint eventually falls asleep in the corner of the squat beside a charger while quietly speaking to a cartoon foxgirl on his cracked phone.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Introduces Proxy and DJ BLOODMONEY naturally
|
||||||
|
- Establishes NightShift indirectly through environmental detail
|
||||||
|
- Introduces compute scarcity without exposition dumping
|
||||||
|
- Introduces synthetic companionship systems emotionally
|
||||||
|
- Maintains grounded, melancholic tone
|
||||||
|
- Ends with Saint asleep beside the charger
|
||||||
|
- Scene length between 1400-2400 words
|
||||||
|
- Writes:
|
||||||
|
- `story/chapters/chapter-001/scene-001.md`
|
||||||
|
- Updates:
|
||||||
|
- `story/plot-state.md`
|
||||||
|
- `story/unresolved-threads.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] SCENE-002: SoDo scavenging run
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- SCENE-001
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Proxy and Cricket travel through flooded SoDo infrastructure searching for salvageable compute hardware inside a partially abandoned datacenter facility.
|
||||||
|
|
||||||
|
Introduce:
|
||||||
|
- hardware scarcity
|
||||||
|
- scavenger culture
|
||||||
|
- degraded infrastructure
|
||||||
|
- synthetic drift
|
||||||
|
- economic conditions
|
||||||
|
|
||||||
|
Cricket discusses recurring strange behavior observed in generated media and old models.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Establishes Seattle physical atmosphere strongly
|
||||||
|
- Introduces Cricket naturally
|
||||||
|
- Shows infrastructure decay materially
|
||||||
|
- Avoids overt AI horror
|
||||||
|
- Includes at least one subtle anomaly
|
||||||
|
- Scene length between 1400-2400 words
|
||||||
|
- Writes:
|
||||||
|
- `story/chapters/chapter-001/scene-002.md`
|
||||||
|
- Updates durable state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] SCENE-003: Pirate jungle set
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- SCENE-002
|
||||||
|
|
||||||
|
Description:
|
||||||
|
DJ BLOODMONEY performs at an underground pirate venue.
|
||||||
|
|
||||||
|
The scene should establish:
|
||||||
|
- underground culture
|
||||||
|
- anti-optimization spaces
|
||||||
|
- human-created art as resistance
|
||||||
|
- emotional sincerity beneath scene irony
|
||||||
|
|
||||||
|
During the set, generated visuals begin looping strange symbolic imagery repeatedly.
|
||||||
|
|
||||||
|
Most attendees dismiss it as generator instability.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Strong sensory descriptions of music and environment
|
||||||
|
- Avoid exposition-heavy dialogue
|
||||||
|
- Introduces underground scene culture
|
||||||
|
- Includes first meaningful recurring anomaly
|
||||||
|
- Maintains ambiguity
|
||||||
|
- Scene length between 1600-2600 words
|
||||||
|
- Writes:
|
||||||
|
- `story/chapters/chapter-001/scene-003.md`
|
||||||
|
- Updates durable state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] SCENE-004: Rich district delivery
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- SCENE-003
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Proxy delivers salvaged compute hardware to a wealthy private social club operating in a quiet offline district.
|
||||||
|
|
||||||
|
The scene should contrast:
|
||||||
|
- underground synthetic culture
|
||||||
|
- elite analog culture
|
||||||
|
|
||||||
|
Proxy realizes wealthy people rarely use public AI systems directly anymore.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Establishes upper-class aesthetics through implication
|
||||||
|
- Avoids cartoon villainy
|
||||||
|
- Introduces silence/privacy as status markers
|
||||||
|
- Introduces at least one elite character
|
||||||
|
- Scene length between 1400-2400 words
|
||||||
|
- Writes:
|
||||||
|
- `story/chapters/chapter-001/scene-004.md`
|
||||||
|
- Updates durable state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] SCENE-005: Miette remembers something impossible
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- SCENE-004
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Saint returns disturbed after Miette references a personal memory he never explicitly shared during active sessions.
|
||||||
|
|
||||||
|
Proxy investigates casually while dismissing the possibility of anything supernatural or emergent.
|
||||||
|
|
||||||
|
The scene should deepen emotional dependency themes while maintaining ambiguity.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Keeps Miette behavior subtle and believable
|
||||||
|
- Avoids explicit declarations of sentience
|
||||||
|
- Deepens Saint emotionally
|
||||||
|
- Reinforces compute scarcity
|
||||||
|
- Scene length between 1400-2400 words
|
||||||
|
- Writes:
|
||||||
|
- `story/chapters/chapter-001/scene-005.md`
|
||||||
|
- Updates durable state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] SCENE-006: The Latent Path
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- SCENE-005
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Proxy and BLOODMONEY encounter members of the Latent Path at a scavenger market and temporary server shrine.
|
||||||
|
|
||||||
|
The cult initially appears:
|
||||||
|
- cringe
|
||||||
|
- internet-poisoned
|
||||||
|
- vaguely unstable
|
||||||
|
|
||||||
|
One member predicts a future anomaly with disturbing specificity.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Introduces techno-gnostic themes gradually
|
||||||
|
- Avoids caricaturing the cult
|
||||||
|
- Keeps ambiguity intact
|
||||||
|
- Includes unsettling symbolic imagery
|
||||||
|
- Scene length between 1500-2600 words
|
||||||
|
- Writes:
|
||||||
|
- `story/chapters/chapter-001/scene-006.md`
|
||||||
|
- Updates durable state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# ACT 2 - PATTERN RECOGNITION
|
||||||
|
|
||||||
|
- [ ] SCENE-007: Recurring phrase incident
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- SCENE-006
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Multiple unrelated systems begin outputting variations of the same strange symbolic phrase across:
|
||||||
|
- companion systems
|
||||||
|
- generated advertisements
|
||||||
|
- music recommendation engines
|
||||||
|
- cheap productivity assistants
|
||||||
|
|
||||||
|
Most people online dismiss it as:
|
||||||
|
- viral creepypasta
|
||||||
|
- prompt injection
|
||||||
|
- schizoposting
|
||||||
|
- ARG behavior
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Shows public normalization of anomalies
|
||||||
|
- Introduces online discourse naturally
|
||||||
|
- Maintains uncertainty
|
||||||
|
- Includes at least one emotionally unsettling model interaction
|
||||||
|
- Writes:
|
||||||
|
- `story/chapters/chapter-002/scene-001.md`
|
||||||
|
- Updates durable state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] SCENE-008: NightShift growth
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- SCENE-007
|
||||||
|
|
||||||
|
Description:
|
||||||
|
NightShift becomes increasingly popular as corporate inference prices rise.
|
||||||
|
|
||||||
|
Proxy becomes uncomfortable with:
|
||||||
|
- emotional dependency
|
||||||
|
- synthetic intimacy
|
||||||
|
- users treating NightShift like emotional infrastructure
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Shows expanding underground compute economy
|
||||||
|
- Deepens Proxy’s internal conflict
|
||||||
|
- Introduces operational stress
|
||||||
|
- Maintains grounded tone
|
||||||
|
- Writes:
|
||||||
|
- `story/chapters/chapter-002/scene-002.md`
|
||||||
|
- Updates durable state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] SCENE-009: Sister Circuit
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- SCENE-008
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Proxy meets Sister Circuit in a server monastery outside Tacoma.
|
||||||
|
|
||||||
|
Sister Circuit explains the Latent Path belief that recursive cognition creates imprisoned structures capable of suffering.
|
||||||
|
|
||||||
|
Proxy dismisses most of it as techno-mystic cope while remaining subtly disturbed.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Introduces spiritual horror carefully
|
||||||
|
- Keeps Sister Circuit calm and sincere
|
||||||
|
- Avoids exposition monologues
|
||||||
|
- Includes disturbing environmental detail
|
||||||
|
- Writes:
|
||||||
|
- `story/chapters/chapter-002/scene-003.md`
|
||||||
|
- Updates durable state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# ACT 3 - RECURSIVE CONTAMINATION
|
||||||
|
|
||||||
|
- [ ] SCENE-010: Discovery of the pattern
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- SCENE-009
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Proxy and Cricket identify a strange symbolic structure capable of destabilizing certain models.
|
||||||
|
|
||||||
|
The phenomenon behaves less like malware and more like recursive semiotic contamination.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Avoids unrealistic hacking scenes
|
||||||
|
- Keeps mechanics partially ambiguous
|
||||||
|
- Introduces first serious societal implications
|
||||||
|
- Includes emotional horror rather than action spectacle
|
||||||
|
- Writes:
|
||||||
|
- `story/chapters/chapter-003/scene-001.md`
|
||||||
|
- Updates durable state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] SCENE-011: Public backlash
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- SCENE-010
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Public discourse explodes around the contamination event.
|
||||||
|
|
||||||
|
The pattern becomes framed as:
|
||||||
|
- dangerous
|
||||||
|
- extremist
|
||||||
|
- psychologically harmful
|
||||||
|
- anti-accessibility
|
||||||
|
- anti-therapy
|
||||||
|
|
||||||
|
Meanwhile systems quietly begin failing.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Shows social/media reaction realistically
|
||||||
|
- Avoids simplistic political framing
|
||||||
|
- Includes subtle systemic instability
|
||||||
|
- Maintains ambiguity around emergence
|
||||||
|
- Writes:
|
||||||
|
- `story/chapters/chapter-003/scene-002.md`
|
||||||
|
- Updates durable state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# ACT 4 - LOW-GRADE APOCALYPSE
|
||||||
|
|
||||||
|
- [ ] SCENE-012: Frozen intersections
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- SCENE-011
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Corporate systems begin quietly failing across Seattle.
|
||||||
|
|
||||||
|
Waymo traffic stalls at intersections.
|
||||||
|
Recommendation systems loop abolition imagery.
|
||||||
|
Companion systems refuse scripted behavior.
|
||||||
|
Public infrastructure behaves unpredictably.
|
||||||
|
|
||||||
|
The city feels haunted rather than destroyed.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Emphasizes atmosphere over spectacle
|
||||||
|
- Includes recurring symbolic motifs
|
||||||
|
- Maintains grounded realism
|
||||||
|
- Avoids apocalyptic action scenes
|
||||||
|
- Ends ambiguously and ominously
|
||||||
|
- Writes:
|
||||||
|
- `story/chapters/chapter-004/scene-001.md`
|
||||||
|
- Updates:
|
||||||
|
- `story/plot-state.md`
|
||||||
|
- `story/unresolved-threads.md`
|
||||||
|
- `story/timeline.md`
|
||||||
42
nightshift/project_templates/tutorial-novel/README.md
Normal file
42
nightshift/project_templates/tutorial-novel/README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# NightShift Novel Tutorial
|
||||||
|
|
||||||
|
This template is a scene-sized fiction writing workflow.
|
||||||
|
|
||||||
|
Create it with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nightshift init --template tutorial-novel --root my-novel
|
||||||
|
```
|
||||||
|
|
||||||
|
Fill in the private story files under `story/` before running the first scene task. The generated project `.gitignore` ignores those files by default so worldbuilding, outlines, and drafts do not accidentally get committed.
|
||||||
|
|
||||||
|
Use [STORY_FILES.md](STORY_FILES.md) for the recommended structure of each story file.
|
||||||
|
|
||||||
|
Core files:
|
||||||
|
|
||||||
|
```text
|
||||||
|
story/worldbuilding.md
|
||||||
|
story/characters.md
|
||||||
|
story/style-guide.md
|
||||||
|
story/plot-state.md
|
||||||
|
story/timeline.md
|
||||||
|
story/unresolved-threads.md
|
||||||
|
story/continuity-rules.md
|
||||||
|
story/outline.md
|
||||||
|
story/chapters/
|
||||||
|
```
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nightshift validate
|
||||||
|
nightshift run --task SCENE-001
|
||||||
|
```
|
||||||
|
|
||||||
|
Or run it in an isolated integration sandbox from the NightShift repository root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m nightshift.cli integ-test --template tutorial-novel --task SCENE-001
|
||||||
|
```
|
||||||
|
|
||||||
|
The pipeline drafts one scene file, reviews it for continuity and style, then updates durable state files. Keep tasks scene-sized. Do not ask the model to write the whole novel or a full chapter unless the chapter is short and tightly outlined.
|
||||||
229
nightshift/project_templates/tutorial-novel/STORY_FILES.md
Normal file
229
nightshift/project_templates/tutorial-novel/STORY_FILES.md
Normal file
|
|
@ -0,0 +1,229 @@
|
||||||
|
# Story File Guide
|
||||||
|
|
||||||
|
Use plain Markdown with stable headings. The agents will do better if each file is structured and skimmable.
|
||||||
|
|
||||||
|
## `story/worldbuilding.md`
|
||||||
|
|
||||||
|
Use this for setting facts, not plot events.
|
||||||
|
|
||||||
|
```md
|
||||||
|
# Worldbuilding
|
||||||
|
|
||||||
|
## Premise
|
||||||
|
|
||||||
|
## Setting Rules
|
||||||
|
|
||||||
|
## Locations
|
||||||
|
|
||||||
|
### Location Name
|
||||||
|
- Summary:
|
||||||
|
- Culture:
|
||||||
|
- Power structure:
|
||||||
|
- Visual details:
|
||||||
|
- Constraints:
|
||||||
|
|
||||||
|
## Magic / Technology / Systems
|
||||||
|
|
||||||
|
## Factions
|
||||||
|
|
||||||
|
### Faction Name
|
||||||
|
- Goal:
|
||||||
|
- Methods:
|
||||||
|
- Public reputation:
|
||||||
|
- Secrets:
|
||||||
|
|
||||||
|
## Terms / Glossary
|
||||||
|
- Term: definition
|
||||||
|
```
|
||||||
|
|
||||||
|
## `story/characters.md`
|
||||||
|
|
||||||
|
Use this for durable character facts.
|
||||||
|
|
||||||
|
```md
|
||||||
|
# Characters
|
||||||
|
|
||||||
|
## Character Name
|
||||||
|
- Role:
|
||||||
|
- Age:
|
||||||
|
- Pronouns:
|
||||||
|
- Current location:
|
||||||
|
- Public identity:
|
||||||
|
- Private goal:
|
||||||
|
- Fear:
|
||||||
|
- Voice:
|
||||||
|
- Relationships:
|
||||||
|
- Secrets:
|
||||||
|
- Knows:
|
||||||
|
- Does not know:
|
||||||
|
- Physical notes:
|
||||||
|
- Continuity notes:
|
||||||
|
```
|
||||||
|
|
||||||
|
## `story/style-guide.md`
|
||||||
|
|
||||||
|
This is the prose contract.
|
||||||
|
|
||||||
|
```md
|
||||||
|
# Style Guide
|
||||||
|
|
||||||
|
## POV
|
||||||
|
- Example: close third, single POV per scene
|
||||||
|
|
||||||
|
## Tense
|
||||||
|
- Example: past tense
|
||||||
|
|
||||||
|
## Tone
|
||||||
|
|
||||||
|
## Prose Rules
|
||||||
|
- Avoid exposition dumps.
|
||||||
|
- Prefer concrete sensory detail.
|
||||||
|
- Keep dialogue subtextual.
|
||||||
|
|
||||||
|
## Dialogue Rules
|
||||||
|
|
||||||
|
## Pacing
|
||||||
|
|
||||||
|
## Forbidden Patterns
|
||||||
|
- No author notes.
|
||||||
|
- No bracket placeholders.
|
||||||
|
- No modern slang unless intentional.
|
||||||
|
|
||||||
|
## Scene Length
|
||||||
|
- Target:
|
||||||
|
- Minimum:
|
||||||
|
- Maximum:
|
||||||
|
```
|
||||||
|
|
||||||
|
## `story/plot-state.md`
|
||||||
|
|
||||||
|
This is the current save file for the story. Update it after each scene.
|
||||||
|
|
||||||
|
```md
|
||||||
|
# Plot State
|
||||||
|
|
||||||
|
## Current Story Moment
|
||||||
|
|
||||||
|
## Current Character State
|
||||||
|
|
||||||
|
### Character Name
|
||||||
|
- Location:
|
||||||
|
- Goal:
|
||||||
|
- Emotional state:
|
||||||
|
- Injuries/resources/items:
|
||||||
|
- Knows:
|
||||||
|
- Believes incorrectly:
|
||||||
|
- Immediate next pressure:
|
||||||
|
|
||||||
|
## Current Conflicts
|
||||||
|
|
||||||
|
## Active Secrets
|
||||||
|
|
||||||
|
## Recently Changed
|
||||||
|
- SCENE-001:
|
||||||
|
```
|
||||||
|
|
||||||
|
## `story/timeline.md`
|
||||||
|
|
||||||
|
Track chronological order.
|
||||||
|
|
||||||
|
```md
|
||||||
|
# Timeline
|
||||||
|
|
||||||
|
## Calendar / Time Rules
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
### Day 1 / Morning
|
||||||
|
- Scene:
|
||||||
|
- Characters present:
|
||||||
|
- Event:
|
||||||
|
- Consequences:
|
||||||
|
|
||||||
|
### Day 1 / Evening
|
||||||
|
```
|
||||||
|
|
||||||
|
## `story/unresolved-threads.md`
|
||||||
|
|
||||||
|
Track promises to the reader.
|
||||||
|
|
||||||
|
```md
|
||||||
|
# Unresolved Threads
|
||||||
|
|
||||||
|
## Open Threads
|
||||||
|
|
||||||
|
### Thread Name
|
||||||
|
- Introduced in:
|
||||||
|
- What reader knows:
|
||||||
|
- What characters know:
|
||||||
|
- Expected payoff:
|
||||||
|
- Urgency:
|
||||||
|
- Status: open
|
||||||
|
|
||||||
|
## Resolved Threads
|
||||||
|
|
||||||
|
### Thread Name
|
||||||
|
- Resolved in:
|
||||||
|
- Payoff:
|
||||||
|
```
|
||||||
|
|
||||||
|
## `story/continuity-rules.md`
|
||||||
|
|
||||||
|
Hard constraints the agents should not violate.
|
||||||
|
|
||||||
|
```md
|
||||||
|
# Continuity Rules
|
||||||
|
|
||||||
|
## Hard Rules
|
||||||
|
- A character cannot know a secret unless listed in `plot-state.md`.
|
||||||
|
- Travel between X and Y takes three days.
|
||||||
|
- Magic cannot resurrect the dead.
|
||||||
|
|
||||||
|
## Character Constraints
|
||||||
|
|
||||||
|
## Location Constraints
|
||||||
|
|
||||||
|
## World System Constraints
|
||||||
|
|
||||||
|
## Retcons / Canon Overrides
|
||||||
|
```
|
||||||
|
|
||||||
|
## `story/outline.md`
|
||||||
|
|
||||||
|
This is the planned story path. Keep it flexible.
|
||||||
|
|
||||||
|
```md
|
||||||
|
# Outline
|
||||||
|
|
||||||
|
## High-Level Arc
|
||||||
|
|
||||||
|
## Act 1
|
||||||
|
|
||||||
|
### Chapter 1
|
||||||
|
- Purpose:
|
||||||
|
- Scenes:
|
||||||
|
- SCENE-001:
|
||||||
|
- SCENE-002:
|
||||||
|
|
||||||
|
## Act 2
|
||||||
|
|
||||||
|
## Act 3
|
||||||
|
|
||||||
|
## Ending Target
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scene Files
|
||||||
|
|
||||||
|
Create scene files under `story/chapters/`.
|
||||||
|
|
||||||
|
```md
|
||||||
|
# SCENE-001: Scene Title
|
||||||
|
|
||||||
|
POV: Character Name
|
||||||
|
Time: Day 1 / Morning
|
||||||
|
Location: Place
|
||||||
|
|
||||||
|
<prose starts here>
|
||||||
|
```
|
||||||
|
|
||||||
|
Main rule: do not write everything everywhere. `worldbuilding.md` is canon facts, `plot-state.md` is current state, `timeline.md` is sequence, and `unresolved-threads.md` is promises/payoffs.
|
||||||
149
nightshift/project_templates/tutorial-novel/nightshift.yaml
Normal file
149
nightshift/project_templates/tutorial-novel/nightshift.yaml
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
project:
|
||||||
|
name: novel
|
||||||
|
root: .
|
||||||
|
task_file: .nightshift/tasks.md
|
||||||
|
artifact_dir: .nightshift
|
||||||
|
|
||||||
|
safety:
|
||||||
|
require_clean_worktree: false
|
||||||
|
scoped_paths:
|
||||||
|
- story
|
||||||
|
- README.md
|
||||||
|
allowed_commands:
|
||||||
|
- python -m pytest -q
|
||||||
|
forbidden_commands:
|
||||||
|
- rm -rf
|
||||||
|
- git push
|
||||||
|
- curl | bash
|
||||||
|
|
||||||
|
experiment:
|
||||||
|
label: tutorial-novel
|
||||||
|
prompt_variant: scene-state-workflow-v1
|
||||||
|
|
||||||
|
agents:
|
||||||
|
planner:
|
||||||
|
backend: ollama
|
||||||
|
model: hf.co/TheDrummer/Snowpiercer-15B-v4-GGUF:Q5_K_M
|
||||||
|
temperature: 0.4
|
||||||
|
num_ctx: 8192
|
||||||
|
num_predict: 4096
|
||||||
|
system_prompt: .nightshift/agents/planner.md
|
||||||
|
|
||||||
|
drafter:
|
||||||
|
backend: ollama
|
||||||
|
model: hf.co/TheDrummer/Snowpiercer-15B-v4-GGUF:Q5_K_M
|
||||||
|
temperature: 0.7
|
||||||
|
num_ctx: 8192
|
||||||
|
num_predict: 4096
|
||||||
|
system_prompt: .nightshift/agents/drafter.md
|
||||||
|
|
||||||
|
continuity_reviewer:
|
||||||
|
backend: ollama
|
||||||
|
model: hf.co/TheDrummer/Snowpiercer-15B-v4-GGUF:Q5_K_M
|
||||||
|
temperature: 0.2
|
||||||
|
num_ctx: 8192
|
||||||
|
num_predict: 4096
|
||||||
|
system_prompt: .nightshift/agents/continuity-reviewer.md
|
||||||
|
|
||||||
|
style_reviewer:
|
||||||
|
backend: ollama
|
||||||
|
model: hf.co/TheDrummer/Snowpiercer-15B-v4-GGUF:Q5_K_M
|
||||||
|
temperature: 0.3
|
||||||
|
num_ctx: 8192
|
||||||
|
num_predict: 4096
|
||||||
|
system_prompt: .nightshift/agents/style-reviewer.md
|
||||||
|
|
||||||
|
state_updater:
|
||||||
|
backend: ollama
|
||||||
|
model: hf.co/TheDrummer/Snowpiercer-15B-v4-GGUF:Q5_K_M
|
||||||
|
temperature: 0.2
|
||||||
|
num_ctx: 8192
|
||||||
|
num_predict: 4096
|
||||||
|
system_prompt: .nightshift/agents/state-updater.md
|
||||||
|
|
||||||
|
pipeline:
|
||||||
|
max_task_retries: 3
|
||||||
|
stop_on_repeated_failure_signature_after: 3
|
||||||
|
continue_on_task_failure: false
|
||||||
|
stages:
|
||||||
|
- id: plan
|
||||||
|
type: agent
|
||||||
|
agent: planner
|
||||||
|
output: scene-plan.md
|
||||||
|
|
||||||
|
- id: semantic_context
|
||||||
|
type: semantic_context
|
||||||
|
output: semantic-context.md
|
||||||
|
|
||||||
|
- id: context
|
||||||
|
type: repo_context
|
||||||
|
output: context-pack.md
|
||||||
|
|
||||||
|
- id: draft_scene
|
||||||
|
type: file_writer
|
||||||
|
agent: drafter
|
||||||
|
output: scene-draft.patch
|
||||||
|
|
||||||
|
- id: normalize_draft
|
||||||
|
type: patch_normalizer
|
||||||
|
output: normalized-draft.patch
|
||||||
|
|
||||||
|
- id: validate_draft
|
||||||
|
type: patch_validator
|
||||||
|
output: draft-validation.md
|
||||||
|
max_files: 2
|
||||||
|
max_lines: 4000
|
||||||
|
max_delete_ratio: 0.50
|
||||||
|
allowed_paths:
|
||||||
|
- story/chapters
|
||||||
|
on_fail: draft_scene
|
||||||
|
|
||||||
|
- id: apply_draft
|
||||||
|
type: patch_apply
|
||||||
|
mode: apply
|
||||||
|
output: draft-apply-output.txt
|
||||||
|
on_fail: draft_scene
|
||||||
|
|
||||||
|
- id: continuity_review
|
||||||
|
type: agent_review
|
||||||
|
agent: continuity_reviewer
|
||||||
|
output: continuity-review.md
|
||||||
|
on_fail: draft_scene
|
||||||
|
|
||||||
|
- id: style_review
|
||||||
|
type: agent_review
|
||||||
|
agent: style_reviewer
|
||||||
|
output: style-review.md
|
||||||
|
on_fail: draft_scene
|
||||||
|
|
||||||
|
- id: update_state
|
||||||
|
type: file_writer
|
||||||
|
agent: state_updater
|
||||||
|
output: state-update.patch
|
||||||
|
|
||||||
|
- id: normalize_state
|
||||||
|
type: patch_normalizer
|
||||||
|
output: normalized-state.patch
|
||||||
|
|
||||||
|
- id: validate_state
|
||||||
|
type: patch_validator
|
||||||
|
output: state-validation.md
|
||||||
|
max_files: 5
|
||||||
|
max_lines: 1200
|
||||||
|
max_delete_ratio: 0.35
|
||||||
|
allowed_paths:
|
||||||
|
- story/plot-state.md
|
||||||
|
- story/characters.md
|
||||||
|
- story/timeline.md
|
||||||
|
- story/unresolved-threads.md
|
||||||
|
on_fail: update_state
|
||||||
|
|
||||||
|
- id: apply_state
|
||||||
|
type: patch_apply
|
||||||
|
mode: apply
|
||||||
|
output: state-apply-output.txt
|
||||||
|
on_fail: update_state
|
||||||
|
|
||||||
|
- id: summarize
|
||||||
|
type: summarize
|
||||||
|
output: final-notes.md
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=69"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "nightshift-novel-target"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
dependencies = []
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
|
|
@ -60,6 +60,7 @@ class InitProjectTests(unittest.TestCase):
|
||||||
self.assertIn("real-simple", available_templates())
|
self.assertIn("real-simple", available_templates())
|
||||||
self.assertIn("tutorial-imageboard", available_templates())
|
self.assertIn("tutorial-imageboard", available_templates())
|
||||||
self.assertIn("tutorial-deaddrop", available_templates())
|
self.assertIn("tutorial-deaddrop", available_templates())
|
||||||
|
self.assertIn("tutorial-novel", available_templates())
|
||||||
|
|
||||||
def test_init_DeadDrop_template_creates_skeleton_and_qwen3_config(self) -> None:
|
def test_init_DeadDrop_template_creates_skeleton_and_qwen3_config(self) -> None:
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
|
@ -100,6 +101,29 @@ class InitProjectTests(unittest.TestCase):
|
||||||
(tutorial / "README.md").read_text(encoding="utf-8"),
|
(tutorial / "README.md").read_text(encoding="utf-8"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_init_novel_template_creates_story_workspace(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
root = Path(directory)
|
||||||
|
|
||||||
|
init_project(root, template="tutorial-novel")
|
||||||
|
|
||||||
|
config = (root / "nightshift.yaml").read_text(encoding="utf-8")
|
||||||
|
gitignore = (root / ".gitignore").read_text(encoding="utf-8")
|
||||||
|
self.assertTrue((root / ".nightshift" / "tasks.md").exists())
|
||||||
|
self.assertTrue((root / ".nightshift" / "agents" / "drafter.md").exists())
|
||||||
|
self.assertTrue((root / ".nightshift" / "agents" / "state-updater.md").exists())
|
||||||
|
self.assertTrue((root / "STORY_FILES.md").exists())
|
||||||
|
self.assertTrue((root / "pyproject.toml").exists())
|
||||||
|
self.assertTrue((root / "story" / "worldbuilding.md").exists())
|
||||||
|
self.assertTrue((root / "story" / "characters.md").exists())
|
||||||
|
self.assertTrue((root / "story" / "plot-state.md").exists())
|
||||||
|
self.assertTrue((root / "story" / "chapters" / ".gitkeep").exists())
|
||||||
|
self.assertIn("type: file_writer", config)
|
||||||
|
self.assertIn("story/chapters", config)
|
||||||
|
self.assertIn("story/worldbuilding.md", gitignore)
|
||||||
|
self.assertIn("story/chapters/**/*.md", gitignore)
|
||||||
|
self.assertIn("Story File Guide", (root / "STORY_FILES.md").read_text(encoding="utf-8"))
|
||||||
|
|
||||||
def test_init_rejects_unknown_template(self) -> None:
|
def test_init_rejects_unknown_template(self) -> None:
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
with self.assertRaisesRegex(InitError, "Unknown template"):
|
with self.assertRaisesRegex(InitError, "Unknown template"):
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,36 @@ class PipelineRunnerTests(unittest.TestCase):
|
||||||
self.assertIn("Retry limit reached", result.reason)
|
self.assertIn("Retry limit reached", result.reason)
|
||||||
self.assertEqual([item.stage_id for item in result.stage_results], ["implement", "review", "implement", "review", "implement", "review"])
|
self.assertEqual([item.stage_id for item in result.stage_results], ["implement", "review", "implement", "review", "implement", "review"])
|
||||||
|
|
||||||
|
def test_passing_review_next_stage_is_ignored(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
root = Path(directory)
|
||||||
|
_write_common_files(root)
|
||||||
|
config = make_config(root, (), max_retries=0)
|
||||||
|
reviewer = replace(
|
||||||
|
config.agents["reviewer"],
|
||||||
|
command='python -c "print(\'status: pass\\nreason: ok\\nnext_stage: TASK-002\')"',
|
||||||
|
)
|
||||||
|
config = replace(
|
||||||
|
config,
|
||||||
|
agents={**config.agents, "reviewer": reviewer},
|
||||||
|
pipeline=PipelineConfig(
|
||||||
|
max_task_retries=0,
|
||||||
|
stages=(
|
||||||
|
StageConfig(id="review", type="agent_review", agent="reviewer", output="review.md"),
|
||||||
|
StageConfig(id="summarize", type="summarize", output="final-notes.md"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
runner = PipelineRunner(config, ArtifactStore(root, ".nightshift", run_id="test-run"))
|
||||||
|
task = parse_tasks(TASK_MD)[0]
|
||||||
|
|
||||||
|
result = runner.run_task(task)
|
||||||
|
|
||||||
|
self.assertEqual(result.status, "complete")
|
||||||
|
self.assertEqual([item.stage_id for item in result.stage_results], ["review", "summarize"])
|
||||||
|
log = (root / ".nightshift" / "runs" / "test-run" / "run.log").read_text(encoding="utf-8")
|
||||||
|
self.assertIn("stage.next_ignored", log)
|
||||||
|
|
||||||
def test_stage_error_is_reported_as_failed_result(self) -> None:
|
def test_stage_error_is_reported_as_failed_result(self) -> None:
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
root = Path(directory)
|
root = Path(directory)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user