@codedrifters/configulator 0.0.361 → 0.0.362

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.js CHANGED
@@ -451,6 +451,7 @@ __export(index_exports, {
451
451
  renderSkillEvalsRuleContent: () => renderSkillEvalsRuleContent,
452
452
  renderSkillEvalsRunnerScript: () => renderSkillEvalsRunnerScript,
453
453
  renderSourceTierExamples: () => renderSourceTierExamples,
454
+ renderStripToolArtifactTagsProcedure: () => renderStripToolArtifactTagsProcedure,
454
455
  renderTemporalFramingCheckerScript: () => renderTemporalFramingCheckerScript,
455
456
  renderTemporalFramingRuleContent: () => renderTemporalFramingRuleContent,
456
457
  renderUnblockDependentsScript: () => renderUnblockDependentsScript,
@@ -487,6 +488,7 @@ __export(index_exports, {
487
488
  slackBundle: () => slackBundle,
488
489
  softwareProfileBundle: () => softwareProfileBundle,
489
490
  standardsResearchBundle: () => standardsResearchBundle,
491
+ stripToolArtifactTagsProcedure: () => stripToolArtifactTagsProcedure,
490
492
  tsdocRecordToFindings: () => tsdocRecordToFindings,
491
493
  turborepoBundle: () => turborepoBundle,
492
494
  typescriptBundle: () => typescriptBundle,
@@ -4264,7 +4266,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
4264
4266
  "- **After modifying Projen configuration**, run the three-step regen sequence: `pnpm i`, then `pnpm exec projen`, then `pnpm i` again. The leading `pnpm i` syncs `node_modules` with the lockfile so synth runs against the right configulator/projen/plugin versions; the trailing `pnpm i` refreshes the lockfile to match anything projen rewrote in `package.json`.",
4265
4267
  "- **Configure dependencies through Projen** \u2014 never use `npm install`, `pnpm add`, or `yarn add`. Add them to `deps` or `devDeps` in Projen config.",
4266
4268
  "- **Export from index.ts** to maintain clean public APIs",
4267
- '- **`defaultMode: "dontAsk"` is configulator\'s hardcoded default** for the rendered Claude Code `settings.json`. Scheduled-task workers (issue-worker, orchestrator, pr-reviewer, and the analyst/writer family) run autonomously and would deadlock on confirmation prompts, so the synthesised default suppresses them. Override only after revisiting the autonomous-worker contract end-to-end; the override path is `claudeSettings.defaultMode` on `AgentConfigOptions`.',
4269
+ '- **`defaultMode` is opt-in** for the rendered Claude Code `settings.json`. configulator does not set it by default, so the synthesised `settings.json` omits the key unless the consumer opts in. Consumers that run autonomous scheduled-task workers (issue-worker, orchestrator, pr-reviewer, and the analyst/writer family) \u2014 which would deadlock on confirmation prompts \u2014 opt in by setting `claudeSettings.defaultMode: "dontAsk"` on `AgentConfigOptions`.',
4268
4270
  "",
4269
4271
  "## Repository Layout",
4270
4272
  "",
@@ -9845,6 +9847,108 @@ var checkLinksProcedure = {
9845
9847
  description: "Link integrity checker that wraps `astro check` (internal links) and `lychee` (external URLs) and normalizes their output into a single JSON-array stream of { url, docPath, line, kind, reason } records. Detection is data: the helper exits 0 when a tool ran successfully regardless of how many broken links it reported. Non-zero exits are reserved for tool-level failures.",
9846
9848
  content: renderCheckLinksProcedure()
9847
9849
  };
9850
+ function renderStripToolArtifactTagsProcedure() {
9851
+ return [
9852
+ "#!/usr/bin/env bash",
9853
+ "# strip-tool-artifact-tags.sh \u2014 remove leaked tool-call wrapper",
9854
+ "# closing tags from the end of an authored markdown file.",
9855
+ "#",
9856
+ "# Usage:",
9857
+ "# .claude/procedures/strip-tool-artifact-tags.sh <file-path>",
9858
+ "#",
9859
+ "# Authoring agents occasionally leak tool-call wrapper *closing*",
9860
+ "# tags (</content>, </invoke>, </parameter>) as trailing whole",
9861
+ "# lines in the markdown they write. astro check and CI link checks",
9862
+ "# do not catch them. This helper strips those EOF artifact lines",
9863
+ "# (plus any now-trailing blank lines) and rewrites the file with a",
9864
+ "# single final newline.",
9865
+ "#",
9866
+ "# Guards (mirrors check-links.sh):",
9867
+ "# - Operates only on an existing file whose path is under",
9868
+ "# docs/src/content/docs/. Any other path, or a missing file,",
9869
+ "# is a silent no-op.",
9870
+ "# - Only WHOLE-LINE EOF tags are removed; inline `<...>` prose or",
9871
+ "# fenced code is never touched.",
9872
+ "# - Idempotent: a re-run on a clean file changes nothing.",
9873
+ "# - Never fails the tool call \u2014 always exits 0. Diagnostics go to",
9874
+ "# stderr only; the file is rewritten only when it changes.",
9875
+ "",
9876
+ "set -uo pipefail",
9877
+ "",
9878
+ "err() {",
9879
+ ' printf "strip-tool-artifact-tags.sh: %s\\n" "$*" >&2',
9880
+ "}",
9881
+ "",
9882
+ "# No file argument: nothing to do.",
9883
+ 'if [ "$#" -lt 1 ]; then',
9884
+ " exit 0",
9885
+ "fi",
9886
+ "",
9887
+ 'file="$1"',
9888
+ "",
9889
+ "# No-op unless the file actually exists and is a regular file.",
9890
+ 'if [ ! -f "$file" ]; then',
9891
+ " exit 0",
9892
+ "fi",
9893
+ "",
9894
+ "# Path guard: only touch markdown under the Starlight content tree.",
9895
+ "# Match the canonical content-root segment anywhere in the path so",
9896
+ "# the guard works for both absolute and repo-relative paths.",
9897
+ 'case "$file" in',
9898
+ " *docs/src/content/docs/*) ;;",
9899
+ " *) exit 0 ;;",
9900
+ "esac",
9901
+ "",
9902
+ "# Rewrite the file with trailing artifact tags and trailing blank",
9903
+ "# lines removed. awk buffers every line, then walks back from EOF",
9904
+ "# dropping lines that are blank or exactly one of the leaked",
9905
+ "# closing tags (trailing whitespace tolerated). Whatever survives",
9906
+ "# is re-emitted with a single final newline. Only whole lines are",
9907
+ "# considered, so inline `<...>` content is never altered.",
9908
+ 'tmp_out="$(mktemp -t strip-tool-artifact-tags-XXXXXX)" || exit 0',
9909
+ "# shellcheck disable=SC2064",
9910
+ `trap "rm -f '$tmp_out'" EXIT`,
9911
+ "",
9912
+ "awk '",
9913
+ " { lines[NR] = $0 }",
9914
+ " END {",
9915
+ " last = NR",
9916
+ " while (last > 0) {",
9917
+ " line = lines[last]",
9918
+ " # Strip trailing whitespace (spaces, tabs, CR) for the match.",
9919
+ " stripped = line",
9920
+ ' sub(/[ \\t\\r]+$/, "", stripped)',
9921
+ ' if (stripped == "" \\',
9922
+ ' || stripped == "</content>" \\',
9923
+ ' || stripped == "</invoke>" \\',
9924
+ ' || stripped == "</parameter>") {',
9925
+ " last--",
9926
+ " continue",
9927
+ " }",
9928
+ " break",
9929
+ " }",
9930
+ " for (i = 1; i <= last; i++) {",
9931
+ " print lines[i]",
9932
+ " }",
9933
+ " }",
9934
+ `' "$file" > "$tmp_out" || exit 0`,
9935
+ "",
9936
+ "# Only write back when the content actually changed \u2014 keeps the",
9937
+ "# helper a true no-op on clean files (idempotent re-runs included).",
9938
+ 'if ! cmp -s "$tmp_out" "$file"; then',
9939
+ ' if ! cat "$tmp_out" > "$file"; then',
9940
+ ' err "failed to rewrite $file"',
9941
+ " fi",
9942
+ "fi",
9943
+ "",
9944
+ "exit 0"
9945
+ ].join("\n");
9946
+ }
9947
+ var stripToolArtifactTagsProcedure = {
9948
+ name: "strip-tool-artifact-tags.sh",
9949
+ description: "Strips leaked tool-call wrapper closing tags (</content>, </invoke>, </parameter>) that authoring agents occasionally emit as trailing whole lines in markdown, plus any now-trailing blank lines, leaving a single final newline. Operates only on an existing file under docs/src/content/docs/; idempotent, a no-op on clean or missing files, and always exits 0 so it can never fail a PostToolUse hook.",
9950
+ content: renderStripToolArtifactTagsProcedure()
9951
+ };
9848
9952
  function renderCheckDocSamplesProcedure() {
9849
9953
  const nodeScript = [
9850
9954
  "(async () => {",
@@ -10017,7 +10121,8 @@ function buildDocsSyncBundle(paths = DEFAULT_AGENT_PATHS) {
10017
10121
  procedures: [
10018
10122
  extractApiProcedure,
10019
10123
  checkLinksProcedure,
10020
- checkDocSamplesProcedure
10124
+ checkDocSamplesProcedure,
10125
+ stripToolArtifactTagsProcedure
10021
10126
  ],
10022
10127
  labels: [
10023
10128
  {
@@ -12643,6 +12748,36 @@ function buildMeetingAnalystSubAgent(tier) {
12643
12748
  "**Goal:** Create GitHub issues for follow-up work, cross-reference the",
12644
12749
  "meeting into existing documentation, and complete bi-directional traceability.",
12645
12750
  "",
12751
+ "### Idempotent dedup gate (open AND closed)",
12752
+ "",
12753
+ "Phase 3 (`meeting:draft`) already files one downstream issue per",
12754
+ "drafted artifact \u2014 a `req:write` per requirement draft, plus",
12755
+ "`docs:write` / `bcm:*` / `research:scope` where applicable. Phase 4",
12756
+ "must **not** re-file what Phase 3 already filed. Before creating",
12757
+ "**any** downstream issue (`req:write`, `docs:write`, `bcm:*`,",
12758
+ "`research:scope`) for a drafted artifact, dedup against existing",
12759
+ "issues:",
12760
+ "",
12761
+ "1. **Search open AND closed issues** for one already covering the",
12762
+ " same artifact, matching on the draft's title / basename \u2014 e.g.",
12763
+ " `gh issue list --search '<draft title or basename>' --state all --json number,title,state`.",
12764
+ " Searching **all states** is load-bearing: the original may have",
12765
+ " already been worked and **merged (closed)**, and an open-only",
12766
+ " search misses it \u2014 re-filing then collides with the shipped",
12767
+ " document and produces an ID-collision PR.",
12768
+ "2. **If a match exists (open or closed), SKIP creation.** Link to",
12769
+ " the existing issue from the `## Downstream Artifacts` section",
12770
+ " instead of filing a new one.",
12771
+ "3. **Only create issues for follow-up work that has no Phase 3",
12772
+ " draft.** A drafted artifact already has its downstream issue;",
12773
+ " file new issues only for items the draft phase did not cover.",
12774
+ "4. **Be idempotent.** Re-running the same `meeting:link` issue must",
12775
+ " file nothing new \u2014 a second run finds the first run's issues",
12776
+ " (now open or closed) via the same search and skips them.",
12777
+ "",
12778
+ "Apply this gate to **both** Step 2 (requirement issues) and Step 4",
12779
+ "(action-item routing) below.",
12780
+ "",
12646
12781
  "### Steps",
12647
12782
  "",
12648
12783
  "1. Read the drafts from Phase 3 (if they exist) and the extraction",
@@ -12652,6 +12787,11 @@ function buildMeetingAnalystSubAgent(tier) {
12652
12787
  " are in scope for direct edits on this meeting. Apply the rules in",
12653
12788
  " the **Areas filtering** section above.",
12654
12789
  "2. Create requirement issues using `gh issue create` with appropriate labels.",
12790
+ " **First apply the Idempotent dedup gate above:** search open AND",
12791
+ " closed issues for one already covering the drafted requirement",
12792
+ " (match on its title / basename) and **skip creation when a match",
12793
+ " exists** \u2014 Phase 3 already filed a `req:write` issue per requirement",
12794
+ " draft, so file here only for requirements with no Phase 3 draft.",
12655
12795
  " Include a `## Traceability` section in each issue body linking back to",
12656
12796
  " the source meeting and extraction file. Issue creation is **not**",
12657
12797
  " gated by areas.",
@@ -12666,9 +12806,13 @@ function buildMeetingAnalystSubAgent(tier) {
12666
12806
  " \u2014 a `req:write` issue for a requirement, a `docs:write` issue",
12667
12807
  " for a docs page, `bcm:*` for a capability model, a",
12668
12808
  " `research:scope` issue for a research note, or a roadmap /",
12669
- " product-doc follow-up. Use the matching template from the",
12670
- " issue-templates page; include a `## Traceability` section.",
12671
- " Issue creation is not gated by areas.",
12809
+ " product-doc follow-up. **First apply the Idempotent dedup gate",
12810
+ " above:** search open AND closed issues for one already covering",
12811
+ " this artifact and **skip creation when a match exists** (link",
12812
+ " to it instead) \u2014 file only for action items that have no Phase",
12813
+ " 3 draft. Use the matching template from the issue-templates",
12814
+ " page; include a `## Traceability` section. Issue creation is",
12815
+ " not gated by areas.",
12672
12816
  " - **Human-owned** (send/schedule/install/decide/communicate/",
12673
12817
  " get-access/build-elsewhere): record it **only** in the notes",
12674
12818
  " `## Action Items` table (step 8 already carries the table",
@@ -14044,7 +14188,7 @@ var DEFAULT_BUNDLE_OVERRIDES = {
14044
14188
  acceptanceCriteria: { smallMax: 3, mediumMax: 14 }
14045
14189
  },
14046
14190
  "regulatory:research": {
14047
- acceptanceCriteria: { smallMax: 3, mediumMax: 10 }
14191
+ acceptanceCriteria: { smallMax: 3, mediumMax: 12 }
14048
14192
  },
14049
14193
  "standards:research": {
14050
14194
  acceptanceCriteria: { smallMax: 3, mediumMax: 10 }
@@ -14060,6 +14204,32 @@ var DEFAULT_BUNDLE_OVERRIDES = {
14060
14204
  "software:matrix": {
14061
14205
  acceptanceCriteria: { smallMax: 3, mediumMax: 12 },
14062
14206
  sources: { smallMax: 2, mediumMax: 15 }
14207
+ },
14208
+ "people:research": {
14209
+ acceptanceCriteria: { smallMax: 3, mediumMax: 12 }
14210
+ },
14211
+ "company:research": {
14212
+ acceptanceCriteria: { smallMax: 3, mediumMax: 12 }
14213
+ },
14214
+ "people:draft": {
14215
+ acceptanceCriteria: { smallMax: 3, mediumMax: 12 }
14216
+ },
14217
+ "company:draft": {
14218
+ acceptanceCriteria: { smallMax: 3, mediumMax: 12 }
14219
+ },
14220
+ "req:draft-trace": {
14221
+ acceptanceCriteria: { smallMax: 3, mediumMax: 20 }
14222
+ },
14223
+ "meeting:notes": {
14224
+ acceptanceCriteria: { smallMax: 3, mediumMax: 9 }
14225
+ },
14226
+ "meeting:draft": {
14227
+ acceptanceCriteria: { smallMax: 3, mediumMax: 15 },
14228
+ sources: { smallMax: 2, mediumMax: 10 }
14229
+ },
14230
+ "meeting:link": {
14231
+ acceptanceCriteria: { smallMax: 3, mediumMax: 15 },
14232
+ sources: { smallMax: 2, mediumMax: 10 }
14063
14233
  }
14064
14234
  };
14065
14235
  var DEFAULT_DECOMPOSITION_TEMPLATE = [
@@ -14323,17 +14493,30 @@ function renderScopeGateShellHelpers(gate) {
14323
14493
  " body=$(cat)",
14324
14494
  " local ac_count sources_count",
14325
14495
  ` ac_count=$(printf '%s\\n' "$body" | awk '`,
14326
- " BEGIN { in_ac=0; count=0 }",
14496
+ " # Count only TOP-LEVEL checkboxes \u2014 those at the shallowest",
14497
+ " # indentation in each Acceptance-Criteria section. A checkbox",
14498
+ " # with nested/indented sub-checkboxes (e.g. a `file N action-",
14499
+ " # item issues` criterion) counts once, mirroring the TS",
14500
+ " # countTopLevelCheckboxes() helper.",
14501
+ " function flush() {",
14502
+ " if (n > 0) { for (i = 0; i < n; i++) if (indents[i] == mini) total++ }",
14503
+ " n = 0; mini = -1",
14504
+ " }",
14505
+ " BEGIN { in_ac=0; total=0; n=0; mini=-1 }",
14327
14506
  " # Fully case-insensitive heading match mirrors the TypeScript",
14328
14507
  " # classifier (/^## acceptance criteria\\s*$/i); POSIX awk has",
14329
14508
  " # no /i flag, so we compare via tolower() instead of a",
14330
14509
  " # per-letter character class like [Aa]cceptance [Cc]riteria",
14331
14510
  " # which would drift for headings like `## ACCEPTANCE CRITERIA`.",
14332
14511
  " { lower = tolower($0) }",
14333
- " lower ~ /^## acceptance criteria[[:space:]]*$/ { in_ac=1; next }",
14334
- " /^## / { in_ac=0 }",
14335
- " in_ac && /^[[:space:]]*-[[:space:]]*\\[[ xX]\\]/ { count++ }",
14336
- " END { print count }",
14512
+ " lower ~ /^## acceptance criteria[[:space:]]*$/ { flush(); in_ac=1; next }",
14513
+ " /^## / { if (in_ac) { flush(); in_ac=0 } }",
14514
+ " in_ac && /^[[:space:]]*-[[:space:]]*\\[[ xX]\\]/ {",
14515
+ " match($0, /^[[:space:]]*/); ind = RLENGTH;",
14516
+ " indents[n++] = ind;",
14517
+ " if (mini < 0 || ind < mini) mini = ind;",
14518
+ " }",
14519
+ " END { flush(); print total }",
14337
14520
  " ')",
14338
14521
  ` sources_count=$(printf '%s\\n' "$body" | awk '`,
14339
14522
  " BEGIN { in_src=0; count=0 }",
@@ -14342,7 +14525,16 @@ function renderScopeGateShellHelpers(gate) {
14342
14525
  " { lower = tolower($0) }",
14343
14526
  " lower ~ /^## (inputs|references|sources)[[:space:]]*$/ { in_src=1; next }",
14344
14527
  " /^## / { in_src=0 }",
14345
- " in_src && /^[[:space:]]*[-*][[:space:]]+/ { count++ }",
14528
+ " # Exclude `Key: value` metadata bullets (e.g. `- Basename: \u2026`)",
14529
+ " # \u2014 a bullet whose text is a `Word:` key/value pair is",
14530
+ " # metadata, not a source. Mirrors the TS countSourceBullets()",
14531
+ " # METADATA_KEY_VALUE_REGEX exclusion.",
14532
+ " in_src && /^[[:space:]]*[-*][[:space:]]+/ {",
14533
+ " rest = $0;",
14534
+ ' sub(/^[[:space:]]*[-*][[:space:]]+/, "", rest);',
14535
+ " if (rest ~ /^[A-Za-z][A-Za-z0-9-]*:[[:space:]]+[^[:space:]]/) next;",
14536
+ " count++;",
14537
+ " }",
14346
14538
  " END { print count }",
14347
14539
  " ')",
14348
14540
  ` printf 'ac=%s sources=%s\\n' "$ac_count" "$sources_count" >&2`,
@@ -14497,37 +14689,80 @@ function resolveScopeClass(acCount, sourcesCount, thresholds) {
14497
14689
  }
14498
14690
  return "large";
14499
14691
  }
14692
+ var AC_CHECKBOX_REGEX = /^(\s*)-\s*\[[ xX]\]/;
14693
+ var SOURCES_BULLET_REGEX = /^\s*[-*]\s+(.*)$/;
14694
+ var METADATA_KEY_VALUE_REGEX = /^[A-Za-z][A-Za-z0-9-]*:\s+\S/;
14500
14695
  function countAcceptanceCriteria(body) {
14501
14696
  return countSectionMatches(
14502
14697
  body,
14503
14698
  /^## acceptance criteria\s*$/i,
14504
- /^\s*-\s*\[[ xX]\]/
14699
+ countTopLevelCheckboxes
14505
14700
  );
14506
14701
  }
14507
14702
  function countSources(body) {
14508
14703
  return countSectionMatches(
14509
14704
  body,
14510
14705
  /^## (?:inputs|references|sources)\s*$/i,
14511
- /^\s*[-*]\s+/
14706
+ countSourceBullets
14512
14707
  );
14513
14708
  }
14514
- function countSectionMatches(body, headingRegex, lineRegex) {
14709
+ function countTopLevelCheckboxes(sectionLines) {
14710
+ let minIndent = Infinity;
14711
+ const indents = [];
14712
+ for (const line of sectionLines) {
14713
+ const match = AC_CHECKBOX_REGEX.exec(line);
14714
+ if (match === null) {
14715
+ continue;
14716
+ }
14717
+ const indent = match[1].length;
14718
+ indents.push(indent);
14719
+ if (indent < minIndent) {
14720
+ minIndent = indent;
14721
+ }
14722
+ }
14723
+ return indents.filter((indent) => indent === minIndent).length;
14724
+ }
14725
+ function countSourceBullets(sectionLines) {
14726
+ let count = 0;
14727
+ for (const line of sectionLines) {
14728
+ const match = SOURCES_BULLET_REGEX.exec(line);
14729
+ if (match === null) {
14730
+ continue;
14731
+ }
14732
+ if (METADATA_KEY_VALUE_REGEX.test(match[1])) {
14733
+ continue;
14734
+ }
14735
+ count += 1;
14736
+ }
14737
+ return count;
14738
+ }
14739
+ function countSectionMatches(body, headingRegex, lineCounter) {
14515
14740
  const lines = body.split(/\r?\n/);
14516
14741
  let count = 0;
14517
14742
  let inSection = false;
14743
+ let sectionLines = [];
14744
+ const flush = () => {
14745
+ if (sectionLines.length > 0) {
14746
+ count += lineCounter(sectionLines);
14747
+ sectionLines = [];
14748
+ }
14749
+ };
14518
14750
  for (const line of lines) {
14519
14751
  if (headingRegex.test(line)) {
14752
+ flush();
14520
14753
  inSection = true;
14521
14754
  continue;
14522
14755
  }
14523
14756
  if (inSection && /^## /.test(line)) {
14757
+ flush();
14524
14758
  inSection = false;
14525
14759
  continue;
14526
14760
  }
14527
- if (inSection && lineRegex.test(line)) {
14528
- count += 1;
14761
+ if (inSection) {
14762
+ sectionLines.push(line);
14529
14763
  }
14530
14764
  }
14765
+ flush();
14531
14766
  return count;
14532
14767
  }
14533
14768
 
@@ -32716,7 +32951,7 @@ var DEFAULT_CLAUDE_HOOKS = {
32716
32951
  hooks: [
32717
32952
  {
32718
32953
  type: "command",
32719
- command: 'case "${CLAUDE_TOOL_INPUT_path:-}" in *docs/src/content/docs/*) if [ -x .claude/procedures/check-links.sh ]; then bash .claude/procedures/check-links.sh "$CLAUDE_TOOL_INPUT_path" 2>&1 | head -20; fi ;; esac'
32954
+ command: 'case "${CLAUDE_TOOL_INPUT_path:-}" in *docs/src/content/docs/*) if [ -x .claude/procedures/strip-tool-artifact-tags.sh ]; then bash .claude/procedures/strip-tool-artifact-tags.sh "$CLAUDE_TOOL_INPUT_path" >/dev/null 2>&1; fi; if [ -x .claude/procedures/check-links.sh ]; then bash .claude/procedures/check-links.sh "$CLAUDE_TOOL_INPUT_path" 2>&1 | head -20; fi ;; esac'
32720
32955
  }
32721
32956
  ]
32722
32957
  }
@@ -32805,8 +33040,10 @@ var AgentConfig = class _AgentConfig extends import_projen8.Component {
32805
33040
  * `Array.from(new Set(...))`; V8 `Set` iteration preserves insertion
32806
33041
  * order, so the final ordering is defaults first, then bundle, then
32807
33042
  * user, with duplicates removed (first-occurrence wins). `defaultMode`
32808
- * defaults to `"dontAsk"` unless overridden see the inline comment
32809
- * on the literal below for the autonomous-worker rationale.
33043
+ * is opt-in (steering decision D7): it is set only from
33044
+ * `userSettings.defaultMode` and left undefined otherwise, so the
33045
+ * renderer omits the key for un-opted-in consumers — see the inline
33046
+ * comment on the literal below.
32810
33047
  *
32811
33048
  * Hooks merge: consumer-supplied entries first, then default entries
32812
33049
  * (Stop, PostToolUse), deduped by `(matcher, JSON-serialized hooks)`.
@@ -32829,15 +33066,16 @@ var AgentConfig = class _AgentConfig extends import_projen8.Component {
32829
33066
  const userDeny = userSettings?.permissions?.deny ?? [];
32830
33067
  return {
32831
33068
  ...userSettings,
32832
- // `defaultMode: "dontAsk"` is configulator's hardcoded default
32833
- // because scheduled-task workers (issue-worker, orchestrator,
32834
- // pr-reviewer, and the analyst/writer family) run autonomously
32835
- // and would deadlock on confirmation prompts. Any other value
32836
- // breaks the autonomous-worker contract override only after
32837
- // revisiting that contract end-to-end. The override path for
32838
- // consumers is `claudeSettings.defaultMode` on
32839
- // `AgentConfigOptions`.
32840
- defaultMode: userSettings?.defaultMode ?? "dontAsk",
33069
+ // `defaultMode` is opt-in (steering decision D7): the base
33070
+ // output does NOT set it unless the consumer supplies
33071
+ // `claudeSettings.defaultMode`. When it is undefined the renderer
33072
+ // omits the key entirely, so an un-opted-in consumer's rendered
33073
+ // settings carry no `defaultMode`. Consumers that run autonomous
33074
+ // scheduled-task workers (issue-worker, orchestrator, pr-reviewer,
33075
+ // and the analyst/writer family) — which would deadlock on
33076
+ // confirmation prompts — opt in by setting
33077
+ // `claudeSettings.defaultMode: "dontAsk"`.
33078
+ defaultMode: userSettings?.defaultMode,
32841
33079
  permissions: {
32842
33080
  ...userSettings?.permissions,
32843
33081
  allow: Array.from(
@@ -39758,6 +39996,7 @@ export const collections = {
39758
39996
  renderSkillEvalsRuleContent,
39759
39997
  renderSkillEvalsRunnerScript,
39760
39998
  renderSourceTierExamples,
39999
+ renderStripToolArtifactTagsProcedure,
39761
40000
  renderTemporalFramingCheckerScript,
39762
40001
  renderTemporalFramingRuleContent,
39763
40002
  renderUnblockDependentsScript,
@@ -39794,6 +40033,7 @@ export const collections = {
39794
40033
  slackBundle,
39795
40034
  softwareProfileBundle,
39796
40035
  standardsResearchBundle,
40036
+ stripToolArtifactTagsProcedure,
39797
40037
  tsdocRecordToFindings,
39798
40038
  turborepoBundle,
39799
40039
  typescriptBundle,