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}")
|
||||
|
||||
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 not in stage_indexes:
|
||||
final_status = "failed"
|
||||
|
|
|
|||
|
|
@ -12,3 +12,5 @@ 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. 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("tutorial-imageboard", 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:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
|
|
@ -100,6 +101,29 @@ class InitProjectTests(unittest.TestCase):
|
|||
(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:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
with self.assertRaisesRegex(InitError, "Unknown template"):
|
||||
|
|
|
|||
|
|
@ -153,6 +153,36 @@ class PipelineRunnerTests(unittest.TestCase):
|
|||
self.assertIn("Retry limit reached", result.reason)
|
||||
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:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user