dsh-novel-write-engine 0.1.3 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -22,8 +22,8 @@ declare const SCENE_TYPES: readonly ["战斗/动作章", "日常过渡章", "情
22
22
  type SceneType = (typeof SCENE_TYPES)[number];
23
23
  /** A violation is blocking unless the rule pack declares it advisory. */
24
24
  type Severity = 'blocker' | 'advisory';
25
- /** All twenty-two service operations. */
26
- declare const OPERATIONS: readonly ["initProject", "archiveChapter", "amend", "rollback", "submitWorldPlan", "submitChapterPlan", "submitDraft", "submitReview", "submitRevision", "runCheck", "getStatus", "getBrief", "countWords", "queryState", "diff", "auditHistory", "advise", "export", "panelSummary", "panelChapters", "panelGates", "stats"];
25
+ /** All thirty-one service operations (22 inherited from 0.1.x, 9 book-level). */
26
+ declare const OPERATIONS: readonly ["initProject", "archiveChapter", "amend", "rollback", "reopenChapter", "submitWorldPlan", "submitChapterPlan", "submitDraft", "submitReview", "submitRevision", "runCheck", "submitBookPlan", "submitOutline", "importOutline", "submitWorldbook", "getStatus", "getBrief", "countWords", "queryState", "diff", "auditHistory", "advise", "search", "doctor", "report", "guide", "export", "panelSummary", "panelChapters", "panelGates", "stats", "restoreBackup"];
27
27
  type Operation = (typeof OPERATIONS)[number];
28
28
  /** Structured, non-fatal note attached to a successful result. */
29
29
  interface Warning {
@@ -72,6 +72,8 @@ declare const ERROR_CODES: {
72
72
  readonly PATH_ESCAPE: "ENGINE_PATH_ESCAPE";
73
73
  readonly LOCK_TIMEOUT: "ENGINE_LOCK_TIMEOUT";
74
74
  readonly TXN_ABORTED: "ENGINE_TXN_ABORTED";
75
+ readonly JOURNAL_MISSING: "ENGINE_JOURNAL_MISSING";
76
+ readonly TXN_IN_PROGRESS: "ENGINE_TXN_IN_PROGRESS";
75
77
  readonly RULEPACK_INVALID: "ENGINE_RULEPACK_INVALID";
76
78
  readonly ID_EXHAUSTED: "ENGINE_ID_EXHAUSTED";
77
79
  readonly CONFIG_INVALID: "ENGINE_CONFIG_INVALID";
@@ -159,6 +161,7 @@ interface CheckDocument {
159
161
  content_hash: string;
160
162
  machine: {
161
163
  violations: Violation[];
164
+ advisories?: Violation[];
162
165
  metrics: WordMetrics;
163
166
  };
164
167
  semantic: {
@@ -169,10 +172,12 @@ interface CheckDocument {
169
172
  note: string;
170
173
  }>;
171
174
  };
175
+ /** Advisory codes the author has explicitly acknowledged for this content. */
176
+ acknowledged?: string[];
172
177
  verdict: 'pass' | 'fail';
173
178
  }
174
179
  /** Every persisted document whose damage status/panel can report. */
175
- type StateErrorCode = 'PROJECT_UNREADABLE' | 'PLAN_UNREADABLE' | 'WORLD_PLAN_UNREADABLE' | 'REVIEW_UNREADABLE' | 'CHECK_UNREADABLE' | 'CHAPTERS_UNREADABLE' | 'CHARACTERS_UNREADABLE' | 'WORLD_UNREADABLE' | 'LOCKS_UNREADABLE' | 'TIMELINE_UNREADABLE' | 'FORESHADOWS_UNREADABLE' | 'RELATIONSHIPS_UNREADABLE' | 'HANDOFF_UNREADABLE';
180
+ type StateErrorCode = 'PROJECT_UNREADABLE' | 'PLAN_UNREADABLE' | 'WORLD_PLAN_UNREADABLE' | 'REVIEW_UNREADABLE' | 'CHECK_UNREADABLE' | 'CHAPTERS_UNREADABLE' | 'CHARACTERS_UNREADABLE' | 'WORLD_UNREADABLE' | 'LOCKS_UNREADABLE' | 'TIMELINE_UNREADABLE' | 'FORESHADOWS_UNREADABLE' | 'RELATIONSHIPS_UNREADABLE' | 'HANDOFF_UNREADABLE' | 'OUTLINE_UNREADABLE' | 'WORLDBOOK_UNREADABLE';
176
181
  /** A state document that exists but cannot be read safely. */
177
182
  interface StateError {
178
183
  path: string;
@@ -187,6 +192,8 @@ interface WordMetrics {
187
192
  numbers: number;
188
193
  chinese_punct: number;
189
194
  english_punct: number;
195
+ /** All punctuation counted by the engine; total = cjk + words + numbers + punctuation. */
196
+ punctuation_chars: number;
190
197
  non_ws_chars: number;
191
198
  total_lines: number;
192
199
  non_empty_lines: number;
@@ -209,6 +216,10 @@ interface ChapterRecord {
209
216
  status: 'draft' | 'reviewed';
210
217
  content_hash: string;
211
218
  summary: ChapterSummary;
219
+ /** How hard this chapter was before it was archived. */
220
+ rounds?: RoundCounters;
221
+ final_check?: 'pass' | 'fail';
222
+ archived_at?: string;
212
223
  /** Keys allocated by the archive transaction; lets a cross-process retry replay. */
213
224
  assigned_ids?: Record<string, string>;
214
225
  /** Digest of the archive request that produced this row. */
@@ -223,6 +234,7 @@ interface ChapterSummary {
223
234
  key: string;
224
235
  title: string;
225
236
  description: string;
237
+ anchor?: string;
226
238
  }>;
227
239
  advanced: Array<{
228
240
  key: string;
@@ -256,6 +268,8 @@ interface RoundCounters {
256
268
  review: number;
257
269
  revise: number;
258
270
  check: number;
271
+ /** Lifetime rollback count on a chapter record; never stored in the live pipeline. */
272
+ rollbacks?: number;
259
273
  }
260
274
  /** How one ledger entity changed in this chapter. */
261
275
  type DeltaAction = 'created' | 'updated' | 'no_change';
@@ -287,6 +301,8 @@ interface ForeshadowDelta {
287
301
  action: DeltaAction;
288
302
  reason?: string;
289
303
  title?: string;
304
+ /** Verbatim manuscript quote used for verification; title stays a readable label. */
305
+ anchor?: string;
290
306
  description?: string;
291
307
  note?: string;
292
308
  new_info?: boolean;
@@ -329,6 +345,28 @@ interface ArchiveHandoff {
329
345
  act?: string;
330
346
  act_progress?: string;
331
347
  }
348
+ /** One issued task-book receipt; the brief itself is rendered on demand. */
349
+ interface BriefReceipt {
350
+ chapter: number;
351
+ stage: BriefStage;
352
+ digest: string;
353
+ issued_at: string;
354
+ }
355
+ /** `state/briefs.yaml`: append-only task-book evidence outside content transactions. */
356
+ interface BriefReceiptsDocument {
357
+ schema_version: 1;
358
+ receipts: BriefReceipt[];
359
+ }
360
+ /** The active draft's own metadata; P2 plan stays immutable after submission. */
361
+ interface DraftState {
362
+ chapter: number;
363
+ title: string;
364
+ scene_type: SceneType;
365
+ narrator: string;
366
+ summary: ChapterSummary;
367
+ content_hash: string;
368
+ updated_at: string;
369
+ }
332
370
  /** Pipeline document (`state/pipeline.yaml`). */
333
371
  interface PipelineState {
334
372
  schema_version: 1;
@@ -341,6 +379,8 @@ interface PipelineState {
341
379
  digest: string;
342
380
  }>;
343
381
  blocking: BlockingItem[];
382
+ /** Current draft summary/title; never written into the P2 plan file. */
383
+ draft?: DraftState;
344
384
  /** Hash of the review document last written by submitReview/submitRevision. */
345
385
  review_digest?: string;
346
386
  last_action: {
@@ -420,6 +460,7 @@ interface ForeshadowsDocument {
420
460
  status: 'planted' | 'dormant' | 'advanced' | 'resolved';
421
461
  resolved_at: number | null;
422
462
  description: string;
463
+ anchor?: string;
423
464
  advance_log: Array<{
424
465
  chapter: number;
425
466
  note: string;
@@ -526,6 +567,317 @@ interface ChapterPlanInput {
526
567
  /** Optional in 0.1.3: older plans remain readable; new plans may name 1–8 actors. */
527
568
  cast?: ChapterCastMember[];
528
569
  }
570
+ /** Book-level pitch and structure, persisted in `outline/outline.yaml`. */
571
+ interface BookPlan {
572
+ premise: string;
573
+ logline: string;
574
+ themes: string[];
575
+ tone: string;
576
+ pov: string;
577
+ audience: string;
578
+ selling_points: string[];
579
+ ending: string;
580
+ /** Strict mode requires a chapter card before P2/P3 for every chapter. */
581
+ strict: boolean;
582
+ /** True once the author explicitly chose strict on/off; used for the first-outline default. */
583
+ strict_explicit?: boolean;
584
+ }
585
+ /** One volume of the planned book. `end_chapter` is inclusive. */
586
+ interface OutlineVolume {
587
+ id: string;
588
+ title: string;
589
+ start_chapter: number;
590
+ end_chapter: number;
591
+ goal: string;
592
+ arc: string;
593
+ climax: string;
594
+ status: 'planned' | 'writing' | 'done';
595
+ }
596
+ /** One planned chapter card. Only the archive transaction may write the archived fields. */
597
+ interface OutlineChapter {
598
+ num: number;
599
+ volume: number | null;
600
+ title: string;
601
+ scene_type: SceneType;
602
+ goal: string;
603
+ conflict: string;
604
+ hook: string;
605
+ cast: string[];
606
+ target_words: [number, number];
607
+ notes: string;
608
+ status: 'planned' | 'archived';
609
+ archived_at: string | null;
610
+ word_count: number | null;
611
+ score: number | null;
612
+ }
613
+ /** The planning SSOT read by P1–P8, PG-13, report, doctor and export. */
614
+ interface OutlineDocument {
615
+ schema_version: 1;
616
+ updated_at: string;
617
+ book: BookPlan;
618
+ volumes: OutlineVolume[];
619
+ chapters: OutlineChapter[];
620
+ }
621
+ /** One worldbuilding rule/constraint. */
622
+ interface WorldbookRule {
623
+ name: string;
624
+ description: string;
625
+ }
626
+ /** One faction in the world bible. */
627
+ interface WorldbookFaction {
628
+ name: string;
629
+ description: string;
630
+ goal: string;
631
+ }
632
+ /** One location in the world bible. */
633
+ interface WorldbookLocation {
634
+ name: string;
635
+ description: string;
636
+ layout: string;
637
+ capacity: number;
638
+ }
639
+ /** One planned character (not the post-prose ledger entry). */
640
+ interface WorldbookCharacter {
641
+ name: string;
642
+ role: string;
643
+ description: string;
644
+ arc: string;
645
+ }
646
+ /** One glossary entry. */
647
+ interface WorldbookGlossary {
648
+ term: string;
649
+ definition: string;
650
+ }
651
+ /** One backstory event. */
652
+ interface WorldbookBackstory {
653
+ time: string;
654
+ event: string;
655
+ }
656
+ /** One declared numeric canon, checked against the manuscript on request. */
657
+ interface WorldbookFact {
658
+ alias: string;
659
+ value: number;
660
+ unit: string;
661
+ tolerance?: number;
662
+ note?: string;
663
+ }
664
+ /** Worldbuilding plan, persisted in `outline/worldbook.yaml`. */
665
+ interface WorldbookDocument {
666
+ schema_version: 1;
667
+ updated_at: string;
668
+ premise: string;
669
+ setting: string;
670
+ rules: WorldbookRule[];
671
+ factions: WorldbookFaction[];
672
+ locations: WorldbookLocation[];
673
+ characters: WorldbookCharacter[];
674
+ glossary: WorldbookGlossary[];
675
+ backstory: WorldbookBackstory[];
676
+ /** Optional numeric canon; old documents omit it. */
677
+ facts?: WorldbookFact[];
678
+ themes: string[];
679
+ }
680
+ /** One hit from `novel_search`; the scope names the source family. */
681
+ interface SearchResultItem {
682
+ kind: 'chapters' | 'summaries' | 'outline' | 'worldbook' | 'ledgers';
683
+ chapter?: number;
684
+ title?: string;
685
+ paragraph?: number;
686
+ snippet: string;
687
+ }
688
+ /** Search response; `total` counts every matching segment, `results` is capped by `limit`. */
689
+ interface SearchReport {
690
+ query: string;
691
+ scope: 'all' | 'chapters' | 'summaries' | 'outline' | 'worldbook' | 'ledgers';
692
+ total: number;
693
+ truncated: boolean;
694
+ results: SearchResultItem[];
695
+ }
696
+ /** One structured health check. */
697
+ interface DoctorCheck {
698
+ id: string;
699
+ severity: 'ok' | 'info' | 'warning' | 'error';
700
+ title: string;
701
+ detail: string;
702
+ hint?: string;
703
+ }
704
+ /** `novel_doctor` payload; a missing project is reported, never thrown. */
705
+ interface DoctorReport {
706
+ ok: boolean;
707
+ root: string;
708
+ checks: DoctorCheck[];
709
+ summary: {
710
+ errors: number;
711
+ warnings: number;
712
+ infos: number;
713
+ };
714
+ }
715
+ /** One canonical outline-progress calculation shared by every adapter. */
716
+ interface OutlineProgress {
717
+ present: boolean;
718
+ target: number | null;
719
+ total: number;
720
+ planned: number;
721
+ archived: number;
722
+ missingCards: number;
723
+ plannedCoverage: number | null;
724
+ archivedCoverage: number | null;
725
+ /** Compatibility alias: planned coverage when a target exists, otherwise card coverage. */
726
+ coverage: number;
727
+ /** Fraction of archived chapters that have a matching archived card. */
728
+ cardCoverage: number;
729
+ volumes: number;
730
+ }
731
+ /** Book-wide quality, outline, cast and pacing aggregates. */
732
+ interface BookReport {
733
+ progress: {
734
+ archived: number;
735
+ target: number | null;
736
+ words: number;
737
+ average_words: number;
738
+ percent: number;
739
+ remaining_words: number | null;
740
+ remaining_words_min: number | null;
741
+ remaining_words_average: number | null;
742
+ remaining_words_basis: 'actual_average' | 'minimum_target' | 'none';
743
+ };
744
+ outline: {
745
+ present: boolean;
746
+ strict: boolean;
747
+ target: number | null;
748
+ total: number;
749
+ planned: number;
750
+ archived: number;
751
+ missing_cards: number;
752
+ planned_coverage: number | null;
753
+ archived_coverage: number | null;
754
+ coverage: number;
755
+ card_coverage: number;
756
+ volumes: Array<{
757
+ title: string;
758
+ start: number;
759
+ end: number;
760
+ planned: number;
761
+ archived: number;
762
+ }>;
763
+ drift: Array<{
764
+ chapter: number;
765
+ field: string;
766
+ planned: string;
767
+ actual: string;
768
+ }>;
769
+ };
770
+ worldbook: {
771
+ present: boolean;
772
+ characters: number;
773
+ locations: number;
774
+ factions: number;
775
+ rules: number;
776
+ glossary: number;
777
+ backstory: number;
778
+ facts?: number;
779
+ };
780
+ quality: {
781
+ average: number;
782
+ median: number;
783
+ min: number;
784
+ max: number;
785
+ last_five: number[];
786
+ trend: number;
787
+ dimensions: Array<{
788
+ id: string;
789
+ average: number;
790
+ samples: number;
791
+ }>;
792
+ };
793
+ /** Per-chapter effort survives archive (pipeline rounds are reset). */
794
+ effort: {
795
+ reviews: number;
796
+ revisions: number;
797
+ checks: number;
798
+ rollbacks: number;
799
+ chapters: Array<{
800
+ num: number;
801
+ reviews: number;
802
+ revisions: number;
803
+ checks: number;
804
+ rollbacks: number;
805
+ final_check: 'pass' | 'fail' | null;
806
+ archived_at: string | null;
807
+ }>;
808
+ };
809
+ pacing: {
810
+ scene_types: Record<string, number>;
811
+ hook_strength: Record<string, number>;
812
+ plot_advancement: Record<string, number>;
813
+ longest: {
814
+ chapter: number;
815
+ words: number;
816
+ } | null;
817
+ shortest: {
818
+ chapter: number;
819
+ words: number;
820
+ } | null;
821
+ };
822
+ cast: {
823
+ total: number;
824
+ top: Array<{
825
+ name: string;
826
+ appearances: number;
827
+ last_seen: number;
828
+ role: string;
829
+ }>;
830
+ };
831
+ foreshadows: {
832
+ open: number;
833
+ dormant: number;
834
+ overdue: number;
835
+ resolved: number;
836
+ oldest: Array<{
837
+ id: string;
838
+ title: string;
839
+ last_touched: number;
840
+ age: number;
841
+ }>;
842
+ };
843
+ timeline: {
844
+ events: number;
845
+ first_chapter: number | null;
846
+ last_chapter: number | null;
847
+ };
848
+ relationships: {
849
+ total: number;
850
+ types: Record<string, number>;
851
+ };
852
+ activity: Array<{
853
+ at: string;
854
+ op: string;
855
+ chapter: number;
856
+ status: string;
857
+ }>;
858
+ warnings: string[];
859
+ }
860
+ /** One onboarding step. */
861
+ interface GuideStep {
862
+ id: string;
863
+ title: string;
864
+ detail: string;
865
+ done: boolean;
866
+ tool?: string;
867
+ }
868
+ /** `novel_guide` payload. */
869
+ interface GuideReport {
870
+ state: 'no-project' | 'new' | 'planning' | 'writing' | 'blocked' | 'finished';
871
+ title: string;
872
+ steps: GuideStep[];
873
+ next: {
874
+ tool: string;
875
+ args: Record<string, unknown>;
876
+ reason: string;
877
+ };
878
+ commands: string[];
879
+ tips: string[];
880
+ }
529
881
  /** Draft submission payload. */
530
882
  interface DraftInput {
531
883
  chapter: number;
@@ -544,10 +896,23 @@ interface ReviewInput {
544
896
  total: number;
545
897
  proof: ReadingProof;
546
898
  }
899
+ /** One exact, unique replacement used by patch-mode revisions. */
900
+ interface RevisionEdit {
901
+ old_text: string;
902
+ new_text: string;
903
+ /** 1-based occurrence; defaults to requiring exactly one match. */
904
+ occurrence?: number;
905
+ }
906
+ /** Patch-mode revision; content and patch are mutually exclusive. */
907
+ interface RevisionPatch {
908
+ expected_hash: string;
909
+ edits: RevisionEdit[];
910
+ }
547
911
  /** Revision submission payload. */
548
912
  interface RevisionInput {
549
913
  chapter: number;
550
- content: string;
914
+ content?: string;
915
+ patch?: RevisionPatch;
551
916
  summary: ChapterSummary;
552
917
  }
553
918
  /** Semantic check items supplied by the model. */
@@ -598,6 +963,12 @@ interface GateInput {
598
963
  /** The three preceding chapters, newest first, for the duplicate rule. */
599
964
  previousTexts: string[];
600
965
  priorSceneTypes: SceneType[];
966
+ /** 0.2.0 planning input; undefined means "no outline" for older fixtures. */
967
+ outline?: OutlineDocument | null;
968
+ /** The chapter plan's target range, used by PG-13. */
969
+ planTargetWords?: [number, number] | null;
970
+ /** The chapter plan's declared cast names, used by PG-13. */
971
+ planCast?: string[] | null;
601
972
  }
602
973
  /** Gate evaluation outcome. */
603
974
  interface GateResult {
@@ -636,6 +1007,36 @@ interface StatusReport {
636
1007
  volume_name?: string;
637
1008
  act?: string;
638
1009
  act_progress?: string;
1010
+ /** 0.2.0 book-level visibility; absent on older synthetic payloads. */
1011
+ book?: {
1012
+ premise: string;
1013
+ logline: string;
1014
+ strict: boolean;
1015
+ outline: {
1016
+ present: boolean;
1017
+ target: number | null;
1018
+ total: number;
1019
+ planned: number;
1020
+ archived: number;
1021
+ missing_cards: number;
1022
+ planned_coverage: number | null;
1023
+ archived_coverage: number | null;
1024
+ coverage: number;
1025
+ card_coverage: number;
1026
+ volumes: number;
1027
+ current: OutlineChapter | null;
1028
+ };
1029
+ worldbook: {
1030
+ present: boolean;
1031
+ characters: number;
1032
+ locations: number;
1033
+ factions: number;
1034
+ rules: number;
1035
+ glossary: number;
1036
+ backstory: number;
1037
+ facts?: number;
1038
+ };
1039
+ };
639
1040
  next: {
640
1041
  tool: string;
641
1042
  args: Record<string, unknown>;
@@ -654,6 +1055,8 @@ interface AdviceReport {
654
1055
  }
655
1056
  /** One history row derived from the journal. */
656
1057
  interface AuditRow {
1058
+ /** Stable chronological cursor; absent only on hand-built test rows. */
1059
+ seq?: number;
657
1060
  at: string;
658
1061
  op: string;
659
1062
  chapter: number;
@@ -661,6 +1064,14 @@ interface AuditRow {
661
1064
  txid: string;
662
1065
  status: 'committed' | 'aborted';
663
1066
  files: string[];
1067
+ /** Machine code and human reason for a refused attempt. */
1068
+ code?: string;
1069
+ reason?: string;
1070
+ hint?: string;
1071
+ stage?: string;
1072
+ /** True when no transaction started: the gate/input refused the call. */
1073
+ attempt?: boolean;
1074
+ blocking?: BlockingItem[];
664
1075
  }
665
1076
  /** One diff hunk between two revisions of a chapter. */
666
1077
  interface DiffReport {
@@ -674,8 +1085,8 @@ interface DiffReport {
674
1085
  line: string;
675
1086
  }>;
676
1087
  }
677
- /** Export capabilities. */
678
- type ExportKind = 'md' | 'txt' | 'epub';
1088
+ /** Export capabilities; the last three are book-planning deliverables. */
1089
+ type ExportKind = 'md' | 'txt' | 'epub' | 'outline' | 'worldbook' | 'proposal';
679
1090
  /** One blacklisted word. */
680
1091
  interface TermEntry {
681
1092
  term: string;
@@ -803,13 +1214,66 @@ interface Rulepack {
803
1214
  exemptions: string[];
804
1215
  }
805
1216
  /** Every persisted document the engine knows how to load. */
806
- type DocKind = 'project' | 'pipeline' | 'chapters' | 'characters' | 'world' | 'locks' | 'foreshadows' | 'timeline' | 'relationships' | 'handoff' | 'review' | 'check' | 'worldPlan' | 'chapterPlan';
1217
+ type DocKind = 'project' | 'pipeline' | 'chapters' | 'characters' | 'world' | 'locks' | 'foreshadows' | 'timeline' | 'relationships' | 'handoff' | 'review' | 'check' | 'worldPlan' | 'chapterPlan' | 'outline' | 'worldbook';
807
1218
  /** One project row in the cross-project registry. */
808
1219
  interface RegistryEntry {
809
1220
  root: string;
810
1221
  title: string;
811
1222
  updated_at: string;
812
1223
  }
1224
+ interface BookPatch {
1225
+ premise?: string;
1226
+ logline?: string;
1227
+ themes?: string[];
1228
+ tone?: string;
1229
+ pov?: string;
1230
+ audience?: string;
1231
+ selling_points?: string[];
1232
+ ending?: string;
1233
+ strict?: boolean;
1234
+ /** Internal provenance for an explicit strict choice. */
1235
+ strict_explicit?: boolean;
1236
+ }
1237
+ interface VolumeInput {
1238
+ id?: string;
1239
+ title: string;
1240
+ start_chapter: number;
1241
+ end_chapter: number;
1242
+ goal?: string;
1243
+ arc?: string;
1244
+ climax?: string;
1245
+ status?: OutlineVolume['status'];
1246
+ }
1247
+ interface OutlineChapterInput {
1248
+ num: number;
1249
+ volume?: number | null;
1250
+ title: string;
1251
+ scene_type: SceneType;
1252
+ goal: string;
1253
+ conflict: string;
1254
+ hook: string;
1255
+ cast?: string[];
1256
+ target_words: [number, number];
1257
+ notes?: string;
1258
+ }
1259
+ interface OutlineInput {
1260
+ book?: BookPatch;
1261
+ volumes?: VolumeInput[];
1262
+ chapters: OutlineChapterInput[];
1263
+ strict?: boolean;
1264
+ }
1265
+ interface WorldbookPatch {
1266
+ premise?: string;
1267
+ setting?: string;
1268
+ rules?: WorldbookRule[];
1269
+ factions?: WorldbookFaction[];
1270
+ locations?: WorldbookLocation[];
1271
+ characters?: WorldbookCharacter[];
1272
+ glossary?: WorldbookGlossary[];
1273
+ backstory?: WorldbookBackstory[];
1274
+ facts?: WorldbookFact[];
1275
+ themes?: string[];
1276
+ }
813
1277
  /** One rendered task book. */
814
1278
  interface BriefDocument {
815
1279
  stage: BriefStage;
@@ -846,6 +1310,7 @@ declare const GATES: {
846
1310
  readonly 'PG-10': "CONSISTENCY";
847
1311
  readonly 'PG-11': "ROUNDS";
848
1312
  readonly 'PG-12': "ARCHIVE_ATOMIC";
1313
+ readonly 'PG-13': "OUTLINE_ALIGNMENT";
849
1314
  };
850
1315
  type GateId = keyof typeof GATES;
851
1316
  /** Everything gate evaluation needs beyond the input: taste comes from the pack. */
@@ -857,7 +1322,7 @@ interface GateOptions {
857
1322
  declare function parseParagraphLocation(location: string): number | null;
858
1323
  /** Verify one reading-proof reference against the manuscript. */
859
1324
  declare function verifyReference(text: string, reference: ReadingProofReference): string | null;
860
- /** Evaluate all twelve gates and return every blocking item, in canonical order. */
1325
+ /** Evaluate all thirteen gates and return every blocking item, in canonical order. */
861
1326
  declare function evaluateGates(input: GateInput, options: GateOptions): GateResult;
862
1327
  /**
863
1328
  * Which gates apply to an operation. A gate that does not apply must not run:
@@ -912,6 +1377,59 @@ interface PanelSnapshot {
912
1377
  done: boolean;
913
1378
  active: boolean;
914
1379
  }>;
1380
+ /** 0.2.0 book-level planning and health blocks. */
1381
+ book?: PanelBook;
1382
+ quality?: {
1383
+ averageScore: number;
1384
+ scores: number[];
1385
+ trend: number;
1386
+ };
1387
+ health?: {
1388
+ ok: boolean;
1389
+ blocking: number;
1390
+ stateErrors: number;
1391
+ };
1392
+ activity?: Array<{
1393
+ at: string;
1394
+ op: string;
1395
+ chapter: number;
1396
+ status: string;
1397
+ }>;
1398
+ }
1399
+ interface PanelBook {
1400
+ premise: string;
1401
+ logline: string;
1402
+ strict: boolean;
1403
+ target: number | null;
1404
+ outline: {
1405
+ present: boolean;
1406
+ target: number | null;
1407
+ total: number;
1408
+ planned: number;
1409
+ archived: number;
1410
+ missing_cards: number;
1411
+ planned_coverage: number | null;
1412
+ archived_coverage: number | null;
1413
+ coverage: number;
1414
+ card_coverage: number;
1415
+ volumes: Array<{
1416
+ title: string;
1417
+ start: number;
1418
+ end: number;
1419
+ planned: number;
1420
+ archived: number;
1421
+ }>;
1422
+ };
1423
+ worldbook: {
1424
+ present: boolean;
1425
+ characters: number;
1426
+ locations: number;
1427
+ factions: number;
1428
+ rules: number;
1429
+ glossary: number;
1430
+ backstory: number;
1431
+ facts?: number;
1432
+ };
915
1433
  }
916
1434
  interface PanelSummaryDto {
917
1435
  schemaVersion: number;
@@ -942,6 +1460,24 @@ interface PanelSummaryDto {
942
1460
  };
943
1461
  /** Absent for older payloads; false means the current chapter cycle is done. */
944
1462
  finished?: boolean;
1463
+ /** 0.2.0 planning, quality, health and activity blocks. */
1464
+ book?: PanelBook;
1465
+ quality?: {
1466
+ averageScore: number;
1467
+ scores: number[];
1468
+ trend: number;
1469
+ };
1470
+ health?: {
1471
+ ok: boolean;
1472
+ blocking: number;
1473
+ stateErrors: number;
1474
+ };
1475
+ activity?: Array<{
1476
+ at: string;
1477
+ op: string;
1478
+ chapter: number;
1479
+ status: string;
1480
+ }>;
945
1481
  }
946
1482
  interface PanelChapterRow {
947
1483
  num: number;
@@ -1021,6 +1557,40 @@ type EngineEvent = {
1021
1557
  path: string;
1022
1558
  reason: string;
1023
1559
  };
1560
+ } | {
1561
+ name: 'novel/planned';
1562
+ payload: {
1563
+ schemaVersion: 1;
1564
+ projectRoot: string;
1565
+ kind: 'blueprint' | 'outline' | 'worldbook';
1566
+ chapters: number;
1567
+ volumes: number;
1568
+ };
1569
+ } | {
1570
+ name: 'novel/reopened';
1571
+ payload: {
1572
+ schemaVersion: 1;
1573
+ projectRoot: string;
1574
+ chapter: number;
1575
+ restore_txid: string;
1576
+ };
1577
+ } | {
1578
+ name: 'novel/restored';
1579
+ payload: {
1580
+ schemaVersion: 1;
1581
+ projectRoot: string;
1582
+ txid: string;
1583
+ chapter: number;
1584
+ paths: string[];
1585
+ };
1586
+ } | {
1587
+ name: 'novel/exported';
1588
+ payload: {
1589
+ schemaVersion: 1;
1590
+ projectRoot: string;
1591
+ kind: string;
1592
+ files: number;
1593
+ };
1024
1594
  };
1025
1595
  interface InitProjectInput {
1026
1596
  title: string;
@@ -1031,6 +1601,17 @@ interface InitProjectInput {
1031
1601
  words_per_chapter?: [number, number];
1032
1602
  total_chapters?: number | null;
1033
1603
  dialogue_ratio?: [number, number];
1604
+ /** 0.2.0 book-planning seed; every field is optional. */
1605
+ premise?: string;
1606
+ logline?: string;
1607
+ themes?: string[];
1608
+ tone?: string;
1609
+ pov?: string;
1610
+ audience?: string;
1611
+ selling_points?: string[];
1612
+ ending?: string;
1613
+ /** Strict outline is opt-in; a new project defaults to false. */
1614
+ strict_outline?: boolean;
1034
1615
  root?: string;
1035
1616
  }
1036
1617
  interface StatusInput {
@@ -1070,9 +1651,11 @@ interface RevisionSubmission extends Omit<RevisionInput, 'summary'> {
1070
1651
  }
1071
1652
  interface CheckSubmission {
1072
1653
  chapter: number;
1073
- /** The four P6 semantic items; omission is a failed check, never a pass. */
1654
+ /** The five P6 semantic items; omission is a failed check, never a pass. */
1074
1655
  semantic: SemanticItem[];
1075
1656
  scope?: string[];
1657
+ /** Advisory codes the author has read and accepts; blockers cannot be acknowledged. */
1658
+ acknowledge?: string[];
1076
1659
  root?: string;
1077
1660
  }
1078
1661
  interface ArchiveSubmission {
@@ -1089,6 +1672,13 @@ interface AmendSubmission {
1089
1672
  reason: string;
1090
1673
  root?: string;
1091
1674
  }
1675
+ interface RestoreSubmission {
1676
+ txid: string;
1677
+ chapter?: number;
1678
+ /** Restrict to these project-relative files; omitted restores every chapter file in the snapshot. */
1679
+ paths?: string[];
1680
+ root?: string;
1681
+ }
1092
1682
  interface RollbackSubmission {
1093
1683
  chapter: number;
1094
1684
  toStage: 'P1' | 'P2' | 'P3' | 'P4' | 'P5' | 'P6';
@@ -1096,13 +1686,19 @@ interface RollbackSubmission {
1096
1686
  root?: string;
1097
1687
  }
1098
1688
  interface DiffInput {
1099
- chapter: number;
1689
+ chapter?: number;
1100
1690
  before?: string;
1101
1691
  after?: string;
1102
1692
  root?: string;
1103
1693
  }
1104
1694
  interface AuditInput {
1105
1695
  limit?: number;
1696
+ cursor?: number;
1697
+ chapter?: number;
1698
+ op?: string;
1699
+ status?: 'committed' | 'aborted';
1700
+ since?: string;
1701
+ until?: string;
1106
1702
  root?: string;
1107
1703
  }
1108
1704
  interface ExportInput {
@@ -1111,8 +1707,10 @@ interface ExportInput {
1111
1707
  root?: string;
1112
1708
  }
1113
1709
  interface StateQueryInput {
1114
- kind: DocKind | 'novel' | 'planWorld' | 'planChapter';
1710
+ kind: DocKind | 'novel' | 'planWorld' | 'planChapter' | 'history' | 'backups' | 'briefs';
1115
1711
  chapter?: number;
1712
+ /** Select one historical revision instead of listing history. */
1713
+ revision?: number;
1116
1714
  /** Chapter index defaults to a compact read; opt in to the full summaries. */
1117
1715
  include_summary?: boolean;
1118
1716
  /** Inclusive chapter range for the `chapters` index. */
@@ -1124,6 +1722,43 @@ interface WordCountInput {
1124
1722
  chapter?: number;
1125
1723
  root?: string;
1126
1724
  }
1725
+ interface BookPlanSubmission extends BookPatch {
1726
+ volumes?: VolumeInput[];
1727
+ root?: string;
1728
+ }
1729
+ interface OutlineSubmission extends OutlineInput {
1730
+ root?: string;
1731
+ }
1732
+ interface OutlineImportSubmission {
1733
+ format: 'markdown' | 'yaml' | 'json';
1734
+ content: string;
1735
+ dry_run?: boolean;
1736
+ root?: string;
1737
+ }
1738
+ interface WorldbookSubmission extends WorldbookPatch {
1739
+ root?: string;
1740
+ }
1741
+ interface ReopenSubmission {
1742
+ chapter: number;
1743
+ /** A reopen is a history-changing maintenance action; the reason is mandatory. */
1744
+ reason: string;
1745
+ root?: string;
1746
+ }
1747
+ interface SearchInput {
1748
+ query: string;
1749
+ scope?: 'all' | 'chapters' | 'summaries' | 'outline' | 'worldbook' | 'ledgers';
1750
+ limit?: number;
1751
+ root?: string;
1752
+ }
1753
+ interface DoctorInput {
1754
+ root?: string;
1755
+ }
1756
+ interface ReportSubmission {
1757
+ root?: string;
1758
+ }
1759
+ interface GuideInput {
1760
+ root?: string;
1761
+ }
1127
1762
  /**
1128
1763
  * The single write authority. Tools, commands, the panel and task books never
1129
1764
  * touch a file; every mutation is one of these operations.
@@ -1174,12 +1809,61 @@ declare class EngineService {
1174
1809
  chapter: number;
1175
1810
  stage: Stage;
1176
1811
  }>>;
1812
+ submitBookPlan(input: BookPlanSubmission): Promise<OpResult<{
1813
+ premise: string;
1814
+ logline: string;
1815
+ strict: boolean;
1816
+ volumes: number;
1817
+ chapters: number;
1818
+ }>>;
1819
+ submitOutline(input: OutlineSubmission): Promise<OpResult<{
1820
+ planned: number;
1821
+ archived: number;
1822
+ total: number;
1823
+ strict: boolean;
1824
+ volumes: number;
1825
+ }>>;
1826
+ importOutline(input: OutlineImportSubmission): Promise<OpResult<{
1827
+ format: string;
1828
+ planned: number;
1829
+ archived: number;
1830
+ total: number;
1831
+ warnings: string[];
1832
+ preview: string;
1833
+ applied: boolean;
1834
+ }>>;
1835
+ submitWorldbook(input: WorldbookSubmission): Promise<OpResult<{
1836
+ sections: string[];
1837
+ entries: number;
1838
+ counts: Record<string, number>;
1839
+ }>>;
1840
+ reopenChapter(input: ReopenSubmission): Promise<OpResult<{
1841
+ chapter: number;
1842
+ stage: Stage;
1843
+ restore_txid: string;
1844
+ }>>;
1845
+ /**
1846
+ * Restore one active chapter from a backup transaction. Archived chapters
1847
+ * are refused: their manuscript hash is bound by the index, so restoring
1848
+ * behind the index would corrupt the project. Reopen first, then restore.
1849
+ */
1850
+ restoreBackup(input: RestoreSubmission): Promise<OpResult<{
1851
+ txid: string;
1852
+ chapter: number;
1853
+ restored: string[];
1854
+ }>>;
1177
1855
  /**
1178
1856
  * Read-only paths must surface a committed-journal mismatch. The write path
1179
1857
  * already fails closed; without this, status recommends the next write and
1180
1858
  * the model only learns about the mismatch when that write aborts.
1181
1859
  */
1182
1860
  private journalBlocking;
1861
+ /**
1862
+ * Count every committed attempt for one chapter, not just the loop since the
1863
+ * last rollback. `pipeline.rounds` is deliberately reset by rollback to keep
1864
+ * the retry budget fair; it is not the chapter's lifetime effort.
1865
+ */
1866
+ private lifetimeRounds;
1183
1867
  getStatus(input?: StatusInput): Promise<OpResult<StatusReport>>;
1184
1868
  countWords(input?: WordCountInput): Promise<OpResult<WordMetrics>>;
1185
1869
  queryState(input: StateQueryInput): Promise<OpResult<unknown>>;
@@ -1194,6 +1878,11 @@ declare class EngineService {
1194
1878
  blocking: BlockingItem[];
1195
1879
  state_errors: StateError[];
1196
1880
  }>>;
1881
+ search(input: SearchInput): Promise<OpResult<SearchReport>>;
1882
+ doctor(input?: DoctorInput): Promise<OpResult<DoctorReport>>;
1883
+ private recoveryForSafe;
1884
+ report(input?: ReportSubmission): Promise<OpResult<BookReport>>;
1885
+ guide(input?: GuideInput): Promise<OpResult<GuideReport>>;
1197
1886
  getBrief(input: BriefInput): Promise<OpResult<{
1198
1887
  stage: string;
1199
1888
  chapter: number;
@@ -1236,6 +1925,9 @@ declare class EngineService {
1236
1925
  chapter: number;
1237
1926
  verdict: 'pass' | 'fail';
1238
1927
  violations: number;
1928
+ advisories: number;
1929
+ advisory_codes: string[];
1930
+ acknowledged: string[];
1239
1931
  metrics: WordMetrics;
1240
1932
  stage: Stage;
1241
1933
  }>>;
@@ -1286,6 +1978,15 @@ declare class EngineService {
1286
1978
  averageScore: number;
1287
1979
  lastScore: number;
1288
1980
  openForeshadows: number;
1981
+ plannedChapters: number;
1982
+ totalCards: number;
1983
+ outlineCoverage: number | null;
1984
+ archivedCoverage: number | null;
1985
+ missingCards: number;
1986
+ blueprintReady: boolean;
1987
+ worldbookEntries: number;
1988
+ strictOutline: boolean;
1989
+ targetChapters: number | null;
1289
1990
  }>;
1290
1991
  private registry;
1291
1992
  /** List known projects (convenience only; a project opens without a row). */
@@ -1327,6 +2028,47 @@ declare class EngineService {
1327
2028
  preview: string;
1328
2029
  applied: boolean;
1329
2030
  }>>;
2031
+ /**
2032
+ * Planning writes use project targets as invariants. The fallback project is
2033
+ * only for reads; writing against its null target (or default word range)
2034
+ * would create a document the real novel.yaml rejects after repair.
2035
+ */
2036
+ private assertProjectReadable;
2037
+ /**
2038
+ * Planning documents are read-only inputs to the write tools. A corrupt file
2039
+ * must be repaired explicitly with novel_amend; allowing a normal write to
2040
+ * proceed would silently replace every planned card or worldbook section it
2041
+ * could not read.
2042
+ */
2043
+ private assertPlanningDocumentReadable;
2044
+ /**
2045
+ * A strict outline cannot silently lose the active chapter's card while the
2046
+ * chapter is still being written. Otherwise a late replacement turns a
2047
+ * strict project non-strict in effect and archive would auto-create the card
2048
+ * the author was required to provide.
2049
+ */
2050
+ private assertActiveCardPreserved;
2051
+ /**
2052
+ * The outline is a derived progress view and the PG-13 input. Every write
2053
+ * path must stage a document the loader will accept; otherwise a successful
2054
+ * archive/planning call can leave the project with an unreadable outline and
2055
+ * an unreachable repair path. Auto-created cards are normalized by book.ts;
2056
+ * this is the final fail-closed boundary for all callers.
2057
+ */
2058
+ private assertOutlineWritable;
2059
+ /**
2060
+ * total_chapters is a hard ceiling everywhere: outline cards already enforce
2061
+ * it, and writing/archiving a chapter past it would make the auto-created
2062
+ * archived card invalid. Fail at the first stage instead of after the draft.
2063
+ */
2064
+ private assertChapterWithinTarget;
2065
+ /**
2066
+ * Without the chapter index, normalizeOutlineChapters cannot re-add archived
2067
+ * cards and cannot prove that a replacement did not delete them. Planning
2068
+ * writes must wait for a chapters repair; only novel_amend may repair it.
2069
+ */
2070
+ private assertArchiveIndexReadable;
2071
+ private bookStatus;
1330
2072
  private panelSnapshot;
1331
2073
  /**
1332
2074
  * The archive stage has only one legal operation, so a status/panel that
@@ -1340,6 +2082,8 @@ declare class EngineService {
1340
2082
  private roundsLimitBlock;
1341
2083
  /** Pipeline blockers plus budget exhaustion, so status/panel cannot say "all clear". */
1342
2084
  private effectiveBlocking;
2085
+ /** The PG-13 block that status must show before P2/P3 even tries to write. */
2086
+ private outlineGateBlock;
1343
2087
  /**
1344
2088
  * A review that exists but cannot be tied to the pipeline digest would let a
1345
2089
  * hand-edited review pass archive. Archive already fails closed; status and
@@ -1355,12 +2099,25 @@ declare class EngineService {
1355
2099
  /** State damage that makes this operation unsafe to start. */
1356
2100
  private stateGateBlock;
1357
2101
  /** Whether the stored brief still signs the current snapshot. */
2102
+ private readBriefReceipts;
1358
2103
  private briefIsCurrent;
1359
2104
  /** Arguments valid for the tool `STAGE_NEXT` recommends at this stage. */
1360
2105
  private nextArgs;
1361
2106
  private metricsFor;
1362
2107
  private readChapter;
2108
+ /**
2109
+ * archive-digests.json is a derived mirror, not a source of truth. A damaged
2110
+ * mirror must never make every write unreachable; rebuild the missing rows
2111
+ * from the chapter index and let the next archive rewrite a valid file.
2112
+ */
2113
+ private readArchiveDigests;
1363
2114
  private latestBackup;
2115
+ /**
2116
+ * A manuscript export with an unreadable chapter index cannot prove which
2117
+ * chapters exist. Refusing is preferable to writing a short book that looks
2118
+ * complete; the repair path is novel_amend, not a silent partial export.
2119
+ */
2120
+ private assertExportIndexReadable;
1364
2121
  private chaptersForExport;
1365
2122
  private machine;
1366
2123
  private briefContextFor;
@@ -1375,6 +2132,10 @@ declare class EngineService {
1375
2132
  private assertGates;
1376
2133
  private assertChapterParameter;
1377
2134
  private assertChapter;
2135
+ /** True when anything survives in the project directory besides novel.yaml. */
2136
+ private projectStateExists;
2137
+ /** A live writer lock makes a read-only snapshot potentially torn. */
2138
+ private lockIsLive;
1378
2139
  private load;
1379
2140
  /** A repair may bypass the journal hash only when the document is unreadable. */
1380
2141
  private stateDocIsUnreadable;
@@ -1402,6 +2163,8 @@ declare function renderPlainText(project: NovelProject, chapters: readonly Expor
1402
2163
  declare function renderBlurb(project: NovelProject): string;
1403
2164
  /** Cover as a standalone SVG string; text is escaped, palette is theme-neutral. */
1404
2165
  declare function renderCover(project: NovelProject): string;
2166
+ /** A book-level proposal for editors, platforms and the author's own pitch. */
2167
+ declare function renderProposal(project: NovelProject, outline: OutlineDocument, worldbook: WorldbookDocument, chapters: readonly ChapterRecord[]): string;
1405
2168
  /** XHTML chapter document for the EPUB package. */
1406
2169
  declare function renderEpubChapter(chapter: ExportChapter): string;
1407
2170
  declare const name = "dsh-novel-write-engine";
@@ -1426,4 +2189,4 @@ declare const Config: Schema<Config>;
1426
2189
  * created lazily per project root.
1427
2190
  */
1428
2191
  declare function apply(ctx: Context, config: Config): void;
1429
- export { AdviceReport, ArchiveHandoff, AuditRow, BlockingItem, BriefDocument, BriefLibrary, BriefStage, ChapterCastMember, ChapterPlanInput, ChapterRecord, ChapterStatusRow, ChapterSummary, ChaptersDocument, CharacterDelta, CharactersDocument, CheckDocument, Config, Deduction, DeltaAction, DiffReport, DraftInput, ERROR_CODES, EngineBlockedError, EngineError, EngineFaultError, EngineInputError, EngineService, ErrorCode, ExportChapter, ExportKind, ForeshadowDelta, ForeshadowsDocument, GENRES, GateInput, GateOptions, GateResult, Genre, HandoffDocument, LocksDocument, MachineMetrics, NovelProject, OPERATIONS, OpResult, Operation, PLATFORMS, PanelChapterRow, PanelChaptersDto, PanelDraft, PanelGatesDto, PanelSnapshot, PanelSummaryDto, PipelineState, Platform, ReadingProof, ReadingProofReference, RelationshipDelta, RelationshipsDocument, ReviewDocument, ReviewInput, ReviewIssue, RevisionInput, RoundCounters, SCENE_TYPES, SCHEMA_VERSION, SceneType, ScoreDimension, SemanticItem, Severity, Stage, StateDelta, StateError, StateErrorCode, StatusReport, TimelineDelta, TimelineDocument, Violation, Warning, WordMetrics, WorldDelta, WorldDocument, WorldPlanInput, applicableGates, apply, blockedError, briefStageFor, chapterHeading, evaluateGates, faultError, gateName, inject, inputError, loadRulepack, name, parseParagraphLocation, phaseStrip, projectChapter, projectChapters, projectGates, projectSummary, renderBlurb, renderCover, renderEpubChapter, renderMarkdown, renderPlainText, verifyReference };
2192
+ export { AdviceReport, ArchiveHandoff, AuditRow, BlockingItem, BookPlan, BookReport, BriefDocument, BriefLibrary, BriefReceipt, BriefReceiptsDocument, BriefStage, ChapterCastMember, ChapterPlanInput, ChapterRecord, ChapterStatusRow, ChapterSummary, ChaptersDocument, CharacterDelta, CharactersDocument, CheckDocument, Config, Deduction, DeltaAction, DiffReport, DoctorCheck, DoctorReport, DraftInput, DraftState, ERROR_CODES, EngineBlockedError, EngineError, EngineFaultError, EngineInputError, EngineService, ErrorCode, ExportChapter, ExportKind, ForeshadowDelta, ForeshadowsDocument, GENRES, GateInput, GateOptions, GateResult, Genre, GuideReport, GuideStep, HandoffDocument, LocksDocument, MachineMetrics, NovelProject, OPERATIONS, OpResult, Operation, OutlineChapter, OutlineDocument, OutlineProgress, OutlineVolume, PLATFORMS, PanelBook, PanelChapterRow, PanelChaptersDto, PanelDraft, PanelGatesDto, PanelSnapshot, PanelSummaryDto, PipelineState, Platform, ReadingProof, ReadingProofReference, RelationshipDelta, RelationshipsDocument, ReviewDocument, ReviewInput, ReviewIssue, RevisionEdit, RevisionInput, RevisionPatch, RoundCounters, SCENE_TYPES, SCHEMA_VERSION, SceneType, ScoreDimension, SearchReport, SearchResultItem, SemanticItem, Severity, Stage, StateDelta, StateError, StateErrorCode, StatusReport, TimelineDelta, TimelineDocument, Violation, Warning, WordMetrics, WorldDelta, WorldDocument, WorldPlanInput, WorldbookBackstory, WorldbookCharacter, WorldbookDocument, WorldbookFact, WorldbookFaction, WorldbookGlossary, WorldbookLocation, WorldbookRule, applicableGates, apply, blockedError, briefStageFor, chapterHeading, evaluateGates, faultError, gateName, inject, inputError, loadRulepack, name, parseParagraphLocation, phaseStrip, projectChapter, projectChapters, projectGates, projectSummary, renderBlurb, renderCover, renderEpubChapter, renderMarkdown, renderPlainText, renderProposal, verifyReference };