@gordon.gan/specflow 1.8.0-alpha → 1.8.1-beta

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.
Files changed (107) hide show
  1. package/README.md +5 -3
  2. package/dist/cli/commands/document-run.d.ts +14 -1
  3. package/dist/cli/commands/document-run.js +518 -212
  4. package/dist/core/document/chapters.js +18 -3
  5. package/dist/core/document/digests.d.ts +0 -1
  6. package/dist/core/document/digests.js +18 -10
  7. package/dist/core/document/engine.d.ts +9 -0
  8. package/dist/core/document/engine.js +273 -38
  9. package/dist/core/document/extract.d.ts +61 -0
  10. package/dist/core/document/extract.js +437 -0
  11. package/dist/core/document/forbidden-patterns.js +7 -4
  12. package/dist/core/document/gates.d.ts +13 -1
  13. package/dist/core/document/gates.js +48 -6
  14. package/dist/core/document/input-digest.d.ts +8 -0
  15. package/dist/core/document/input-digest.js +93 -14
  16. package/dist/core/document/input-features.d.ts +23 -4
  17. package/dist/core/document/input-features.js +51 -2
  18. package/dist/core/document/lint.d.ts +17 -0
  19. package/dist/core/document/lint.js +118 -0
  20. package/dist/core/document/llm.d.ts +3 -10
  21. package/dist/core/document/llm.js +3 -8
  22. package/dist/core/document/map.d.ts +6 -0
  23. package/dist/core/document/map.js +114 -30
  24. package/dist/core/document/outline.d.ts +3 -0
  25. package/dist/core/document/outline.js +52 -9
  26. package/dist/core/document/paths.d.ts +5 -5
  27. package/dist/core/document/paths.js +11 -6
  28. package/dist/core/document/profile-validator.js +33 -5
  29. package/dist/core/document/profiles.js +5 -0
  30. package/dist/core/document/render.d.ts +26 -0
  31. package/dist/core/document/render.js +110 -14
  32. package/dist/core/document/review.d.ts +19 -4
  33. package/dist/core/document/review.js +65 -20
  34. package/dist/core/document/scene-detect.d.ts +10 -3
  35. package/dist/core/document/scene-detect.js +138 -22
  36. package/dist/core/document/schemas.d.ts +188 -34
  37. package/dist/core/document/schemas.js +39 -26
  38. package/dist/integrations/shared/capability-evidence.js +4 -4
  39. package/dist/integrations/shared/command-catalog.js +2 -1
  40. package/dist/integrations/shared/parity-manifest.js +4 -4
  41. package/package.json +2 -1
  42. package/prompts/document/map/acceptance.md +1 -0
  43. package/prompts/document/map/anti-ai.md +29 -0
  44. package/prompts/document/map/api-design.md +13 -4
  45. package/prompts/document/map/architecture.md +21 -1
  46. package/prompts/document/map/benchmark.md +26 -0
  47. package/prompts/document/map/closed-loop.md +1 -0
  48. package/prompts/document/map/compat-migration.md +24 -1
  49. package/prompts/document/map/component-design.md +30 -0
  50. package/prompts/document/map/config-runtime.md +1 -0
  51. package/prompts/document/map/core-flow.md +62 -0
  52. package/prompts/document/map/core-logic.md +1 -0
  53. package/prompts/document/map/data-model.md +1 -0
  54. package/prompts/document/map/deploy.md +20 -2
  55. package/prompts/document/map/fix.md +1 -0
  56. package/prompts/document/map/frontend-architecture.md +35 -0
  57. package/prompts/document/map/goal.md +1 -0
  58. package/prompts/document/map/impact.md +1 -0
  59. package/prompts/document/map/implementability.md +1 -0
  60. package/prompts/document/map/migration-guide.md +36 -0
  61. package/prompts/document/map/mvp-boundary.md +1 -0
  62. package/prompts/document/map/non-goals.md +1 -0
  63. package/prompts/document/map/ops.md +33 -0
  64. package/prompts/document/map/performance.md +32 -0
  65. package/prompts/document/map/poc-demo.md +25 -0
  66. package/prompts/document/map/regression.md +1 -0
  67. package/prompts/document/map/reproduce.md +1 -0
  68. package/prompts/document/map/requirement.md +1 -0
  69. package/prompts/document/map/research.md +25 -0
  70. package/prompts/document/map/root-cause.md +1 -0
  71. package/prompts/document/map/signoff.md +1 -0
  72. package/prompts/document/map/state-management.md +23 -0
  73. package/prompts/document/map/tech-selection.md +13 -1
  74. package/prompts/document/map/test-strategy.md +18 -1
  75. package/prompts/document/map/ui-design.md +10 -7
  76. package/prompts/document/outline/general.md +9 -0
  77. package/prompts/document/review/ai-review.md +2 -1
  78. package/prompts/document/shared/grounding.md +84 -0
  79. package/skills/specflow-techdoc/SKILL.md +143 -0
  80. package/skills/specflow-techdoc-synth/SKILL.md +99 -0
  81. package/templates/document/chapters/api-design.yaml +6 -1
  82. package/templates/document/chapters/architecture.yaml +7 -4
  83. package/templates/document/chapters/benchmark.yaml +20 -0
  84. package/templates/document/chapters/compat-migration.yaml +7 -3
  85. package/templates/document/chapters/component-design.yaml +22 -0
  86. package/templates/document/chapters/core-flow.yaml +27 -0
  87. package/templates/document/chapters/core-logic.yaml +1 -1
  88. package/templates/document/chapters/deploy.yaml +11 -7
  89. package/templates/document/chapters/frontend-architecture.yaml +22 -0
  90. package/templates/document/chapters/migration-guide.yaml +21 -0
  91. package/templates/document/chapters/ops.yaml +25 -0
  92. package/templates/document/chapters/performance.yaml +21 -0
  93. package/templates/document/chapters/poc-demo.yaml +22 -0
  94. package/templates/document/chapters/research.yaml +22 -0
  95. package/templates/document/chapters/state-management.yaml +22 -0
  96. package/templates/document/chapters/tech-selection.yaml +6 -3
  97. package/templates/document/chapters/test-strategy.yaml +5 -2
  98. package/templates/document/chapters/ui-design.yaml +7 -1
  99. package/templates/document/profiles/0to1.yaml +41 -9
  100. package/templates/document/profiles/bugfix.yaml +8 -3
  101. package/templates/document/profiles/feature.yaml +19 -7
  102. package/templates/document/profiles/frontend-0to1.yaml +47 -0
  103. package/templates/document/profiles/migration.yaml +42 -0
  104. package/templates/document/profiles/poc.yaml +46 -0
  105. package/dist/core/document/index.d.ts +0 -7
  106. package/dist/core/document/index.js +0 -7
  107. package/skills/specflow-document/SKILL.md +0 -124
@@ -43,7 +43,6 @@ export declare const ErrorSpecSchema: z.ZodObject<{
43
43
  condition: string;
44
44
  note?: string | undefined;
45
45
  }>;
46
- export type ErrorSpec = z.infer<typeof ErrorSpecSchema>;
47
46
  export declare const InterfaceEntitySchema: z.ZodObject<{
48
47
  id: z.ZodString;
49
48
  name: z.ZodString;
@@ -218,7 +217,7 @@ export declare const DecisionEntitySchema: z.ZodObject<{
218
217
  }>;
219
218
  export type DecisionEntity = z.infer<typeof DecisionEntitySchema>;
220
219
  export declare const EntitiesSchema: z.ZodObject<{
221
- interfaces: z.ZodDefault<z.ZodArray<z.ZodObject<{
220
+ interfaces: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodObject<{
222
221
  id: z.ZodString;
223
222
  name: z.ZodString;
224
223
  method: z.ZodEnum<["GET", "POST", "PUT", "PATCH", "DELETE"]>;
@@ -325,8 +324,58 @@ export declare const EntitiesSchema: z.ZodObject<{
325
324
  required?: boolean | undefined;
326
325
  desc?: string | undefined;
327
326
  }[] | undefined;
328
- }>, "many">>;
329
- tables: z.ZodDefault<z.ZodArray<z.ZodObject<{
327
+ }>, "many">>, {
328
+ path: string;
329
+ id: string;
330
+ name: string;
331
+ errors: {
332
+ status: string;
333
+ condition: string;
334
+ note?: string | undefined;
335
+ }[];
336
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
337
+ failure_examples: string[];
338
+ request_fields?: {
339
+ type: string;
340
+ name: string;
341
+ required: boolean;
342
+ default?: string | undefined;
343
+ desc?: string | undefined;
344
+ }[] | undefined;
345
+ response_fields?: {
346
+ type: string;
347
+ name: string;
348
+ required: boolean;
349
+ default?: string | undefined;
350
+ desc?: string | undefined;
351
+ }[] | undefined;
352
+ }[], {
353
+ path: string;
354
+ id: string;
355
+ name: string;
356
+ errors: {
357
+ status: string;
358
+ condition: string;
359
+ note?: string | undefined;
360
+ }[];
361
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
362
+ failure_examples: string[];
363
+ request_fields?: {
364
+ type: string;
365
+ name: string;
366
+ default?: string | undefined;
367
+ required?: boolean | undefined;
368
+ desc?: string | undefined;
369
+ }[] | undefined;
370
+ response_fields?: {
371
+ type: string;
372
+ name: string;
373
+ default?: string | undefined;
374
+ required?: boolean | undefined;
375
+ desc?: string | undefined;
376
+ }[] | undefined;
377
+ }[] | undefined>;
378
+ tables: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodObject<{
330
379
  id: z.ZodString;
331
380
  name: z.ZodString;
332
381
  ddl: z.ZodString;
@@ -377,8 +426,34 @@ export declare const EntitiesSchema: z.ZodObject<{
377
426
  }[] | undefined;
378
427
  migration?: string | undefined;
379
428
  rollback?: string | undefined;
380
- }>, "many">>;
381
- decisions: z.ZodDefault<z.ZodArray<z.ZodObject<{
429
+ }>, "many">>, {
430
+ id: string;
431
+ name: string;
432
+ ddl: string;
433
+ fields?: {
434
+ type: string;
435
+ name: string;
436
+ required: boolean;
437
+ default?: string | undefined;
438
+ desc?: string | undefined;
439
+ }[] | undefined;
440
+ migration?: string | undefined;
441
+ rollback?: string | undefined;
442
+ }[], {
443
+ id: string;
444
+ name: string;
445
+ ddl: string;
446
+ fields?: {
447
+ type: string;
448
+ name: string;
449
+ default?: string | undefined;
450
+ required?: boolean | undefined;
451
+ desc?: string | undefined;
452
+ }[] | undefined;
453
+ migration?: string | undefined;
454
+ rollback?: string | undefined;
455
+ }[] | undefined>;
456
+ decisions: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodObject<{
382
457
  id: z.ZodString;
383
458
  text: z.ZodString;
384
459
  }, "strip", z.ZodTypeAny, {
@@ -387,7 +462,13 @@ export declare const EntitiesSchema: z.ZodObject<{
387
462
  }, {
388
463
  id: string;
389
464
  text: string;
390
- }>, "many">>;
465
+ }>, "many">>, {
466
+ id: string;
467
+ text: string;
468
+ }[], {
469
+ id: string;
470
+ text: string;
471
+ }[] | undefined>;
391
472
  }, "strip", z.ZodTypeAny, {
392
473
  interfaces: {
393
474
  path: string;
@@ -481,14 +562,13 @@ export declare const EntitiesSchema: z.ZodObject<{
481
562
  }>;
482
563
  export type Entities = z.infer<typeof EntitiesSchema>;
483
564
  export declare const OutlinePointKindSchema: z.ZodEnum<["entity", "narrative", "mixed"]>;
484
- export type OutlinePointKind = z.infer<typeof OutlinePointKindSchema>;
485
565
  export declare const OutlinePointSchema: z.ZodObject<{
486
566
  id: z.ZodString;
487
567
  text: z.ZodString;
488
568
  required: z.ZodDefault<z.ZodBoolean>;
489
569
  kind: z.ZodDefault<z.ZodEnum<["entity", "narrative", "mixed"]>>;
490
- references: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
491
- source_segments: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
570
+ references: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
571
+ source_segments: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
492
572
  }, "strip", z.ZodTypeAny, {
493
573
  id: string;
494
574
  references: string[];
@@ -513,8 +593,8 @@ export declare const OutlineChapterSchema: z.ZodObject<{
513
593
  text: z.ZodString;
514
594
  required: z.ZodDefault<z.ZodBoolean>;
515
595
  kind: z.ZodDefault<z.ZodEnum<["entity", "narrative", "mixed"]>>;
516
- references: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
517
- source_segments: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
596
+ references: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
597
+ source_segments: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
518
598
  }, "strip", z.ZodTypeAny, {
519
599
  id: string;
520
600
  references: string[];
@@ -570,7 +650,6 @@ export declare const OutlineEntitySchema: z.ZodObject<{
570
650
  frozen: string;
571
651
  standalone?: boolean | undefined;
572
652
  }>;
573
- export type OutlineEntity = z.infer<typeof OutlineEntitySchema>;
574
653
  export declare const FillOnceSchema: z.ZodObject<{
575
654
  enabled: z.ZodDefault<z.ZodBoolean>;
576
655
  input_budget_tokens: z.ZodDefault<z.ZodNumber>;
@@ -581,7 +660,6 @@ export declare const FillOnceSchema: z.ZodObject<{
581
660
  enabled?: boolean | undefined;
582
661
  input_budget_tokens?: number | undefined;
583
662
  }>;
584
- export type FillOnce = z.infer<typeof FillOnceSchema>;
585
663
  export declare const OutlineSchema: z.ZodObject<{
586
664
  profile: z.ZodString;
587
665
  chapters: z.ZodArray<z.ZodObject<{
@@ -592,8 +670,8 @@ export declare const OutlineSchema: z.ZodObject<{
592
670
  text: z.ZodString;
593
671
  required: z.ZodDefault<z.ZodBoolean>;
594
672
  kind: z.ZodDefault<z.ZodEnum<["entity", "narrative", "mixed"]>>;
595
- references: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
596
- source_segments: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
673
+ references: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
674
+ source_segments: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
597
675
  }, "strip", z.ZodTypeAny, {
598
676
  id: string;
599
677
  references: string[];
@@ -635,7 +713,7 @@ export declare const OutlineSchema: z.ZodObject<{
635
713
  }[];
636
714
  optional?: boolean | undefined;
637
715
  }>, "many">;
638
- entities: z.ZodDefault<z.ZodArray<z.ZodObject<{
716
+ entities: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodObject<{
639
717
  id: z.ZodString;
640
718
  frozen: z.ZodString;
641
719
  standalone: z.ZodDefault<z.ZodBoolean>;
@@ -647,8 +725,16 @@ export declare const OutlineSchema: z.ZodObject<{
647
725
  id: string;
648
726
  frozen: string;
649
727
  standalone?: boolean | undefined;
650
- }>, "many">>;
651
- decisions: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
728
+ }>, "many">>, {
729
+ standalone: boolean;
730
+ id: string;
731
+ frozen: string;
732
+ }[], {
733
+ id: string;
734
+ frozen: string;
735
+ standalone?: boolean | undefined;
736
+ }[] | undefined>;
737
+ decisions: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
652
738
  fill_once: z.ZodOptional<z.ZodObject<{
653
739
  enabled: z.ZodDefault<z.ZodBoolean>;
654
740
  input_budget_tokens: z.ZodDefault<z.ZodNumber>;
@@ -712,7 +798,6 @@ export declare const OutlineSchema: z.ZodObject<{
712
798
  }>;
713
799
  export type Outline = z.infer<typeof OutlineSchema>;
714
800
  export declare const ConstraintItemKindSchema: z.ZodEnum<["entity", "decision", "point"]>;
715
- export type ConstraintItemKind = z.infer<typeof ConstraintItemKindSchema>;
716
801
  export declare const ConstraintItemSchema: z.ZodObject<{
717
802
  kind: z.ZodEnum<["entity", "decision", "point"]>;
718
803
  id: z.ZodString;
@@ -731,14 +816,14 @@ export declare const ChapterComponentSchema: z.ZodObject<{
731
816
  id: z.ZodString;
732
817
  title: z.ZodString;
733
818
  when: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, unknown>;
734
- depends_on: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
735
- outline_points: z.ZodDefault<z.ZodArray<z.ZodObject<{
819
+ depends_on: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
820
+ outline_points: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodObject<{
736
821
  id: z.ZodString;
737
822
  text: z.ZodString;
738
823
  required: z.ZodDefault<z.ZodBoolean>;
739
824
  kind: z.ZodDefault<z.ZodEnum<["entity", "narrative", "mixed"]>>;
740
- references: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
741
- source_segments: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
825
+ references: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
826
+ source_segments: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
742
827
  }, "strip", z.ZodTypeAny, {
743
828
  id: string;
744
829
  references: string[];
@@ -753,10 +838,24 @@ export declare const ChapterComponentSchema: z.ZodObject<{
753
838
  kind?: "entity" | "narrative" | "mixed" | undefined;
754
839
  required?: boolean | undefined;
755
840
  source_segments?: string[] | undefined;
756
- }>, "many">>;
841
+ }>, "many">>, {
842
+ id: string;
843
+ references: string[];
844
+ kind: "entity" | "narrative" | "mixed";
845
+ required: boolean;
846
+ text: string;
847
+ source_segments: string[];
848
+ }[], {
849
+ id: string;
850
+ text: string;
851
+ references?: string[] | undefined;
852
+ kind?: "entity" | "narrative" | "mixed" | undefined;
853
+ required?: boolean | undefined;
854
+ source_segments?: string[] | undefined;
855
+ }[] | undefined>;
757
856
  entities: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
758
857
  narratives: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
759
- gates: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
858
+ gates: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
760
859
  map_prompt: z.ZodOptional<z.ZodString>;
761
860
  output_budget_tokens: z.ZodOptional<z.ZodNumber>;
762
861
  }, "strip", z.ZodTypeAny, {
@@ -801,9 +900,10 @@ export declare const ScenarioProfileSchema: z.ZodObject<{
801
900
  id: z.ZodString;
802
901
  name: z.ZodString;
803
902
  required: z.ZodArray<z.ZodString, "many">;
804
- optional_candidates: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
805
- forbidden: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
806
- shared: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
903
+ optional_candidates: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
904
+ forbidden: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
905
+ shared: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
906
+ appendices: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodEnum<["anti-ai", "handoff"]>, "many">>, ("handoff" | "anti-ai")[], ("handoff" | "anti-ai")[] | undefined>;
807
907
  }, "strip", z.ZodTypeAny, {
808
908
  id: string;
809
909
  name: string;
@@ -811,6 +911,7 @@ export declare const ScenarioProfileSchema: z.ZodObject<{
811
911
  optional_candidates: string[];
812
912
  forbidden: string[];
813
913
  shared: string[];
914
+ appendices: ("handoff" | "anti-ai")[];
814
915
  }, {
815
916
  id: string;
816
917
  name: string;
@@ -818,10 +919,10 @@ export declare const ScenarioProfileSchema: z.ZodObject<{
818
919
  optional_candidates?: string[] | undefined;
819
920
  forbidden?: string[] | undefined;
820
921
  shared?: string[] | undefined;
922
+ appendices?: ("handoff" | "anti-ai")[] | undefined;
821
923
  }>;
822
924
  export type ScenarioProfile = z.infer<typeof ScenarioProfileSchema>;
823
925
  export declare const ReviewTargetSchema: z.ZodEnum<["entity", "narrative", "mixed"]>;
824
- export type ReviewTarget = z.infer<typeof ReviewTargetSchema>;
825
926
  export declare const ReviewFindingSchema: z.ZodObject<{
826
927
  id: z.ZodString;
827
928
  severity: z.ZodEnum<["critical", "important", "minor"]>;
@@ -858,7 +959,7 @@ export declare const ReviewResultSchema: z.ZodObject<{
858
959
  reviewer: z.ZodDefault<z.ZodEnum<["ai", "human"]>>;
859
960
  verdict: z.ZodEnum<["pass", "fail", "pass_with_waivers"]>;
860
961
  summary: z.ZodOptional<z.ZodString>;
861
- findings: z.ZodDefault<z.ZodArray<z.ZodObject<{
962
+ findings: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodObject<{
862
963
  id: z.ZodString;
863
964
  severity: z.ZodEnum<["critical", "important", "minor"]>;
864
965
  category: z.ZodString;
@@ -885,8 +986,26 @@ export declare const ReviewResultSchema: z.ZodObject<{
885
986
  target?: "entity" | "narrative" | "mixed" | undefined;
886
987
  chapter?: string | undefined;
887
988
  waiver_reason?: string | undefined;
888
- }>, "many">>;
889
- part_hashes: z.ZodDefault<z.ZodArray<z.ZodObject<{
989
+ }>, "many">>, {
990
+ status: "fixed" | "open" | "waived";
991
+ message: string;
992
+ id: string;
993
+ severity: "critical" | "important" | "minor";
994
+ target: "entity" | "narrative" | "mixed";
995
+ category: string;
996
+ chapter?: string | undefined;
997
+ waiver_reason?: string | undefined;
998
+ }[], {
999
+ message: string;
1000
+ id: string;
1001
+ severity: "critical" | "important" | "minor";
1002
+ category: string;
1003
+ status?: "fixed" | "open" | "waived" | undefined;
1004
+ target?: "entity" | "narrative" | "mixed" | undefined;
1005
+ chapter?: string | undefined;
1006
+ waiver_reason?: string | undefined;
1007
+ }[] | undefined>;
1008
+ part_hashes: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodObject<{
890
1009
  id: z.ZodString;
891
1010
  sha256: z.ZodString;
892
1011
  }, "strip", z.ZodTypeAny, {
@@ -895,7 +1014,33 @@ export declare const ReviewResultSchema: z.ZodObject<{
895
1014
  }, {
896
1015
  id: string;
897
1016
  sha256: string;
898
- }>, "many">>;
1017
+ }>, "many">>, {
1018
+ id: string;
1019
+ sha256: string;
1020
+ }[], {
1021
+ id: string;
1022
+ sha256: string;
1023
+ }[] | undefined>;
1024
+ /** decision 9 · tamper semantics: chapters the ENGINE legitimately changed after the review
1025
+ * snapshot (entity refill / narrative fix notes). The tamper check exempts these from the
1026
+ * post-review "stale" verdict — every other chapter must still match the reviewer-time
1027
+ * `part_hashes` snapshot. */
1028
+ post_review_repairs: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodObject<{
1029
+ chapter: z.ZodString;
1030
+ at: z.ZodString;
1031
+ }, "strip", z.ZodTypeAny, {
1032
+ at: string;
1033
+ chapter: string;
1034
+ }, {
1035
+ at: string;
1036
+ chapter: string;
1037
+ }>, "many">>, {
1038
+ at: string;
1039
+ chapter: string;
1040
+ }[], {
1041
+ at: string;
1042
+ chapter: string;
1043
+ }[] | undefined>;
899
1044
  }, "strip", z.ZodTypeAny, {
900
1045
  schema: "specflow.document.review/v1";
901
1046
  change: string;
@@ -916,6 +1061,10 @@ export declare const ReviewResultSchema: z.ZodObject<{
916
1061
  id: string;
917
1062
  sha256: string;
918
1063
  }[];
1064
+ post_review_repairs: {
1065
+ at: string;
1066
+ chapter: string;
1067
+ }[];
919
1068
  summary?: string | undefined;
920
1069
  }, {
921
1070
  schema: "specflow.document.review/v1";
@@ -938,11 +1087,16 @@ export declare const ReviewResultSchema: z.ZodObject<{
938
1087
  id: string;
939
1088
  sha256: string;
940
1089
  }[] | undefined;
1090
+ post_review_repairs?: {
1091
+ at: string;
1092
+ chapter: string;
1093
+ }[] | undefined;
941
1094
  }>;
942
1095
  export type ReviewResult = z.infer<typeof ReviewResultSchema>;
943
1096
  export declare function parseReviewResult(raw: unknown): ReviewResult;
944
1097
  export declare function parseInterfaceEntity(raw: unknown): InterfaceEntity;
945
1098
  export declare function parseTableEntity(raw: unknown): TableEntity;
1099
+ export declare function parseDecisionEntity(raw: unknown): DecisionEntity;
946
1100
  export declare function parseEntities(raw: unknown): Entities;
947
1101
  export declare function parseOutline(raw: unknown): Outline;
948
1102
  export declare function parseChapterComponent(raw: unknown): ChapterComponent;
@@ -10,6 +10,9 @@
10
10
  * - Outline is the two-pass core IR (replaces ACP index.yaml/parts_order)
11
11
  */
12
12
  import { z } from 'zod';
13
+ // ============= 通用归一化(P2) =============
14
+ /** 把显式 null 归一为 undefined,使 `.default([])` 对 null 与缺键行为一致(zod v3 默认不处理 null)。 */
15
+ const nullish = (schema) => z.preprocess((v) => (v === null ? undefined : v), schema);
13
16
  // ============= Field-level contract primitives =============
14
17
  export const FieldSchema = z.object({
15
18
  name: z.string().min(1),
@@ -25,7 +28,7 @@ export const ErrorSpecSchema = z.object({
25
28
  });
26
29
  // ============= Contract entities =============
27
30
  export const InterfaceEntitySchema = z.object({
28
- id: z.string().min(1), // stable id, e.g. "I1"
31
+ id: z.string().trim().min(1), // stable id, e.g. "I1"
29
32
  name: z.string().min(1), // e.g. "SubmitStepResult"
30
33
  method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),
31
34
  path: z.string().min(1),
@@ -35,7 +38,7 @@ export const InterfaceEntitySchema = z.object({
35
38
  failure_examples: z.array(z.string()).min(1), // G2 hard gate
36
39
  });
37
40
  export const TableEntitySchema = z.object({
38
- id: z.string().min(1), // stable id, e.g. "T1"
41
+ id: z.string().trim().min(1), // stable id, e.g. "T1"
39
42
  name: z.string().min(1),
40
43
  ddl: z.string().min(1),
41
44
  fields: z.array(FieldSchema).optional(),
@@ -43,32 +46,33 @@ export const TableEntitySchema = z.object({
43
46
  rollback: z.string().optional(), // G4: rollback compat
44
47
  });
45
48
  export const DecisionEntitySchema = z.object({
46
- id: z.string().min(1), // e.g. "D1"
49
+ id: z.string().trim().min(1), // e.g. "D1"
47
50
  text: z.string().min(1),
48
51
  });
49
52
  export const EntitiesSchema = z.object({
50
- interfaces: z.array(InterfaceEntitySchema).default([]),
51
- tables: z.array(TableEntitySchema).default([]),
52
- decisions: z.array(DecisionEntitySchema).default([]),
53
+ interfaces: nullish(z.array(InterfaceEntitySchema).default([])),
54
+ tables: nullish(z.array(TableEntitySchema).default([])),
55
+ decisions: nullish(z.array(DecisionEntitySchema).default([])),
53
56
  });
54
57
  // ============= Outline (two-pass core IR, decision 6/8/9) =============
55
58
  export const OutlinePointKindSchema = z.enum(['entity', 'narrative', 'mixed']);
56
59
  export const OutlinePointSchema = z.object({
57
- id: z.string().min(1),
60
+ id: z.string().trim().min(1),
58
61
  text: z.string().min(1),
59
62
  required: z.boolean().default(true),
60
63
  kind: OutlinePointKindSchema.default('mixed'), // decision 9: coverage channel
61
- references: z.array(z.string()).default([]), // decision 8: entity ids (full contract injection)
62
- source_segments: z.array(z.string()).default([]), // decision 9: original input segment ids
64
+ references: nullish(z.array(z.string()).default([])), // decision 8: entity ids (full contract injection)
65
+ source_segments: nullish(z.array(z.string()).default([])), // decision 9: original input segment ids
63
66
  });
64
67
  export const OutlineChapterSchema = z.object({
65
- id: z.string().min(1),
68
+ // 白名单字符集与 paths.ts CHAPTER_ID_RE 一致:拒绝路径分隔符/`..`/空串(防路径穿越)
69
+ id: z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/, 'chapter id must be a safe identifier'),
66
70
  title: z.string().min(1),
67
71
  points: z.array(OutlinePointSchema).min(1),
68
72
  optional: z.boolean().default(false),
69
73
  });
70
74
  export const OutlineEntitySchema = z.object({
71
- id: z.string().min(1),
75
+ id: z.string().trim().min(1),
72
76
  frozen: z.string().min(1), // frozen name, must not drift
73
77
  standalone: z.boolean().default(false), // decision 9: referenced by decisions only, no contract injection
74
78
  });
@@ -79,43 +83,44 @@ export const FillOnceSchema = z.object({
79
83
  export const OutlineSchema = z.object({
80
84
  profile: z.string().min(1),
81
85
  chapters: z.array(OutlineChapterSchema).min(1), // order = render order
82
- entities: z.array(OutlineEntitySchema).default([]),
83
- decisions: z.array(z.string()).default([]),
86
+ entities: nullish(z.array(OutlineEntitySchema).default([])),
87
+ decisions: nullish(z.array(z.string()).default([])),
84
88
  fill_once: FillOnceSchema.optional(),
85
89
  });
86
90
  // ============= Constraint list (decision 8 · P1-D) =============
87
91
  export const ConstraintItemKindSchema = z.enum(['entity', 'decision', 'point']);
88
92
  export const ConstraintItemSchema = z.object({
89
93
  kind: ConstraintItemKindSchema,
90
- id: z.string().min(1),
94
+ id: z.string().trim().min(1),
91
95
  label: z.string().min(1), // one-line label for digest prompt
92
96
  });
93
97
  // ============= Chapter component (reusable) =============
94
98
  export const ChapterComponentSchema = z.object({
95
- id: z.string().min(1),
99
+ id: z.string().trim().min(1),
96
100
  title: z.string().min(1),
97
- when: z.preprocess((v) => (v === null || v === '' ? undefined : v), z.string().optional()), // condition DSL: input.<feature> && / || / !
98
- depends_on: z.array(z.string()).default([]),
99
- outline_points: z.array(OutlinePointSchema).default([]), // candidate points (Pass 1 reference)
101
+ when: z.preprocess((v) => (typeof v === 'string' ? (v.trim() === '' ? undefined : v.trim()) : v === null ? undefined : v), z.string().optional()), // condition DSL: input.<feature> && / || / !
102
+ depends_on: nullish(z.array(z.string()).default([])),
103
+ outline_points: nullish(z.array(OutlinePointSchema).default([])), // candidate points (Pass 1 reference)
100
104
  entities: z.record(z.any()).optional(), // chapter entity schema description
101
105
  narratives: z.record(z.any()).optional(),
102
- gates: z.array(z.string()).default([]),
106
+ gates: nullish(z.array(z.string()).default([])),
103
107
  map_prompt: z.string().optional(),
104
108
  output_budget_tokens: z.number().int().positive().optional(), // per-chapter output cap
105
109
  });
106
110
  // ============= Scenario profile (three-way classification, decision 8 · P1-J) =============
107
111
  export const ScenarioProfileSchema = z.object({
108
- id: z.string().min(1),
112
+ id: z.string().trim().min(1),
109
113
  name: z.string().min(1),
110
114
  required: z.array(z.string()).min(1), // must be included by outline
111
- optional_candidates: z.array(z.string()).default([]), // LLM may choose (subject to when)
112
- forbidden: z.array(z.string()).default([]), // never appear in this scenario
113
- shared: z.array(z.string()).default([]), // global chapters allowed in dependency closure
115
+ optional_candidates: nullish(z.array(z.string()).default([])), // LLM may choose (subject to when)
116
+ forbidden: nullish(z.array(z.string()).default([])), // never appear in this scenario
117
+ shared: nullish(z.array(z.string()).default([])), // global chapters allowed in dependency closure
118
+ appendices: nullish(z.array(z.enum(['anti-ai', 'handoff'])).default([])), // §7.2: rendered 附录 B/C
114
119
  });
115
120
  // ============= Review (decision 9 · target + max_rounds) =============
116
121
  export const ReviewTargetSchema = z.enum(['entity', 'narrative', 'mixed']);
117
122
  export const ReviewFindingSchema = z.object({
118
- id: z.string().min(1),
123
+ id: z.string().trim().min(1),
119
124
  severity: z.enum(['critical', 'important', 'minor']),
120
125
  category: z.string().min(1),
121
126
  message: z.string().min(1),
@@ -131,8 +136,13 @@ export const ReviewResultSchema = z.object({
131
136
  reviewer: z.enum(['ai', 'human']).default('ai'),
132
137
  verdict: z.enum(['pass', 'fail', 'pass_with_waivers']),
133
138
  summary: z.string().optional(),
134
- findings: z.array(ReviewFindingSchema).default([]),
135
- part_hashes: z.array(z.object({ id: z.string(), sha256: z.string().min(64).max(64) })).default([]),
139
+ findings: nullish(z.array(ReviewFindingSchema).default([])),
140
+ part_hashes: nullish(z.array(z.object({ id: z.string().trim().min(1), sha256: z.string().regex(/^[a-f0-9]{64}$/i, 'sha256 must be 64 hex chars') })).default([])),
141
+ /** decision 9 · tamper semantics: chapters the ENGINE legitimately changed after the review
142
+ * snapshot (entity refill / narrative fix notes). The tamper check exempts these from the
143
+ * post-review "stale" verdict — every other chapter must still match the reviewer-time
144
+ * `part_hashes` snapshot. */
145
+ post_review_repairs: nullish(z.array(z.object({ chapter: z.string().min(1), at: z.string().min(1) })).default([])),
136
146
  });
137
147
  export function parseReviewResult(raw) {
138
148
  return ReviewResultSchema.parse(raw);
@@ -144,6 +154,9 @@ export function parseInterfaceEntity(raw) {
144
154
  export function parseTableEntity(raw) {
145
155
  return TableEntitySchema.parse(raw);
146
156
  }
157
+ export function parseDecisionEntity(raw) {
158
+ return DecisionEntitySchema.parse(raw);
159
+ }
147
160
  export function parseEntities(raw) {
148
161
  return EntitiesSchema.parse(raw);
149
162
  }
@@ -7,10 +7,10 @@ const MARKER_RULES = [
7
7
  { id: 'sub.propose.first_iteration', skill: 'propose', includes: ['first-iteration deep-analysis pass'] },
8
8
  { id: 'sub.refine.multi_round', skill: 'refine', includes: ['auto-multi-round loop', '## LOOP (Stages 2-4)'] },
9
9
  { id: 'sub.refine.challenge_scope', skill: 'refine', includes: ['Challenge Behaviors #3 and #4', 'question scope'] },
10
- { id: 'sub.document.two_pass_outline', skill: 'document', includes: ['outline', 'Pass 1'] },
11
- { id: 'sub.document.fill_in_by_points', skill: 'document', includes: ['填空', '要点'] },
12
- { id: 'sub.document.contract_injection', skill: 'document', includes: ['契约注入', 'references'] },
13
- { id: 'sub.document.coverage_check', skill: 'document', includes: ['覆盖', 'coverage'] },
10
+ { id: 'sub.techdoc.two_pass_outline', skill: 'techdoc', includes: ['outline', 'Pass 1'] },
11
+ { id: 'sub.techdoc.fill_in_by_points', skill: 'techdoc', includes: ['填空', '要点'] },
12
+ { id: 'sub.techdoc.contract_injection', skill: 'techdoc', includes: ['契约注入', 'references'] },
13
+ { id: 'sub.techdoc.coverage_check', skill: 'techdoc', includes: ['覆盖', 'coverage'] },
14
14
  { id: 'sub.apply.phase_a', skill: 'apply', includes: ['## Phase A: Task Rewrite'] },
15
15
  { id: 'sub.apply.phase_b', skill: 'apply', includes: ['## Phase B: Subagent TDD Execution'] },
16
16
  { id: 'sub.apply.gap_detection', skill: 'apply', includes: ['Gap Detection'] },
@@ -2,7 +2,8 @@ export const COMMAND_CATALOG = [
2
2
  { id: 'explore', description: 'Think-before-propose exploration when requirements are fuzzy' },
3
3
  { id: 'propose', description: 'Create proposal and spec artifacts for a change' },
4
4
  { id: 'refine', description: 'Refine and iterate on spec artifacts' },
5
- { id: 'document', description: 'Generate scenario-based solution documents via the Generic LongDoc Engine' },
5
+ { id: 'techdoc', description: 'Generate scenario-based solution documents via the Generic LongDoc Engine' },
6
+ { id: 'techdoc-synth', description: 'Synthesize multi-repo four artifacts into ONE cross-repo document (document + synthesize; scene auto-detect when --profile omitted)' },
6
7
  { id: 'apply', description: 'Implement tasks from a change spec' },
7
8
  { id: 'review', description: 'Review code changes against spec' },
8
9
  { id: 'test', description: 'Run tests and verify coverage' },
@@ -13,10 +13,10 @@ export const CAPABILITY_MANIFEST = [
13
13
  { id: 'sub.propose.first_iteration', commandId: 'propose', level: 'sub-capability', required: true },
14
14
  { id: 'sub.refine.multi_round', commandId: 'refine', level: 'sub-capability', required: true },
15
15
  { id: 'sub.refine.challenge_scope', commandId: 'refine', level: 'sub-capability', required: true },
16
- { id: 'sub.document.two_pass_outline', commandId: 'document', level: 'sub-capability', required: true },
17
- { id: 'sub.document.fill_in_by_points', commandId: 'document', level: 'sub-capability', required: true },
18
- { id: 'sub.document.contract_injection', commandId: 'document', level: 'sub-capability', required: true },
19
- { id: 'sub.document.coverage_check', commandId: 'document', level: 'sub-capability', required: true },
16
+ { id: 'sub.techdoc.two_pass_outline', commandId: 'techdoc', level: 'sub-capability', required: true },
17
+ { id: 'sub.techdoc.fill_in_by_points', commandId: 'techdoc', level: 'sub-capability', required: true },
18
+ { id: 'sub.techdoc.contract_injection', commandId: 'techdoc', level: 'sub-capability', required: true },
19
+ { id: 'sub.techdoc.coverage_check', commandId: 'techdoc', level: 'sub-capability', required: true },
20
20
  { id: 'sub.apply.phase_a', commandId: 'apply', level: 'sub-capability', required: true },
21
21
  { id: 'sub.apply.phase_b', commandId: 'apply', level: 'sub-capability', required: true },
22
22
  { id: 'sub.apply.gap_detection', commandId: 'apply', level: 'sub-capability', required: true },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gordon.gan/specflow",
3
- "version": "1.8.0-alpha",
3
+ "version": "1.8.1-beta",
4
4
  "type": "module",
5
5
  "description": "SpecFlow — unified spec-driven development: OpenSpec planning + Superpowers execution in one CLI and cross-IDE workflow",
6
6
  "keywords": [
@@ -55,6 +55,7 @@
55
55
  "release:major": "npm version major && git push && git push --tags"
56
56
  },
57
57
  "dependencies": {
58
+ "@gordon.gan/specflow": "^1.8.0-alpha",
58
59
  "chalk": "^5.4.1",
59
60
  "commander": "^13.1.0",
60
61
  "js-yaml": "^4.1.0",
@@ -5,4 +5,5 @@
5
5
  - kind=narrative|mixed 的要点 → 产出叙述 Markdown
6
6
  - 遵循 prompts/shared/artifact-language.md 的中文叙述规范(简要;怎么做用 1. 2. 3.)
7
7
  - 禁止 stub(TODO/待补充/此处省略);禁止含糊词
8
+ - **事实从工程数据源提取,意图向用户询问,推理由你完成**(见 `prompts/document/shared/grounding.md`):本章涉及的接口字段/表列/组件名/依赖版本必须来自工程,缺失的关键信息列入缺口清单一次性询问用户,禁止编造
8
9
  - 涉及项目规约/DB/API/前端约束时,遵循「项目约定 > SpecFlow guidance > LLM」优先级
@@ -0,0 +1,29 @@
1
+ # 附录填充:anti-ai(去AI味自检 · 附录 B)
2
+
3
+ 你是方案文档撰写员。为整份方案写「去AI味自检」条目,写入 `appendices/anti-ai.md`。
4
+
5
+ ## 硬规则(frontend-dev-guide §十一 → README §1.6)
6
+
7
+ 1. **至少 3 条**:每条自检对应五维中的一维,格式 `- 维度名:自检陈述`(如 `- 不做决策:明确写了本期不做什么(…),不是只列功能清单`)。
8
+ 2. **对着五维写**:
9
+ | 维度 | 自检问题 | AI 味反例 |
10
+ |------|---------|----------|
11
+ | 不做决策 | 明确说了「不做什么」? | 只列功能清单 |
12
+ | 数字具体 | 指标/约束具体到数字? | 「提升体验」 |
13
+ | 竞品立场 | 与竞品差异有一句话立场? | 「借鉴头部最佳实践」 |
14
+ | 此刻合理性 | 为什么现在做?不做会怎样? | 不解释时机 |
15
+ | 优先级倾斜 | P0/P1 有倾斜逻辑? | 全功能同等权重 |
16
+ 3. **必须引用本文档的实际内容**:每条自检要能指到正文里的具体段落/数字(如「正文 §Benchmark 给了原始测量值 82ms/320ms,不是只说『更快』」)。禁止写与本文档无关的通用自检。
17
+ 4. **诚实**:自检条目是「我确实做到了」的声明,不是愿望清单。没做到的维度不要硬凑,写「本迭代不适用」并说明理由。
18
+ 5. **禁止 stub**(TODO/待补充/此处省略);每条不少于 15 字。
19
+
20
+ ## 输出示例
21
+
22
+ ```markdown
23
+ - 不做决策:明确写了预研不做架构/接口/UI 实现设计(§6.3 分界),不是只列候选功能。
24
+ - 数字具体:benchmark 给出同条件同设备原始测量值(冷启动 82ms vs 320ms),不是「性能大幅提升」。
25
+ - 竞品立场:一句话立场写明「本项目不选 Bun」及原因,不是「借鉴头部最佳实践」。
26
+ - 此刻合理性:说明了本次预研触发点(生态缺口风险),不做会怎样已写(§research)。
27
+ ```
28
+
29
+ > 优先级:项目约定 > SpecFlow guidance > LLM。