@pmelab/gtd 27.9.0 → 28.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 (37) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +50 -26
  3. package/claude/hooks/doors.ts +12 -0
  4. package/claude/hooks/register.tsx +2 -4
  5. package/claude/hooks/ship.ts +115 -20
  6. package/dist/chunks/{reserved-UdSuHDUJ.mjs → reserved-BIsngMGL.mjs} +168 -27
  7. package/dist/chunks/{tail-fj60rQ3y.mjs → tail-DHEaOHyp.mjs} +1 -1
  8. package/dist/gtd.bundle.mjs +43 -15
  9. package/dist/testkit.mjs +43 -15
  10. package/dist/workflows/cleanup.mjs +3 -4
  11. package/dist/workflows/feature.mjs +2 -2
  12. package/dist/workflows/fix.mjs +2 -2
  13. package/dist/workflows/review.mjs +2 -2
  14. package/dist/workflows/rollout.mjs +19 -4
  15. package/dist/workflows.mjs +186 -127
  16. package/package.json +1 -1
  17. package/skills/authoring/SKILL.md +4 -3
  18. package/src/cli/Cli.ts +4 -4
  19. package/src/install/ConfigScaffold.ts +4 -1
  20. package/src/install/Install.test.ts +10 -0
  21. package/src/install/Install.ts +5 -2
  22. package/src/workflow/load.test.ts +82 -5
  23. package/src/workflow/load.ts +40 -16
  24. package/src/workflows/cleanup/cleanup.feature +21 -0
  25. package/src/workflows/cleanup/cleanup.ts +1 -1
  26. package/src/workflows/index.ts +2 -1
  27. package/src/workflows/rollout/rollout.feature +282 -2
  28. package/src/workflows/rollout/rollout.test.ts +96 -2
  29. package/src/workflows/rollout/rollout.ts +41 -3
  30. package/src/workflows/shared/options.test.ts +25 -2
  31. package/src/workflows/shared/options.ts +7 -4
  32. package/src/workflows/shared/registry.test.ts +41 -1
  33. package/src/workflows/shared/registry.ts +26 -1
  34. package/src/workflows/shared/text.test.ts +252 -2
  35. package/src/workflows/shared/text.ts +44 -1
  36. package/src/workflows/shared/vars.ts +2 -0
  37. package/dist/chunks/registry-CP3YuV9j.mjs +0 -101
@@ -21520,8 +21520,11 @@ hands off to \`gtd-build\`, which halts at the blocked gate and prints it,
21520
21520
  rather than starting the review; \`gtd-door fix\` on a GREEN suite is a no-op
21521
21521
  straight back to \`idle\` — the exec'd \`gtd-build\` exits immediately on it.
21522
21522
  \`gtd doors\` lists the doors a repository offers (those of the workflows its
21523
- config exports — the bundled \`fix\` and \`review [base]\` — plus any it
21524
- declares). Refuses with
21523
+ config exports — the bundled \`fix\` and \`review [base]\`, plus the opt-in
21524
+ \`rollout <flag>\` (turn a release flag default-on) and \`cleanup <flag>\`
21525
+ (remove it), which a config adds by exporting \`rollout\` from
21526
+ \`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
21527
+ \`@pmelab/gtd/workflows/cleanup\` — plus any it declares). Refuses with
21525
21528
  \`usage: gtd-door <name> [args...]\` on stderr and exit \`2\` when the name is
21526
21529
  missing — nothing was even attempted. Default path: \`~/.local/bin/gtd-door\`.
21527
21530
 
@@ -21631,7 +21634,10 @@ const renderBriefing = () => HEADER() + BEAT_PROTOCOL + JSON_FIELD_REFERENCE + D
21631
21634
  const CONFIG_SCAFFOLD = `// The workflows gtd runs here: gtd's shipped feature, review and fix.
21632
21635
  // Every exported function is startable with \`gtd --workflow <name>\`; the
21633
21636
  // default export is what a change in the tree starts. Each workflow brings
21634
- // its own doors: \`gtd door review [base]\` and \`gtd door fix\`.
21637
+ // its own doors: \`gtd door review [base]\` and \`gtd door fix\`. Export \`rollout\`
21638
+ // from \`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
21639
+ // \`@pmelab/gtd/workflows/cleanup\` to add \`gtd door rollout <flag>\` and
21640
+ // \`gtd door cleanup <flag>\`.
21635
21641
  export {
21636
21642
  feature as default,
21637
21643
  feature,
@@ -38993,10 +38999,10 @@ const doorsFrom = (exports) => {
38993
38999
  };
38994
39000
  /**
38995
39001
  * Each exported workflow's own doors, onto the name it is exported under. The
38996
- * `doors` export adds to them and settles a name two workflows both declare.
39002
+ * `doors` export adds to them and declares a name several workflows clash on.
38997
39003
  */
38998
39004
  const derivedDoors = (catalogue, declared) => {
38999
- const doors = /* @__PURE__ */ new Map();
39005
+ const builders = /* @__PURE__ */ new Map();
39000
39006
  for (const [workflow, loaded] of catalogue) for (const [name, spec] of Object.entries(loaded.doors ?? {})) {
39001
39007
  const door = {
39002
39008
  ...spec,
@@ -39004,12 +39010,22 @@ const derivedDoors = (catalogue, declared) => {
39004
39010
  };
39005
39011
  const problem = doorProblem(name, door);
39006
39012
  if (problem !== void 0) throw new Error(`workflow "${workflow}" builds door "${name}", which ${problem}`);
39007
- const other = doors.get(name);
39008
- if (other !== void 0 && !declared.has(name)) throw new Error(`door "${name}" is built by workflows "${other.workflow}" and "${workflow}" — declare it in the "doors" export to choose one (a declared door replaces the derived one, so restate its args and vars)`);
39009
- doors.set(name, door);
39013
+ builders.set(name, [...builders.get(name) ?? [], door]);
39014
+ }
39015
+ const doors = /* @__PURE__ */ new Map();
39016
+ const clashes = /* @__PURE__ */ new Map();
39017
+ for (const [name, built] of builders) {
39018
+ const owner = ownerOf(name, built);
39019
+ if (owner !== void 0) doors.set(name, owner);
39020
+ else if (!declared.has(name)) clashes.set(name, built.map((door) => door.workflow));
39010
39021
  }
39011
- return new Map([...doors, ...declared]);
39022
+ return {
39023
+ doors: new Map([...doors, ...declared]),
39024
+ clashes
39025
+ };
39012
39026
  };
39027
+ /** The export named after the door owns it, else the sole builder; a clash of several builds no door. */
39028
+ const ownerOf = (name, built) => built.find((door) => door.workflow === name) ?? (built.length === 1 ? built[0] : void 0);
39013
39029
  /** Sort the doors and check each names a startable workflow. */
39014
39030
  const checkDoors = (declared, catalogue) => {
39015
39031
  const doors = new Map([...declared].sort(([a], [b]) => a.localeCompare(b)));
@@ -39040,16 +39056,28 @@ const loadWorkflows = (module, shipped) => {
39040
39056
  return workflow === void 0 ? [] : [[name, workflow]];
39041
39057
  }));
39042
39058
  const defaultWorkflow = built.get(value);
39059
+ const derived = rejections.length > 0 ? {
39060
+ doors: /* @__PURE__ */ new Map(),
39061
+ clashes: /* @__PURE__ */ new Map()
39062
+ } : derivedDoors(catalogue, doorsFrom(exports));
39043
39063
  return {
39044
39064
  file,
39045
39065
  catalogue,
39046
- doors: rejections.length > 0 ? /* @__PURE__ */ new Map() : checkDoors(derivedDoors(catalogue, doorsFrom(exports)), catalogue),
39066
+ doors: checkDoors(derived.doors, catalogue),
39047
39067
  default: defaultWorkflow && {
39048
39068
  ...defaultWorkflow,
39049
39069
  name: defaultName
39050
39070
  },
39051
39071
  config: compileConfig(exports["config"], module.filepath),
39052
- diagnostics: nameRejections(rejections)
39072
+ diagnostics: [...nameRejections(rejections), ...[...derived.clashes].map(([name, builders]) => {
39073
+ const names = [...builders].sort().map((b) => JSON.stringify(b));
39074
+ return {
39075
+ severity: "warning",
39076
+ message: `door "${name}" is built by workflows ${names.slice(0, -1).join(", ")} and ${names.at(-1)} and none is exported under that name — declare it in the "doors" export`,
39077
+ path: ["doors", name],
39078
+ origin: file.origin
39079
+ };
39080
+ })]
39053
39081
  };
39054
39082
  };
39055
39083
  const evaluateFromDisk = (module, shipped) => {
@@ -42186,10 +42214,10 @@ const COMMAND_ROWS = [
42186
42214
  "positional args to that workflow's process settings — the",
42187
42215
  "same start `gtd --workflow` makes, printed the same way.",
42188
42216
  "Each workflow the config exports brings its own doors (gtd's",
42189
- "`fix` brings `fix`, `review` brings `review [base]`), plus",
42190
- "the config's `doors` export. An unknown door, or too few or",
42191
- "too many args, is a usage error (exit 2) naming the doors or",
42192
- "the door's synopsis"
42217
+ "`fix`, `review [base]`, `rollout <flag>` and",
42218
+ "`cleanup <flag>`), plus the config's `doors` export. An",
42219
+ "unknown door, or too few or too many args, is a usage error",
42220
+ "(exit 2) naming the doors or the door's synopsis"
42193
42221
  ]
42194
42222
  },
42195
42223
  {
package/dist/testkit.mjs CHANGED
@@ -6647,8 +6647,11 @@ hands off to \`gtd-build\`, which halts at the blocked gate and prints it,
6647
6647
  rather than starting the review; \`gtd-door fix\` on a GREEN suite is a no-op
6648
6648
  straight back to \`idle\` — the exec'd \`gtd-build\` exits immediately on it.
6649
6649
  \`gtd doors\` lists the doors a repository offers (those of the workflows its
6650
- config exports — the bundled \`fix\` and \`review [base]\` — plus any it
6651
- declares). Refuses with
6650
+ config exports — the bundled \`fix\` and \`review [base]\`, plus the opt-in
6651
+ \`rollout <flag>\` (turn a release flag default-on) and \`cleanup <flag>\`
6652
+ (remove it), which a config adds by exporting \`rollout\` from
6653
+ \`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
6654
+ \`@pmelab/gtd/workflows/cleanup\` — plus any it declares). Refuses with
6652
6655
  \`usage: gtd-door <name> [args...]\` on stderr and exit \`2\` when the name is
6653
6656
  missing — nothing was even attempted. Default path: \`~/.local/bin/gtd-door\`.
6654
6657
 
@@ -6758,7 +6761,10 @@ const renderBriefing = () => HEADER() + BEAT_PROTOCOL + JSON_FIELD_REFERENCE + D
6758
6761
  const CONFIG_SCAFFOLD = `// The workflows gtd runs here: gtd's shipped feature, review and fix.
6759
6762
  // Every exported function is startable with \`gtd --workflow <name>\`; the
6760
6763
  // default export is what a change in the tree starts. Each workflow brings
6761
- // its own doors: \`gtd door review [base]\` and \`gtd door fix\`.
6764
+ // its own doors: \`gtd door review [base]\` and \`gtd door fix\`. Export \`rollout\`
6765
+ // from \`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
6766
+ // \`@pmelab/gtd/workflows/cleanup\` to add \`gtd door rollout <flag>\` and
6767
+ // \`gtd door cleanup <flag>\`.
6762
6768
  export {
6763
6769
  feature as default,
6764
6770
  feature,
@@ -8104,10 +8110,10 @@ const doorsFrom = (exports) => {
8104
8110
  };
8105
8111
  /**
8106
8112
  * Each exported workflow's own doors, onto the name it is exported under. The
8107
- * `doors` export adds to them and settles a name two workflows both declare.
8113
+ * `doors` export adds to them and declares a name several workflows clash on.
8108
8114
  */
8109
8115
  const derivedDoors = (catalogue, declared) => {
8110
- const doors = /* @__PURE__ */ new Map();
8116
+ const builders = /* @__PURE__ */ new Map();
8111
8117
  for (const [workflow, loaded] of catalogue) for (const [name, spec] of Object.entries(loaded.doors ?? {})) {
8112
8118
  const door = {
8113
8119
  ...spec,
@@ -8115,12 +8121,22 @@ const derivedDoors = (catalogue, declared) => {
8115
8121
  };
8116
8122
  const problem = doorProblem(name, door);
8117
8123
  if (problem !== void 0) throw new Error(`workflow "${workflow}" builds door "${name}", which ${problem}`);
8118
- const other = doors.get(name);
8119
- if (other !== void 0 && !declared.has(name)) throw new Error(`door "${name}" is built by workflows "${other.workflow}" and "${workflow}" — declare it in the "doors" export to choose one (a declared door replaces the derived one, so restate its args and vars)`);
8120
- doors.set(name, door);
8124
+ builders.set(name, [...builders.get(name) ?? [], door]);
8125
+ }
8126
+ const doors = /* @__PURE__ */ new Map();
8127
+ const clashes = /* @__PURE__ */ new Map();
8128
+ for (const [name, built] of builders) {
8129
+ const owner = ownerOf(name, built);
8130
+ if (owner !== void 0) doors.set(name, owner);
8131
+ else if (!declared.has(name)) clashes.set(name, built.map((door) => door.workflow));
8121
8132
  }
8122
- return new Map([...doors, ...declared]);
8133
+ return {
8134
+ doors: new Map([...doors, ...declared]),
8135
+ clashes
8136
+ };
8123
8137
  };
8138
+ /** The export named after the door owns it, else the sole builder; a clash of several builds no door. */
8139
+ const ownerOf = (name, built) => built.find((door) => door.workflow === name) ?? (built.length === 1 ? built[0] : void 0);
8124
8140
  /** Sort the doors and check each names a startable workflow. */
8125
8141
  const checkDoors = (declared, catalogue) => {
8126
8142
  const doors = new Map([...declared].sort(([a], [b]) => a.localeCompare(b)));
@@ -8151,16 +8167,28 @@ const loadWorkflows = (module, shipped) => {
8151
8167
  return workflow === void 0 ? [] : [[name, workflow]];
8152
8168
  }));
8153
8169
  const defaultWorkflow = built.get(value);
8170
+ const derived = rejections.length > 0 ? {
8171
+ doors: /* @__PURE__ */ new Map(),
8172
+ clashes: /* @__PURE__ */ new Map()
8173
+ } : derivedDoors(catalogue, doorsFrom(exports));
8154
8174
  return {
8155
8175
  file,
8156
8176
  catalogue,
8157
- doors: rejections.length > 0 ? /* @__PURE__ */ new Map() : checkDoors(derivedDoors(catalogue, doorsFrom(exports)), catalogue),
8177
+ doors: checkDoors(derived.doors, catalogue),
8158
8178
  default: defaultWorkflow && {
8159
8179
  ...defaultWorkflow,
8160
8180
  name: defaultName
8161
8181
  },
8162
8182
  config: compileConfig(exports["config"], module.filepath),
8163
- diagnostics: nameRejections(rejections)
8183
+ diagnostics: [...nameRejections(rejections), ...[...derived.clashes].map(([name, builders]) => {
8184
+ const names = [...builders].sort().map((b) => JSON.stringify(b));
8185
+ return {
8186
+ severity: "warning",
8187
+ message: `door "${name}" is built by workflows ${names.slice(0, -1).join(", ")} and ${names.at(-1)} and none is exported under that name — declare it in the "doors" export`,
8188
+ path: ["doors", name],
8189
+ origin: file.origin
8190
+ };
8191
+ })]
8164
8192
  };
8165
8193
  };
8166
8194
  const evaluateFromDisk = (module, shipped) => {
@@ -11284,10 +11312,10 @@ const COMMAND_ROWS = [
11284
11312
  "positional args to that workflow's process settings — the",
11285
11313
  "same start `gtd --workflow` makes, printed the same way.",
11286
11314
  "Each workflow the config exports brings its own doors (gtd's",
11287
- "`fix` brings `fix`, `review` brings `review [base]`), plus",
11288
- "the config's `doors` export. An unknown door, or too few or",
11289
- "too many args, is a usage error (exit 2) naming the doors or",
11290
- "the door's synopsis"
11315
+ "`fix`, `review [base]`, `rollout <flag>` and",
11316
+ "`cleanup <flag>`), plus the config's `doors` export. An",
11317
+ "unknown door, or too few or too many args, is a usage error",
11318
+ "(exit 2) naming the doors or the door's synopsis"
11291
11319
  ]
11292
11320
  },
11293
11321
  {
@@ -1,7 +1,6 @@
1
1
  import { agent, changes, glob, read, refuse, scope, start, vars } from "../flows.mjs";
2
- import { a as flagShipped, b as coder, c as envDefaults, i as skills, n as summary, r as access, s as defaults, t as steering } from "../chunks/reserved-UdSuHDUJ.mjs";
3
- import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-fj60rQ3y.mjs";
4
- import { i as requireFlag, r as registryEntry, t as flagReferences } from "../chunks/registry-CP3YuV9j.mjs";
2
+ import { a as flagShipped, ct as defaults, dt as flagReferences, i as skills, lt as envDefaults, mt as requireFlag, n as summary, pt as registryEntry, r as access, t as steering, v as coder } from "../chunks/reserved-BIsngMGL.mjs";
3
+ import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-DHEaOHyp.mjs";
5
4
  //#region src/workflows/cleanup/cleanup.ts
6
5
  const removingPrompt = (name, registry) => `Clean up the release flag "${name}", which is default-on: delete its entry in ${registry}, and remove every \`GTD_FLAG_${name.toUpperCase()}\` check from the shipped sources. Keep the flag-on path; delete code only the off path reached, including tests that pin the off path.`;
7
6
  const cleanupFlow = async () => {
@@ -29,7 +28,7 @@ const cleanupFlow = async () => {
29
28
  });
30
29
  await handOffTail(false, start(), guard);
31
30
  };
32
- const cleanup = flagShipped(cleanupFlow);
31
+ const cleanup = flagShipped(cleanupFlow, "cleanup");
33
32
  //#endregion
34
33
  //#region src/workflows/cleanup/index.ts
35
34
  var cleanup_default = cleanup;
@@ -1,7 +1,7 @@
1
1
  import { answered, changes, changesSince, glob, hasThreadFor, head, human, judge, judgeDiffShape, kept, mark, numeric, quote, read, refuse, removeScript, requireAnswers, requireProgress, requireReplies, requireRevert, requireThreadsClosed, restoreScript, revertScript, run, scope, seamGate, start, vars } from "../flows.mjs";
2
2
  import { review as review$1 } from "../workflow-kit.mjs";
3
- import { $ as refactorFixPrompt, A as architectureAuthorPrompt, B as designGateAnswerMessage, C as fixRisks, D as reviewing, F as buildReviewCollectingPrompt, H as designTriagePrompt, I as buildReviewFixNitsPrompt, J as implementFixPrompt, K as idleMessage, L as buildReviewTriageMessage, M as architectureGateAnswerMessage, N as buildReviewAnswerQuestionsPrompt, O as agentWithSkills, P as buildReviewAwaitAfterDeferMessage, Q as refactorBuildingPrompt, R as builderSystem, T as reviewMissing, V as designSystem, X as packagesItemBuildingPrompt, Y as naivePassPrompt, Z as packagesItemFixSuitePrompt, _ as REQUIREMENTS, at as seamByHandMessage, b as coder, c as envDefaults, ct as unwindFailedMessage, d as gate, et as reviewEditNotesCapture, h as FEEDBACK, i as skills, it as reviewerSystem, j as architectureDecomposePrompt, k as architectSystem, lt as unwindFailure, m as ARCHITECTURE, n as summary, o as shipped, ot as seamSteerMessage, p as healthy, q as implementBuildingPrompt, r as access, rt as reviewNotesCapture, s as defaults, st as startGateBlockedMessage, t as steering, tt as reviewEditsCapture, v as REVIEW, w as planner, x as fixCheck, y as awaitReview, z as deferNotesScript } from "../chunks/reserved-UdSuHDUJ.mjs";
4
- import { a as tail, i as refusePendingHandOff, n as closeReview } from "../chunks/tail-fj60rQ3y.mjs";
3
+ import { $ as reviewEditsCapture, A as architectureGateAnswerMessage, B as designTriagePrompt, C as reviewMissing, D as architectSystem, E as agentWithSkills, F as buildReviewTriageMessage, G as implementBuildingPrompt, I as builderSystem, J as packagesItemBuildingPrompt, K as implementFixPrompt, L as deferNotesScript, M as buildReviewAwaitAfterDeferMessage, N as buildReviewCollectingPrompt, O as architectureAuthorPrompt, P as buildReviewFixNitsPrompt, Q as reviewEditNotesCapture, R as designGateAnswerMessage, S as planner, T as reviewing, W as idleMessage, X as refactorBuildingPrompt, Y as packagesItemFixSuitePrompt, Z as refactorFixPrompt, _ as awaitReview, at as startGateBlockedMessage, ct as defaults, d as healthy, f as ARCHITECTURE, g as REVIEW, h as REQUIREMENTS, i as skills, it as seamSteerMessage, j as buildReviewAnswerQuestionsPrompt, k as architectureDecomposePrompt, l as gate, lt as envDefaults, n as summary, nt as reviewerSystem, o as shipped, ot as unwindFailedMessage, p as FEEDBACK, q as naivePassPrompt, r as access, rt as seamByHandMessage, st as unwindFailure, t as steering, tt as reviewNotesCapture, v as coder, x as fixRisks, y as fixCheck, z as designSystem } from "../chunks/reserved-BIsngMGL.mjs";
4
+ import { a as tail, i as refusePendingHandOff, n as closeReview } from "../chunks/tail-DHEaOHyp.mjs";
5
5
  //#region src/workflows/feature/tier.ts
6
6
  /** GTD_FLAG_NAIVE_PASS, GTD_FLAG_RISK_TIER: both pinned at process start, so a mid-process flip never moves a process. */
7
7
  const riskTierOn = () => vars.flag_naive_pass === "1" && vars.flag_risk_tier === "1";
@@ -1,7 +1,7 @@
1
1
  import { read, refuse, removeScript, run, scope, start } from "../flows.mjs";
2
2
  import { d as reviewNotes, f as reviewRisks } from "../chunks/steering-DVILanVf.mjs";
3
- import { O as agentWithSkills, U as finisherSystem, W as fixReviewNotesPrompt, b as coder, c as envDefaults, i as skills, l as baseline, n as summary, o as shipped, r as access, s as defaults, t as steering, v as REVIEW } from "../chunks/reserved-UdSuHDUJ.mjs";
4
- import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-fj60rQ3y.mjs";
3
+ import { E as agentWithSkills, H as fixReviewNotesPrompt, V as finisherSystem, ct as defaults, g as REVIEW, i as skills, lt as envDefaults, n as summary, o as shipped, r as access, s as baseline, t as steering, v as coder } from "../chunks/reserved-BIsngMGL.mjs";
4
+ import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-DHEaOHyp.mjs";
5
5
  //#region src/workflows/fix/fix.ts
6
6
  /**
7
7
  * Fix every note a human left in a handed-off `.gtd/REVIEW.md` and every
@@ -1,6 +1,6 @@
1
1
  import { start } from "../flows.mjs";
2
- import { c as envDefaults, d as gate, i as skills, n as summary, nt as reviewGateBlockedMessage, o as shipped, r as access, s as defaults, t as steering } from "../chunks/reserved-UdSuHDUJ.mjs";
3
- import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-fj60rQ3y.mjs";
2
+ import { ct as defaults, et as reviewGateBlockedMessage, i as skills, l as gate, lt as envDefaults, n as summary, o as shipped, r as access, t as steering } from "../chunks/reserved-BIsngMGL.mjs";
3
+ import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-DHEaOHyp.mjs";
4
4
  //#region src/workflows/review/review.ts
5
5
  /**
6
6
  * Pure review of everything since `reviewBase`: a green gate, the quality
@@ -1,9 +1,23 @@
1
- import { agent, changesSince, head, read, refuse, scope, vars } from "../flows.mjs";
2
- import { a as flagShipped, b as coder, c as envDefaults, d as gate, i as skills, n as summary, r as access, s as defaults, t as steering } from "../chunks/reserved-UdSuHDUJ.mjs";
3
- import { i as requireFlag, n as flipProblems } from "../chunks/registry-CP3YuV9j.mjs";
1
+ import { agent, changesSince, glob, head, mark, read, refuse, removeScript, run, scope, vars } from "../flows.mjs";
2
+ import { a as flagShipped, ct as defaults, ft as flipProblems, i as skills, l as gate, lt as envDefaults, mt as requireFlag, n as summary, p as FEEDBACK, r as access, t as steering, ut as flagMentions, v as coder } from "../chunks/reserved-BIsngMGL.mjs";
4
3
  //#region src/workflows/rollout/rollout.ts
5
4
  const flippingPrompt = (name, registry) => `Roll out the release flag "${name}": in ${registry}, set \`defaultOn: true\` on its entry. Change nothing else — no other entry, no other field, no other file, no comments.`;
6
5
  const suiteBlockedMessage = () => "The suite is red after the rollout. Fix it by hand — a rollout may only touch the flag's registry entry — then land.";
6
+ const MARKED_MENTIONS = 50;
7
+ const listMentions = async (registry, name, guard) => {
8
+ await run("mentions", removeScript([FEEDBACK]), { label: "Listing flag mentions" });
9
+ guard();
10
+ const mentions = flagMentions(glob("**").filter((path) => path !== registry && path !== ".gtd" && !path.startsWith(".gtd/")).sort().map((path) => ({
11
+ path,
12
+ text: read(path) ?? ""
13
+ })), name);
14
+ mark("flag-mentions", String(mentions.length));
15
+ for (const m of mentions.slice(0, MARKED_MENTIONS)) try {
16
+ mark("flag-mention", `${m.path}:${m.line}`);
17
+ } catch {
18
+ continue;
19
+ }
20
+ };
7
21
  const rolloutFlow = async () => {
8
22
  const registry = vars.flagRegistry ?? "";
9
23
  const name = vars.flag ?? "";
@@ -23,9 +37,10 @@ const rolloutFlow = async () => {
23
37
  });
24
38
  guard();
25
39
  await gate("suite", suiteBlockedMessage(), guard);
40
+ await listMentions(registry, name, guard);
26
41
  });
27
42
  };
28
- const rollout = flagShipped(rolloutFlow);
43
+ const rollout = flagShipped(rolloutFlow, "rollout");
29
44
  //#endregion
30
45
  //#region src/workflows/rollout/index.ts
31
46
  var rollout_default = rollout;