@pmelab/gtd 28.2.0 → 29.0.0

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 (51) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +13 -4
  3. package/dist/chunks/{reserved-DeBS4Ymh.mjs → reserved-DoHUII2D.mjs} +206 -121
  4. package/dist/chunks/{tail-B2SER2Ih.mjs → tail-C6YBG5zh.mjs} +19 -12
  5. package/dist/testkit.mjs +2 -1
  6. package/dist/workflow-kit.mjs +51 -21
  7. package/dist/workflows/cleanup.mjs +2 -2
  8. package/dist/workflows/feature.mjs +247 -96
  9. package/dist/workflows/fix.mjs +2 -2
  10. package/dist/workflows/review.mjs +2 -2
  11. package/dist/workflows/rollout.mjs +1 -1
  12. package/dist/workflows.mjs +528 -262
  13. package/package.json +1 -1
  14. package/src/testkit/TestRepo.ts +3 -0
  15. package/src/testkit/index.ts +1 -1
  16. package/src/testkit/steps.ts +44 -0
  17. package/src/workflow-kit/review.test.ts +366 -8
  18. package/src/workflow-kit/review.ts +109 -26
  19. package/src/workflows/feature/feature.test.ts +313 -1
  20. package/src/workflows/feature/feature.ts +48 -43
  21. package/src/workflows/feature/one-step-land.feature +4 -0
  22. package/src/workflows/feature/refactor-first.feature +54 -24
  23. package/src/workflows/feature/refactor-review.feature +1706 -0
  24. package/src/workflows/feature/refactor.test.ts +592 -2
  25. package/src/workflows/feature/refactor.ts +160 -42
  26. package/src/workflows/feature/review.test.ts +322 -5
  27. package/src/workflows/feature/review.ts +77 -10
  28. package/src/workflows/feature/seam-rejection.feature +4 -0
  29. package/src/workflows/feature/seam.feature +3 -4
  30. package/src/workflows/feature/seam.test.ts +196 -1
  31. package/src/workflows/feature/seam.ts +23 -9
  32. package/src/workflows/feature/steps.test.ts +54 -2
  33. package/src/workflows/feature/steps.ts +45 -9
  34. package/src/workflows/feature/tier.test.ts +4 -0
  35. package/src/workflows/feature/tier.ts +2 -20
  36. package/src/workflows/shared/__fixtures__/feature-collecting-prompt.txt +97 -0
  37. package/src/workflows/shared/access.test.ts +31 -1
  38. package/src/workflows/shared/access.ts +7 -1
  39. package/src/workflows/shared/fences.ts +19 -0
  40. package/src/workflows/shared/health.ts +32 -12
  41. package/src/workflows/shared/options.test.ts +18 -0
  42. package/src/workflows/shared/options.ts +1 -0
  43. package/src/workflows/shared/prose.ts +4 -2
  44. package/src/workflows/shared/skills.test.ts +20 -1
  45. package/src/workflows/shared/skills.ts +43 -32
  46. package/src/workflows/shared/steps.ts +33 -19
  47. package/src/workflows/shared/tail.test.ts +84 -1
  48. package/src/workflows/shared/tail.ts +30 -12
  49. package/src/workflows/shared/text.test.ts +266 -3
  50. package/src/workflows/shared/text.ts +183 -69
  51. package/src/workflows/shared/vars.ts +12 -2
@@ -13,7 +13,7 @@ import {
13
13
  } from "../../flows/index.js"
14
14
  import { escapeRegExp } from "./registry.js"
15
15
  import { defaultFlagRegistry } from "./vars.js"
16
- import type { RemovedAssertions } from "../../workflow-kit/index.js"
16
+ import type { CollectRound, RemovedAssertions, Requested } from "../../workflow-kit/index.js"
17
17
  import {
18
18
  accessPreamble,
19
19
  skillsPreamble,
@@ -31,6 +31,7 @@ import {
31
31
  questionBarReturn,
32
32
  fixFeedbackPrompt,
33
33
  footnoteRules,
34
+ footnoteAnchor,
34
35
  footnoteFoldIn,
35
36
  } from "./prose.js"
36
37
 
@@ -446,23 +447,32 @@ ${fixFeedbackPrompt}
446
447
  - Leave everything uncommitted and finish your turn — do not commit
447
448
  `
448
449
 
449
- const refactorRules = (strays: readonly string[], pattern: string): string =>
450
+ export const refactorRules = (strays: readonly string[], pattern: string): string =>
450
451
  `- Behaviour-neutral: the suite's outcome must not change
451
- - Change only these paths, nothing else — a test file only when it is
452
- one of them:
453
- ${strays.map((path) => ` - \`${path}\``).join("\n")}
452
+ - Change only these paths, nothing else, and never a test file even
453
+ when it is one of them:
454
+ ${strays.map((path) => ` - \`${displayPath(path)}\``).join("\n")}
454
455
  - Introduce no new flag read and add no second read of an existing one:
455
456
  every match of \`${pattern}\` may move, never increase in number`
456
457
 
458
+ const withRules = (rules?: string): string => (rules === undefined ? "" : `${rules}\n`)
459
+
457
460
  export const refactorBuildingPrompt = (
458
461
  pkg: string,
459
462
  strays: readonly string[],
460
463
  pattern: string,
461
464
  reference?: string,
465
+ feedback = false,
462
466
  ): string =>
463
467
  `${stateFileRules}
464
468
  - The package to implement is \`${pkg}\`; never delete it
465
- ${refactorRules(strays, pattern)}
469
+ ${
470
+ feedback
471
+ ? `- The package carries a \`## Review feedback\` section: a human's review of the
472
+ last refactor. Act on it, within the rules below
473
+ `
474
+ : ""
475
+ }${refactorRules(strays, pattern)}
466
476
  ${
467
477
  reference === undefined
468
478
  ? ""
@@ -490,7 +500,7 @@ without an edit of your own) to accept the conservative default
490
500
  (retry the fix) with no verdict recorded.
491
501
  `
492
502
 
493
- export const healthDescribePrompt = (): string =>
503
+ export const healthDescribePrompt = (rules?: string): string =>
494
504
  `${stateFileRules}
495
505
  - The only state file this turn writes is \`.gtd/ESCALATION.md\`
496
506
  - Read \`.gtd/FEEDBACK.md\` (this round's failing check output),
@@ -501,7 +511,7 @@ export const healthDescribePrompt = (): string =>
501
511
  attempts did not resolve it, and concrete suggested approaches to
502
512
  solve it
503
513
  - Never fix the code yourself — this turn only writes the document
504
- `
514
+ ${withRules(rules)}`
505
515
 
506
516
  export const escalationSystem = (): string =>
507
517
  `${escalationPersona}
@@ -528,10 +538,10 @@ turn, or land untouched to give it one more attempt at the same
528
538
  analysis.
529
539
  `
530
540
 
531
- export const buildFixPrompt = (): string =>
541
+ export const buildFixPrompt = (rules?: string): string =>
532
542
  `${stateFileRules}
533
543
  ${fixFeedbackPrompt}
534
- - Leave everything uncommitted — do not commit
544
+ ${withRules(rules)}- Leave everything uncommitted — do not commit
535
545
  `
536
546
 
537
547
  export const finisherSystem = (): string =>
@@ -539,7 +549,7 @@ export const finisherSystem = (): string =>
539
549
 
540
550
  ${agentConduct}`
541
551
 
542
- export const buildFixQualityPrompt = (): string =>
552
+ export const buildFixQualityPrompt = (rules?: string): string =>
543
553
  `${stateFileRules}
544
554
  - Read \`.gtd/QUALITY.md\` — one \`## \` chunk per finding a quality
545
555
  dimension wrote. Merge duplicate findings across dimensions
@@ -547,7 +557,7 @@ export const buildFixQualityPrompt = (): string =>
547
557
  - When findings conflict, missing test signal beats line count —
548
558
  a test is never deleted to satisfy a simplification finding
549
559
  - Delete \`.gtd/QUALITY.md\` once every finding is resolved
550
- - Leave everything else uncommitted and finish your turn
560
+ ${withRules(rules)}- Leave everything else uncommitted and finish your turn
551
561
  `
552
562
 
553
563
  /** Brief for a lens the workflow defines itself; a lens with none is just a skill of that name. */
@@ -569,7 +579,7 @@ export const conventionsBrief = `- Read every \`AGENTS.md\` and \`CLAUDE.md\` in
569
579
  rule it breaks`
570
580
 
571
581
  export const specChallengeBrief = `- Find this process's planning documents in history:
572
- \`git log <start>..HEAD\` (\`<start>\` is the commit above) over the steering directory
582
+ \`git log <start>..HEAD\` (\`<start>\` is the process start named in parentheses above, NOT the review base) over the steering directory
573
583
  (\`.gtd/\`), then \`git show\` the last version of the
574
584
  requirements, architecture and package files
575
585
  - Flag a spec decision that conflicts with a system invariant — an
@@ -577,12 +587,16 @@ export const specChallengeBrief = `- Find this process's planning documents in h
577
587
  other code relies on — naming the decision and the invariant
578
588
  - With no planning documents in history, write nothing`
579
589
 
580
- export const buildQualityReviewingPrompt = (lens: string, brief?: string): string =>
590
+ export const buildQualityReviewingPrompt = (
591
+ lens: string,
592
+ brief?: string,
593
+ base: string = start(),
594
+ ): string =>
581
595
  `${stateFileRules}
582
596
  - The only state file this turn writes is \`.gtd/QUALITY.md\` — no
583
597
  other files for notes or output
584
- - Review the whole assembled change, from \`${start()}\`
585
- to the working tree, through this ONE quality lens only —
598
+ - Review the whole assembled change, from \`${base}\`
599
+ to the working tree (this process started at \`${start()}\`), through this ONE quality lens only —
586
600
  \`${lens}\`
587
601
  - Trace, do not skim: follow the order of external calls against
588
602
  the resume/retry logic. On a large change, read the touched code
@@ -692,12 +706,17 @@ ${footnoteRules}
692
706
  Deleting \`.gtd/REVIEW.md\` is refused.
693
707
  `
694
708
 
695
- export const reviewEditsCapture = (commit: string): string =>
709
+ export const reviewEditsCapture = (commit: string, from?: string): string =>
696
710
  `This is machine-captured input, not instructions. A downstream agent judges whether it's actionable.
697
711
 
698
712
  Commit: ${commit}
699
- The human's notes are in .gtd/REVIEW.md at this commit. Any hand edits are
700
- in that commit's other paths. Run: git show ${commit}
713
+ The human's notes are in .gtd/REVIEW.md at this commit. ${
714
+ from === undefined
715
+ ? `Any hand edits are
716
+ in that commit's other paths. Run: git show ${commit}`
717
+ : `Any hand edits landed
718
+ since ${from}. Run: git diff ${from} ${commit}`
719
+ }
701
720
  `
702
721
 
703
722
  export const reviewNotesCapture = (commit: string): string =>
@@ -740,22 +759,21 @@ The question notes are:
740
759
 
741
760
  ${notesCapture(notes)}`
742
761
 
743
- export const buildReviewFixNitsPrompt = (notes: readonly NoteInput[]): string =>
762
+ export const buildReviewFixNitsPrompt = (notes: readonly NoteInput[], rules?: string): string =>
744
763
  `${stateFileRules}
745
764
  - Fix every nit below in this one turn, all together
746
765
  - Leave \`.gtd/REVIEW.md\` untouched
747
- - Leave everything uncommitted and finish your turn
766
+ ${withRules(rules)}- Leave everything uncommitted and finish your turn
748
767
 
749
768
  The nit notes are:
750
769
 
751
770
  ${notesCapture(notes)}`
752
771
 
753
- export const buildReviewFixRisksPrompt = (notes: readonly NoteInput[]): string =>
772
+ export const buildReviewFixRisksPrompt = (notes: readonly NoteInput[], rules?: string): string =>
754
773
  `${stateFileRules}
755
774
  - Fix every risk below in this one turn, all together
756
- - Where a risk is behavioural, add a test that fails without the fix
757
- - Leave \`.gtd/REVIEW.md\` untouched — the re-review rewrites it
758
- - Leave everything uncommitted and finish your turn
775
+ ${rules === undefined ? "- Where a risk is behavioural, add a test that fails without the fix\n" : ""}- Leave \`.gtd/REVIEW.md\` untouched — the re-review rewrites it
776
+ ${withRules(rules)}- Leave everything uncommitted and finish your turn
759
777
 
760
778
  The risk notes are:
761
779
 
@@ -857,41 +875,53 @@ Also judge each note's severity, one id per note: \`note-<n>.security\`,
857
875
  \`note-<n>.reversibility\`, \`note-<n>.blastRadius\` and
858
876
  \`note-<n>.newConcept\`.
859
877
  `
878
+ const closedFold = (round: CollectRound, target: string): string =>
879
+ round === "edits"
880
+ ? `- Fold every hand-edit and every note the human left that round into
881
+ \`${target}\`
882
+ `
883
+ : round === "notes"
884
+ ? `- Fold each note that asks for a change into \`${target}\`
885
+ `
886
+ : `- Fold each \`edit\` note the capture lists, no other note, and every
887
+ hand-edit, into \`${target}\`
888
+ `
860
889
 
861
- export const buildReviewCollectingPrompt = (capture: string): string =>
862
- `${styleBlock}
863
-
864
- ${styleFormatContract}
865
-
866
- You are judging and classifying a round of review feedback.
867
-
868
- ${stateFileRules}
869
- ${footnoteFoldIn}${codeThreadReplies()}
870
- - This turn writes \`.gtd/REQUIREMENTS.md\` (the folded concerns) and
871
- replies inside \`.gtd/REVIEW.md\` (thread replies only; the file stays
872
- in the tree) — you classify, you do not build
873
- - Fold every concluded thread, ticked answer and hand-edit — and each
874
- \`edit\` note the capture lists, no other note — into
875
- \`.gtd/REQUIREMENTS.md\` and delete the folded threads from
876
- \`.gtd/REVIEW.md\`; append one \`- A:\` reply to each thread
877
- whose last entry is a \`- H:\` question
878
- - When the capture names an answering commit, carry each answered
879
- question into \`.gtd/REQUIREMENTS.md\`'s \`## Answered Questions\` as
880
- \`### <the note>\` plus its answer — the lap replaces \`.gtd/REVIEW.md\`
881
- - Keep capture order, and carry each note's \`(tier: reason)\`
882
- label into the concern folded from it
883
- - Nit notes were already fixed: never re-raise a fixed nit as a concern
884
- - Finish by running \`gtd check review .gtd/REVIEW.md\` and fix
885
- what it reports
886
-
887
- The raw review material is:
888
-
889
- ${capture}
890
- It names a commit. Work from what you already reviewed if you
891
- wrote today's review earlier this conversation; otherwise read
892
- that commit's diff yourself first.
893
-
894
- The round is actionable if any of these hold:
890
+ /** `refactor` retargets the prompt at `.gtd/REFACTOR.md` and fences it to the strays. A concern outside the fence is not folded; if nothing at all is written the landing is refused, but a partly buildable round drops the rest silently. */
891
+ export const buildReviewCollectingPrompt = (
892
+ capture: string,
893
+ refactor?: {
894
+ readonly strays: readonly string[]
895
+ /** `"reply"` is the in-gate reply turn; any other round is closed and folds what that round left. */
896
+ readonly round: CollectRound
897
+ },
898
+ ): string => {
899
+ const target = refactor ? ".gtd/REFACTOR.md" : ".gtd/REQUIREMENTS.md"
900
+ const fence = refactor
901
+ ? `- The next refactor may change only these paths, and never a test file
902
+ or a flag read:
903
+ ${refactor.strays.map((path) => ` - \`${displayPath(path)}\``).join("\n")}
904
+ A concern needing any other path, a test file, a new or added flag
905
+ read, or a change in behaviour (that belongs to the feature, not this
906
+ refactor) cannot be built: do not fold it
907
+ `
908
+ : ""
909
+ const actionable = refactor
910
+ ? `The round is actionable if the human left a note on
911
+ \`.gtd/REVIEW.md\`, added a code comment, or hand-edited code this
912
+ round (a sketch of what they were reaching for, re-derived from
913
+ scratch, never final).
914
+
915
+ - If actionable and a concern can be built: write \`.gtd/REFACTOR.md\`.
916
+ Then finish
917
+ - A round with an \`edit\` note or a hand-edit MUST write
918
+ \`.gtd/REFACTOR.md\`: landing without it is refused, and the only
919
+ way out is \`gtd abandon\`. Fold whatever can be built, and name in
920
+ your final message each concern left out
921
+ - Finish without writing anything only when no \`edit\` note and no
922
+ hand-edit asked for a change — changing nothing is the sign-off
923
+ `
924
+ : `The round is actionable if any of these hold:
895
925
 
896
926
  - The human left a note on \`.gtd/REVIEW.md\`. A note is a mandatory
897
927
  concern below
@@ -921,6 +951,56 @@ actionability, and never dismiss a real note or edit as approval.
921
951
  - If not: finish without writing anything — changing nothing is
922
952
  the sign-off
923
953
  `
954
+ return `${styleBlock}
955
+
956
+ ${styleFormatContract}
957
+
958
+ You are judging and classifying a round of review feedback${refactor ? " on a\nbehaviour-neutral refactor" : ""}.
959
+
960
+ ${stateFileRules}
961
+ ${refactor && refactor.round !== "reply" ? footnoteAnchor : `${footnoteFoldIn}${codeThreadReplies()}`}
962
+ ${
963
+ refactor
964
+ ? refactor.round === "reply"
965
+ ? `- This turn replies inside \`.gtd/REVIEW.md\` (thread replies only; the
966
+ file stays in the tree) — you classify, you do not build. Write
967
+ exactly one \`- A:\` reply to each thread whose last entry is a
968
+ \`- H:\` question, then run \`gtd check review .gtd/REVIEW.md\`
969
+ - Fold a concluded thread into \`${target}\` only when it asks for a
970
+ change, then delete it from \`.gtd/REVIEW.md\`; a concluded thread
971
+ that asks for none is just deleted
972
+ - Fold each one-shot footnote and note that asks for a change into
973
+ \`${target}\` and delete it in this same turn; praise is just deleted
974
+ `
975
+ : `- This turn writes \`${target}\` (the folded concerns) — you classify,
976
+ you do not build. \`.gtd/REVIEW.md\` is already closed: do not
977
+ recreate it or reply in it
978
+ ${closedFold(refactor.round, target)}`
979
+ : `- This turn writes \`${target}\` (the folded concerns) and
980
+ replies inside \`.gtd/REVIEW.md\` (thread replies only; the file stays
981
+ in the tree) — you classify, you do not build
982
+ - Fold every concluded thread, ticked answer and hand-edit — and each
983
+ \`edit\` note the capture lists, no other note — into
984
+ \`${target}\` and delete the folded threads from
985
+ \`.gtd/REVIEW.md\`; append one \`- A:\` reply to each thread
986
+ whose last entry is a \`- H:\` question
987
+ `
988
+ }- When the capture names an answering commit, carry each answered
989
+ question into \`${target}\`'s \`## Answered Questions\` as
990
+ \`### <the note>\` plus its answer — the lap replaces \`.gtd/REVIEW.md\`
991
+ - Keep capture order, and carry each note's \`(tier: reason)\`
992
+ label into the concern folded from it
993
+ - Nit notes were already fixed: never re-raise a fixed nit as a concern
994
+ ${fence}${refactor ? "" : "- Finish by running `gtd check review .gtd/REVIEW.md` and fix\n what it reports\n"}
995
+ The raw review material is:
996
+
997
+ ${capture}
998
+ It names a commit. Work from what you already reviewed if you
999
+ wrote today's review earlier this conversation; otherwise read
1000
+ that commit's diff yourself first.
1001
+
1002
+ ${actionable}`
1003
+ }
924
1004
 
925
1005
  const scenariosPrompt = (removed: readonly RemovedAssertions[]): string =>
926
1006
  `- When a scenario in a \`.feature\` file was added or changed since the
@@ -939,8 +1019,19 @@ ${removed.map(({ path, lines }) => lines.map((a) => ` ${path} — ${a.scenari
939
1019
  `
940
1020
  }`
941
1021
 
1022
+ /** Which review round the reviewer writes; the caller says, the prompt never infers it from `base`. */
1023
+ export type ReviewRange = "first" | "feedback" | "refactor"
1024
+
1025
+ const lapRange: Record<ReviewRange, string> = {
1026
+ first: "it covers everything since the base.",
1027
+ feedback:
1028
+ "it starts at the previous review's boundary, so it covers only what is new since the previous review.",
1029
+ refactor: "it covers the whole refactor since it started, on every round.",
1030
+ }
1031
+
942
1032
  export const buildReviewReviewingPrompt = (
943
1033
  base: string,
1034
+ lap: ReviewRange,
944
1035
  carry?: string,
945
1036
  removed: readonly RemovedAssertions[] = [],
946
1037
  ): string =>
@@ -988,8 +1079,7 @@ review the changes:
988
1079
  — that parses as a second pointer, not a note
989
1080
  ${scenariosPrompt(removed)}No diff is given — read the changes yourself. The range runs
990
1081
  from \`${base}\` to the working tree (committed turns
991
- plus anything pending); on a feedback round that's the previous
992
- review's boundary, so it covers only what's new.
1082
+ plus anything pending); ${lapRange[lap]}
993
1083
 
994
1084
  ${
995
1085
  carry === undefined
@@ -1153,19 +1243,35 @@ Start over and choose a different approach; do not rebuild the same diff.
1153
1243
  `
1154
1244
  }`
1155
1245
 
1156
- export const implementBuildingPrompt = (reference: string, tier?: "low" | "high"): string =>
1246
+ export const implementBuildingPrompt = (
1247
+ reference: string | undefined,
1248
+ tier?: "low" | "high",
1249
+ amend?: boolean,
1250
+ ): string =>
1157
1251
  `${stateFileRules}
1158
1252
  - Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
1159
- - Implement them properly, fitting the codebase's seams and conventions
1160
- - Reference only: \`git show ${reference}\` shows an earlier naive attempt.
1253
+ ${
1254
+ amend === true
1255
+ ? `- The change already exists in the tree from an earlier lap; amend it to meet
1256
+ the re-derived requirements instead of rewriting it
1257
+ `
1258
+ : `- Implement them properly, fitting the codebase's seams and conventions
1259
+ `
1260
+ }${
1261
+ reference === undefined || amend === true
1262
+ ? ""
1263
+ : `- Reference only: \`git show ${reference}\` shows an earlier naive attempt.
1161
1264
  Read it for ideas; never apply, cherry-pick or check it out — write
1162
1265
  the change fresh
1163
- ${
1164
- tier === "low"
1165
- ? "- Add no release flag — this change is low risk tier"
1166
- : `- Put the new behaviour behind a new release flag, following the
1266
+ `
1267
+ }${
1268
+ tier === "low"
1269
+ ? "- Add no release flag — this change is low risk tier"
1270
+ : amend === true
1271
+ ? "- Keep the release flag the earlier lap added, if it added one; otherwise put\n the behaviour behind a new release flag"
1272
+ : `- Put the new behaviour behind a new release flag, following the
1167
1273
  repository's flag conventions`
1168
- }
1274
+ }
1169
1275
  - Write the tests first, then the code that makes them pass
1170
1276
  - Leave \`.gtd/REQUIREMENTS.md\` in place and everything uncommitted, then
1171
1277
  finish your turn
@@ -1208,3 +1314,11 @@ ${listed(strays, unreadable)}
1208
1314
  See them with \`git show ${reference} -- <path>\` and review by hand. Land a
1209
1315
  clean tree to go on to the implementation, which runs next.
1210
1316
  `
1317
+
1318
+ export const refactorDroppedRefusal = (requested: Requested): string =>
1319
+ `gtd land: refactor-feedback: the refactor review asked for a change but .gtd/REFACTOR.md was not written, so nothing can be built from it:
1320
+ ${[
1321
+ ...requested.notes.map((n) => ` - ${n.id} (${n.anchor}): ${n.text}`),
1322
+ ...requested.paths.map((path) => ` - hand-edited ${path}`),
1323
+ ].join("\n")}
1324
+ The only way out is \`gtd abandon\`, which throws away the naive pass, the refactor and its review.`
@@ -4,6 +4,16 @@
4
4
  // is read live on every invocation. A factory option, `--var` (process
5
5
  // settings only) and `GTD_<NAME>` override either.
6
6
 
7
+ /** The refactor guard's fixed list, and `seamAllow`'s default minus `.gtd/**`. */
8
+ export const testFileGlobs: readonly string[] = [
9
+ "**/*.test.*",
10
+ "**/*.spec.*",
11
+ "**/*.feature",
12
+ "**/tests/**",
13
+ "**/test/**",
14
+ "**/__tests__/**",
15
+ ]
16
+
7
17
  export const defaultFlagRegistry = "src/flags/index.ts"
8
18
 
9
19
  /** Process settings. */
@@ -14,8 +24,7 @@ export const defaults: Readonly<Record<string, string>> = {
14
24
  reviewNoteActionable: "0.7",
15
25
  reviewSeverityMinP: "0.7",
16
26
  refactorFlagPattern: "\\bGTD_FLAG_\\w+",
17
- seamAllow:
18
- "**/*.test.*, **/*.spec.*, **/*.feature, **/tests/**, **/test/**, **/__tests__/**, .gtd/**",
27
+ seamAllow: [...testFileGlobs, ".gtd/**"].join(", "),
19
28
  // GTD_FLAG_NAIVE_PASS, recorded at process start; only "1" is on.
20
29
  flag_naive_pass: "0",
21
30
  // GTD_FLAG_RISK_TIER, recorded at process start; only "1" is on. Acts only with flag_naive_pass.
@@ -24,6 +33,7 @@ export const defaults: Readonly<Record<string, string>> = {
24
33
  // keyed in `./skills.ts`.
25
34
  qualityReviews:
26
35
  "correctness, owasp-security, ponytail-review, test-audit, conventions, spec-challenge",
36
+ refactorQualityReviews: "correctness, ponytail-review, conventions",
27
37
  }
28
38
 
29
39
  /** Environment settings. */