@codedrifters/configulator 0.0.405 → 0.0.406

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
@@ -228,6 +228,7 @@ __export(index_exports, {
228
228
  DEFAULT_DECOMPOSITION_TEMPLATE: () => DEFAULT_DECOMPOSITION_TEMPLATE,
229
229
  DEFAULT_DISPATCH_MODEL: () => DEFAULT_DISPATCH_MODEL,
230
230
  DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO: () => DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO,
231
+ DEFAULT_GITHUB_ISSUE_TYPE: () => DEFAULT_GITHUB_ISSUE_TYPE,
231
232
  DEFAULT_HOUSEKEEPING_MODEL: () => DEFAULT_HOUSEKEEPING_MODEL,
232
233
  DEFAULT_ISSUE_PRIORITY: () => DEFAULT_ISSUE_PRIORITY,
233
234
  DEFAULT_ISSUE_STATUS: () => DEFAULT_ISSUE_STATUS,
@@ -274,6 +275,8 @@ __export(index_exports, {
274
275
  DEFAULT_UNBLOCK_DEPENDENTS_ENABLED: () => DEFAULT_UNBLOCK_DEPENDENTS_ENABLED,
275
276
  DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED: () => DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED,
276
277
  DOCS_SYNC_AUDIT_SCHEMA_VERSION: () => DOCS_SYNC_AUDIT_SCHEMA_VERSION,
278
+ GITHUB_ISSUE_TYPES: () => GITHUB_ISSUE_TYPES,
279
+ GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX: () => GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX,
277
280
  JsiiFaker: () => JsiiFaker,
278
281
  LAYOUT_ENFORCEMENT: () => LAYOUT_ENFORCEMENT,
279
282
  LAYOUT_ROOT_BY_PROJECT_TYPE: () => LAYOUT_ROOT_BY_PROJECT_TYPE,
@@ -298,6 +301,7 @@ __export(index_exports, {
298
301
  ResetTask: () => ResetTask,
299
302
  SCHEDULED_TASK_MODEL_VALUES: () => SCHEDULED_TASK_MODEL_VALUES,
300
303
  SCOPE_CLASS_VALUES: () => SCOPE_CLASS_VALUES,
304
+ SET_ISSUE_TYPE_HELPER_PATH: () => SET_ISSUE_TYPE_HELPER_PATH,
301
305
  SHARED_EDITING_CONFLICT_STRATEGY_VALUES: () => SHARED_EDITING_CONFLICT_STRATEGY_VALUES,
302
306
  STARLIGHT_ROLE: () => STARLIGHT_ROLE,
303
307
  SUPPRESSED_WORKFLOW_RULE_NAMES: () => SUPPRESSED_WORKFLOW_RULE_NAMES,
@@ -383,6 +387,7 @@ __export(index_exports, {
383
387
  formatLayoutViolation: () => formatLayoutViolation,
384
388
  formatStarlightSingletonViolation: () => formatStarlightSingletonViolation,
385
389
  getLatestEligibleVersion: () => getLatestEligibleVersion,
390
+ githubIssueTypeForTitle: () => githubIssueTypeForTitle,
386
391
  githubWorkflowBundle: () => githubWorkflowBundle,
387
392
  hasAnyDocsEmittingBundle: () => hasAnyDocsEmittingBundle,
388
393
  hasAnyDownstreamIssueKindBundle: () => hasAnyDownstreamIssueKindBundle,
@@ -440,10 +445,14 @@ __export(index_exports, {
440
445
  renderCustomDocSections: () => renderCustomDocSections,
441
446
  renderExtractApiProcedure: () => renderExtractApiProcedure,
442
447
  renderFocusSection: () => renderFocusSection,
448
+ renderGithubIssueTypeSection: () => renderGithubIssueTypeSection,
449
+ renderGithubIssueTypeSectionLines: () => renderGithubIssueTypeSectionLines,
443
450
  renderIssueTemplatesBundleHook: () => renderIssueTemplatesBundleHook,
444
451
  renderIssueTemplatesCheckerScript: () => renderIssueTemplatesCheckerScript,
445
452
  renderIssueTemplatesRuleContent: () => renderIssueTemplatesRuleContent,
446
453
  renderIssueTemplatesStarterPage: () => renderIssueTemplatesStarterPage,
454
+ renderIssueTypeAssignmentBlanket: () => renderIssueTypeAssignmentBlanket,
455
+ renderIssueTypeAssignmentStep: () => renderIssueTypeAssignmentStep,
447
456
  renderMeetingTypesSection: () => renderMeetingTypesSection,
448
457
  renderNextRequirementIdProcedure: () => renderNextRequirementIdProcedure,
449
458
  renderPhaseTypeInvariantSection: () => renderPhaseTypeInvariantSection,
@@ -460,6 +469,7 @@ __export(index_exports, {
460
469
  renderScheduledTasksSection: () => renderScheduledTasksSection,
461
470
  renderScopeGateSection: () => renderScopeGateSection,
462
471
  renderScopeGateShellHelpers: () => renderScopeGateShellHelpers,
472
+ renderSetIssueTypeFallbackLines: () => renderSetIssueTypeFallbackLines,
463
473
  renderSharedEditingBundleHook: () => renderSharedEditingBundleHook,
464
474
  renderSharedEditingHelperScript: () => renderSharedEditingHelperScript,
465
475
  renderSharedEditingRuleContent: () => renderSharedEditingRuleContent,
@@ -470,6 +480,7 @@ __export(index_exports, {
470
480
  renderStripToolArtifactTagsProcedure: () => renderStripToolArtifactTagsProcedure,
471
481
  renderTemporalFramingCheckerScript: () => renderTemporalFramingCheckerScript,
472
482
  renderTemporalFramingRuleContent: () => renderTemporalFramingRuleContent,
483
+ renderTitlePrefixTypeBullets: () => renderTitlePrefixTypeBullets,
473
484
  renderUnblockDependentsScript: () => renderUnblockDependentsScript,
474
485
  renderUnblockDependentsSection: () => renderUnblockDependentsSection,
475
486
  requirementsAnalystBundle: () => requirementsAnalystBundle,
@@ -638,6 +649,603 @@ function renderClaimVerificationSection() {
638
649
  ];
639
650
  }
640
651
 
652
+ // src/agent/bundles/bundle-ownership.ts
653
+ var BUNDLE_OWNERSHIP = {
654
+ agenda: {
655
+ typeLabels: ["agenda"],
656
+ phaseLabelPrefixes: ["agenda:"],
657
+ scheduledTaskIds: ["worker-agenda"],
658
+ emitsDocs: true,
659
+ downstreamIssueKinds: true
660
+ },
661
+ "bcm-writer": {
662
+ typeLabels: ["bcm-document"],
663
+ phaseLabelPrefixes: ["bcm:"],
664
+ scheduledTaskIds: ["worker-bcm-writer"],
665
+ emitsDocs: true,
666
+ downstreamIssueKinds: true
667
+ },
668
+ "business-models": {
669
+ typeLabels: ["business-model"],
670
+ phaseLabelPrefixes: ["business-models:"],
671
+ scheduledTaskIds: ["worker-business-models"],
672
+ emitsDocs: true,
673
+ downstreamIssueKinds: true
674
+ },
675
+ "company-profile": {
676
+ typeLabels: ["company-profile"],
677
+ phaseLabelPrefixes: ["company:"],
678
+ scheduledTaskIds: ["worker-company-profile"],
679
+ emitsDocs: true,
680
+ downstreamIssueKinds: true
681
+ },
682
+ "customer-profile": {
683
+ typeLabels: ["customer-profile"],
684
+ phaseLabelPrefixes: ["customer:"],
685
+ scheduledTaskIds: ["worker-customer-profile"],
686
+ emitsDocs: true,
687
+ downstreamIssueKinds: true
688
+ },
689
+ "docs-sync": {
690
+ typeLabels: ["docs-sync"],
691
+ phaseLabelPrefixes: ["docs-sync:"],
692
+ scheduledTaskIds: ["worker-docs-sync"],
693
+ emitsDocs: true,
694
+ downstreamIssueKinds: true
695
+ },
696
+ "industry-discovery": {
697
+ typeLabels: ["industry-discovery"],
698
+ phaseLabelPrefixes: ["industry:"],
699
+ scheduledTaskIds: ["worker-industry-discovery"],
700
+ emitsDocs: true,
701
+ downstreamIssueKinds: true
702
+ },
703
+ "maintenance-audit": {
704
+ typeLabels: ["maintenance"],
705
+ phaseLabelPrefixes: ["maint:"],
706
+ scheduledTaskIds: ["worker-maintenance"],
707
+ emitsDocs: false,
708
+ downstreamIssueKinds: true
709
+ },
710
+ "meeting-analysis": {
711
+ typeLabels: ["meeting-processing"],
712
+ phaseLabelPrefixes: ["meeting:"],
713
+ scheduledTaskIds: ["worker-meeting-analysis"],
714
+ emitsDocs: true,
715
+ downstreamIssueKinds: true
716
+ },
717
+ orchestrator: {
718
+ // The orchestrator pipeline manager itself ships with the
719
+ // `worker-orchestrator` scheduled task. It owns no `type:*` label
720
+ // (it dispatches every type) and no phase-label prefix.
721
+ typeLabels: [],
722
+ phaseLabelPrefixes: [],
723
+ scheduledTaskIds: ["worker-orchestrator"],
724
+ emitsDocs: false,
725
+ downstreamIssueKinds: false
726
+ },
727
+ "people-profile": {
728
+ typeLabels: ["people-profile"],
729
+ phaseLabelPrefixes: ["people:"],
730
+ scheduledTaskIds: ["worker-people-profile"],
731
+ emitsDocs: true,
732
+ downstreamIssueKinds: true
733
+ },
734
+ "pr-review": {
735
+ typeLabels: ["pr-review"],
736
+ phaseLabelPrefixes: [],
737
+ scheduledTaskIds: ["worker-pr-review"],
738
+ emitsDocs: false,
739
+ downstreamIssueKinds: false
740
+ },
741
+ "regulatory-research": {
742
+ typeLabels: ["regulatory-research"],
743
+ phaseLabelPrefixes: ["regulatory:"],
744
+ scheduledTaskIds: ["worker-regulatory-research"],
745
+ emitsDocs: true,
746
+ downstreamIssueKinds: true
747
+ },
748
+ "requirements-analyst": {
749
+ typeLabels: ["requirement"],
750
+ phaseLabelPrefixes: ["req:"],
751
+ scheduledTaskIds: ["worker-requirements-analyst"],
752
+ emitsDocs: true,
753
+ downstreamIssueKinds: true
754
+ },
755
+ "requirements-reviewer": {
756
+ // Co-owns `type:requirement` with the analyst and writer; owns
757
+ // the `req:review` and `req:deprecate` phase labels exactly.
758
+ typeLabels: ["requirement"],
759
+ phaseLabelPrefixes: ["req:review", "req:deprecate"],
760
+ scheduledTaskIds: ["worker-requirements-reviewer"],
761
+ emitsDocs: true,
762
+ downstreamIssueKinds: true
763
+ },
764
+ "requirements-writer": {
765
+ // Co-owns `type:requirement` with the analyst and reviewer; owns
766
+ // the `req:write` phase label exactly.
767
+ typeLabels: ["requirement"],
768
+ phaseLabelPrefixes: ["req:write"],
769
+ scheduledTaskIds: ["worker-requirements-writer"],
770
+ emitsDocs: true,
771
+ downstreamIssueKinds: true
772
+ },
773
+ "research-pipeline": {
774
+ typeLabels: ["research"],
775
+ phaseLabelPrefixes: ["research:"],
776
+ scheduledTaskIds: ["worker-research"],
777
+ emitsDocs: true,
778
+ downstreamIssueKinds: true
779
+ },
780
+ "software-profile": {
781
+ typeLabels: ["software-profile"],
782
+ phaseLabelPrefixes: ["software:"],
783
+ scheduledTaskIds: ["worker-software-profile"],
784
+ emitsDocs: true,
785
+ downstreamIssueKinds: true
786
+ },
787
+ "standards-research": {
788
+ typeLabels: ["standards-research"],
789
+ phaseLabelPrefixes: ["standards:"],
790
+ scheduledTaskIds: ["worker-standards-research"],
791
+ emitsDocs: true,
792
+ downstreamIssueKinds: true
793
+ }
794
+ };
795
+ var CONVENTIONAL_COMMIT_TYPE_LABELS = [
796
+ "type:chore",
797
+ "type:docs",
798
+ "type:feat",
799
+ "type:fix",
800
+ "type:hotfix",
801
+ "type:refactor",
802
+ "type:release"
803
+ ];
804
+ var PHASE_LABEL_TYPE_MAP = buildPhaseLabelTypeMap();
805
+ function typeLabelForPhaseLabel(phaseLabel) {
806
+ const exact = PHASE_LABEL_TYPE_MAP[phaseLabel];
807
+ if (exact !== void 0 && !phaseLabel.endsWith(":")) {
808
+ return exact;
809
+ }
810
+ let bestMatcher;
811
+ for (const matcher of Object.keys(PHASE_LABEL_TYPE_MAP)) {
812
+ if (!matcher.endsWith(":")) {
813
+ continue;
814
+ }
815
+ if (!phaseLabel.startsWith(matcher)) {
816
+ continue;
817
+ }
818
+ if (bestMatcher === void 0 || matcher.length > bestMatcher.length) {
819
+ bestMatcher = matcher;
820
+ }
821
+ }
822
+ return bestMatcher === void 0 ? void 0 : PHASE_LABEL_TYPE_MAP[bestMatcher];
823
+ }
824
+ function resolveTypeLabelForLabels(labels) {
825
+ const phaseLabels = [];
826
+ const candidates = /* @__PURE__ */ new Set();
827
+ for (const label of labels) {
828
+ const typeLabel = typeLabelForPhaseLabel(label);
829
+ if (typeLabel === void 0) {
830
+ continue;
831
+ }
832
+ phaseLabels.push(label);
833
+ candidates.add(typeLabel);
834
+ }
835
+ const candidateTypeLabels = Array.from(candidates).sort();
836
+ if (candidateTypeLabels.length === 0) {
837
+ return { outcome: "none", candidateTypeLabels: [], phaseLabels: [] };
838
+ }
839
+ if (candidateTypeLabels.length === 1) {
840
+ return {
841
+ outcome: "match",
842
+ typeLabel: candidateTypeLabels[0],
843
+ candidateTypeLabels,
844
+ phaseLabels
845
+ };
846
+ }
847
+ return { outcome: "ambiguous", candidateTypeLabels, phaseLabels };
848
+ }
849
+ function renderPhaseTypeInvariantSection(excludeBundles = []) {
850
+ const rows = Object.keys(PHASE_LABEL_TYPE_MAP).filter(
851
+ (matcher) => !isPhaseLabelMatcherOwnedByExcluded(matcher, excludeBundles)
852
+ ).sort().map((matcher) => {
853
+ const display = matcher.endsWith(":") ? `${matcher}*` : matcher;
854
+ const kind = matcher.endsWith(":") ? "prefix" : "exact";
855
+ return `| \`${display}\` | ${kind} | \`${PHASE_LABEL_TYPE_MAP[matcher]}\` |`;
856
+ });
857
+ return [
858
+ "## Phase-label \u2192 `type:<bundle>` invariant",
859
+ "",
860
+ "Every phased-pipeline bundle pairs its `<bundle>:<phase>` labels",
861
+ "with exactly one `type:<bundle>` label. That type label is the",
862
+ "**dedup + dispatch signal**: agents de-duplicate downstream work",
863
+ 'with `gh issue list --label "type:<bundle>"`, and Phase E derives',
864
+ "its funnel-tier sort key from the issue's `type:*` label. An issue",
865
+ "that carries the phase label but a conventional-commit `type:*`",
866
+ "(`type:feat`, `type:docs`, \u2026, stamped from its title prefix by the",
867
+ "generic create-issue workflow) is invisible to that dedup query and",
868
+ "mis-tiers in dispatch.",
869
+ "",
870
+ "The pairing below is derived from the canonical bundle-ownership",
871
+ "map \u2014 the same source of truth that generates the label registry.",
872
+ "Matchers ending in `*` match by prefix; the rest match exactly, and",
873
+ "an exact match always beats a prefix match.",
874
+ "",
875
+ "| Phase label | Match | Required type label |",
876
+ "|-------------|-------|---------------------|",
877
+ ...rows,
878
+ "",
879
+ "### Enforcement",
880
+ "",
881
+ "The Phase D maintenance sweep runs the invariant in auto-correct",
882
+ "mode and emits one summary line:",
883
+ "",
884
+ "```",
885
+ "LABEL_INVARIANT_DONE mode=fix checked=<N> ok=<O> violations=<V> corrected=<C> flagged=<F>",
886
+ "```",
887
+ "",
888
+ "A **report-only** audit is available for repos that prefer",
889
+ "report-then-fix over auto-correct \u2014 same sweep, no mutations:",
890
+ "",
891
+ "```bash",
892
+ ".claude/procedures/check-blocked.sh label-invariant # report only",
893
+ ".claude/procedures/check-blocked.sh label-invariant --fix # apply",
894
+ "```",
895
+ "",
896
+ "### Correction policy",
897
+ "",
898
+ "An issue must carry **exactly one** `type:*` label. Merely *adding*",
899
+ "the required type label to an issue that already carries a",
900
+ "conventional-commit one would leave two, making the funnel-tier sort",
901
+ "key (which reads the **first** `type:*` label) non-deterministic and",
902
+ "violating the label conventions. The correction therefore",
903
+ "**replaces**, in a single atomic `gh issue edit`, so the issue is",
904
+ "never observable carrying two `type:*` labels:",
905
+ "",
906
+ "| Issue state | Action |",
907
+ "|-------------|--------|",
908
+ "| Required type label present, nothing else | none \u2014 compliant, left untouched |",
909
+ "| No `type:*` label at all | **add** the required label |",
910
+ "| Only conventional-commit `type:*` label(s) | **replace** \u2014 remove them, add the required label |",
911
+ "| Required label present **plus** a stray conventional-commit one | **remove** the stray, leaving exactly one |",
912
+ "| A `type:*` label owned by a **different** bundle | **flag** `status:needs-attention` \u2014 never guessed at |",
913
+ "| Phase labels imply **two or more** type labels | **flag** `status:needs-attention` \u2014 never auto-corrected |",
914
+ "",
915
+ "Only a conventional-commit `type:*` label is ever removed. A",
916
+ "`type:*` label owned by another bundle carries real routing",
917
+ "information, so the sweep refuses to guess and hands the issue to a",
918
+ "human instead. Ambiguity is narrower than it looks: all three",
919
+ "requirements bundles declare `type:requirement`, so every `req:*`",
920
+ "phase label resolves to the same type label \u2014 genuine ambiguity",
921
+ "needs phase labels from two bundles with *different* type labels.",
922
+ "",
923
+ "`status:needs-attention` is applied **additively** \u2014 the base",
924
+ "`status:*` label always stays, per the additive-flag rule. Flagging",
925
+ "is idempotent: an issue already carrying the flag is reported but",
926
+ "not re-flagged."
927
+ ].join("\n");
928
+ }
929
+ function isPhaseLabelMatcherOwnedByExcluded(matcher, excludeBundles) {
930
+ if (excludeBundles.length === 0) {
931
+ return false;
932
+ }
933
+ let owned = false;
934
+ for (const [bundleName, ownership] of Object.entries(BUNDLE_OWNERSHIP)) {
935
+ if (!ownership.phaseLabelPrefixes.includes(matcher)) {
936
+ continue;
937
+ }
938
+ owned = true;
939
+ if (!excludeBundles.includes(bundleName)) {
940
+ return false;
941
+ }
942
+ }
943
+ return owned;
944
+ }
945
+ function renderPhaseTypeInvariantShellHelpers() {
946
+ const matchers = Object.keys(PHASE_LABEL_TYPE_MAP);
947
+ const exactMatchers = matchers.filter((m) => !m.endsWith(":")).sort();
948
+ const prefixMatchers = matchers.filter((m) => m.endsWith(":")).sort((a, b) => b.length - a.length || a.localeCompare(b));
949
+ const lines = [
950
+ "# Resolve ONE phase label to the `type:<bundle>` label its owning",
951
+ "# bundle declares. Echoes the label (with the `type:` prefix) or",
952
+ "# nothing when no bundle owns it. Exact-match branches come first,",
953
+ "# so `req:write` (requirements-writer) beats the `req:` prefix",
954
+ "# (requirements-analyst). Generated from BUNDLE_OWNERSHIP \u2014 do not",
955
+ "# hand-edit.",
956
+ "phase_label_type_of() {",
957
+ ' case "${1:-}" in'
958
+ ];
959
+ for (const matcher of exactMatchers) {
960
+ lines.push(` ${matcher}) echo "${PHASE_LABEL_TYPE_MAP[matcher]}" ;;`);
961
+ }
962
+ for (const matcher of prefixMatchers) {
963
+ lines.push(` ${matcher}*) echo "${PHASE_LABEL_TYPE_MAP[matcher]}" ;;`);
964
+ }
965
+ lines.push(
966
+ " *) : ;;",
967
+ " esac",
968
+ "}",
969
+ "",
970
+ "# Return 0 when the argument is a conventional-commit `type:*`",
971
+ "# label \u2014 the ONLY type labels the invariant correction may remove.",
972
+ "# A `type:*` label owned by another bundle is never removed; the",
973
+ "# issue is flagged for human triage instead.",
974
+ "is_conventional_type_label() {",
975
+ ` case "\${1:-}" in`,
976
+ ` ${CONVENTIONAL_COMMIT_TYPE_LABELS.join("|")}) return 0 ;;`,
977
+ " *) return 1 ;;",
978
+ " esac",
979
+ "}",
980
+ "",
981
+ "# Resolve an issue's FULL label list (one label per line on stdin)",
982
+ "# to the type label the phase-label invariant requires. Emits",
983
+ "# KEY=VALUE assignments the caller parses:",
984
+ "# OUTCOME=none \u2014 no recognised phase label; invariant N/A",
985
+ "# OUTCOME=match \u2014 plus TYPE_LABEL=<type:bundle>",
986
+ "# OUTCOME=ambiguous \u2014 plus CANDIDATE_TYPE_LABELS=<space-separated>",
987
+ "# PHASE_LABELS=<space-separated recognised phase labels>",
988
+ "phase_type_of() {",
989
+ " local label resolved",
990
+ ' local matched_types=""',
991
+ ' local matched_phase=""',
992
+ " while IFS= read -r label; do",
993
+ ' [ -z "$label" ] && continue',
994
+ ' resolved=$(phase_label_type_of "$label")',
995
+ ' [ -z "$resolved" ] && continue',
996
+ ' matched_phase="${matched_phase}${label} "',
997
+ ' case " ${matched_types} " in',
998
+ ' *" ${resolved} "*) ;;',
999
+ ' *) matched_types="${matched_types}${resolved} " ;;',
1000
+ " esac",
1001
+ " done",
1002
+ " local count=0",
1003
+ " for resolved in $matched_types; do",
1004
+ " count=$((count + 1))",
1005
+ " done",
1006
+ ' if [ "$count" -eq 0 ]; then',
1007
+ ' echo "OUTCOME=none"',
1008
+ ' elif [ "$count" -eq 1 ]; then',
1009
+ ' echo "OUTCOME=match"',
1010
+ ' echo "TYPE_LABEL=${matched_types% }"',
1011
+ " else",
1012
+ ' echo "OUTCOME=ambiguous"',
1013
+ ' echo "CANDIDATE_TYPE_LABELS=${matched_types% }"',
1014
+ " fi",
1015
+ ' echo "PHASE_LABELS=${matched_phase% }"',
1016
+ "}"
1017
+ );
1018
+ return lines.join("\n");
1019
+ }
1020
+ function buildPhaseLabelTypeMap() {
1021
+ const map = {};
1022
+ for (const [bundleName, ownership] of Object.entries(BUNDLE_OWNERSHIP)) {
1023
+ if (ownership.phaseLabelPrefixes.length === 0) {
1024
+ continue;
1025
+ }
1026
+ if (ownership.typeLabels.length !== 1) {
1027
+ throw new Error(
1028
+ `BUNDLE_OWNERSHIP["${bundleName}"] declares ${ownership.phaseLabelPrefixes.length} phase-label matcher(s) but ${ownership.typeLabels.length} type label(s); a phase label must pair with exactly one type label`
1029
+ );
1030
+ }
1031
+ const typeLabel = `type:${ownership.typeLabels[0]}`;
1032
+ for (const matcher of ownership.phaseLabelPrefixes) {
1033
+ const existing = map[matcher];
1034
+ if (existing !== void 0 && existing !== typeLabel) {
1035
+ throw new Error(
1036
+ `Phase-label matcher "${matcher}" resolves to both "${existing}" and "${typeLabel}"; a matcher must imply exactly one type label`
1037
+ );
1038
+ }
1039
+ map[matcher] = typeLabel;
1040
+ }
1041
+ }
1042
+ return map;
1043
+ }
1044
+ function isTypeLabelOwnedByExcluded(typeLabel, excludedBundles) {
1045
+ if (excludedBundles.length === 0) {
1046
+ return false;
1047
+ }
1048
+ for (const bundleName of excludedBundles) {
1049
+ const ownership = BUNDLE_OWNERSHIP[bundleName];
1050
+ if (!ownership) {
1051
+ continue;
1052
+ }
1053
+ if (ownership.typeLabels.includes(typeLabel)) {
1054
+ const owners = findOwnersOfTypeLabel(typeLabel);
1055
+ const allExcluded = owners.every(
1056
+ (owner) => excludedBundles.includes(owner)
1057
+ );
1058
+ if (allExcluded) {
1059
+ return true;
1060
+ }
1061
+ }
1062
+ }
1063
+ return false;
1064
+ }
1065
+ function isPhaseLabelOwnedByExcluded(phaseLabel, excludedBundles) {
1066
+ if (excludedBundles.length === 0) {
1067
+ return false;
1068
+ }
1069
+ for (const bundleName of excludedBundles) {
1070
+ const ownership = BUNDLE_OWNERSHIP[bundleName];
1071
+ if (!ownership) {
1072
+ continue;
1073
+ }
1074
+ for (const entry of ownership.phaseLabelPrefixes) {
1075
+ if (entry.endsWith(":")) {
1076
+ if (phaseLabel.startsWith(entry)) {
1077
+ return true;
1078
+ }
1079
+ } else if (phaseLabel === entry) {
1080
+ return true;
1081
+ }
1082
+ }
1083
+ }
1084
+ return false;
1085
+ }
1086
+ function isScheduledTaskOwnedByExcluded(taskId, excludedBundles) {
1087
+ if (excludedBundles.length === 0) {
1088
+ return false;
1089
+ }
1090
+ for (const bundleName of excludedBundles) {
1091
+ const ownership = BUNDLE_OWNERSHIP[bundleName];
1092
+ if (!ownership) {
1093
+ continue;
1094
+ }
1095
+ if (ownership.scheduledTaskIds.includes(taskId)) {
1096
+ return true;
1097
+ }
1098
+ }
1099
+ return false;
1100
+ }
1101
+ function hasAnyDocsEmittingBundle(excludedBundles) {
1102
+ for (const [bundleName, ownership] of Object.entries(BUNDLE_OWNERSHIP)) {
1103
+ if (!ownership.emitsDocs) {
1104
+ continue;
1105
+ }
1106
+ if (!excludedBundles.includes(bundleName)) {
1107
+ return true;
1108
+ }
1109
+ }
1110
+ return false;
1111
+ }
1112
+ function hasAnyDownstreamIssueKindBundle(excludedBundles) {
1113
+ for (const [bundleName, ownership] of Object.entries(BUNDLE_OWNERSHIP)) {
1114
+ if (!ownership.downstreamIssueKinds) {
1115
+ continue;
1116
+ }
1117
+ if (!excludedBundles.includes(bundleName)) {
1118
+ return true;
1119
+ }
1120
+ }
1121
+ return false;
1122
+ }
1123
+ function findOwnersOfTypeLabel(typeLabel) {
1124
+ const owners = [];
1125
+ for (const [bundleName, ownership] of Object.entries(BUNDLE_OWNERSHIP)) {
1126
+ if (ownership.typeLabels.includes(typeLabel)) {
1127
+ owners.push(bundleName);
1128
+ }
1129
+ }
1130
+ return owners;
1131
+ }
1132
+
1133
+ // src/agent/bundles/issue-type.ts
1134
+ var GITHUB_ISSUE_TYPES = ["Epic", "Feature", "Bug", "Task"];
1135
+ var DEFAULT_GITHUB_ISSUE_TYPE = "Task";
1136
+ var NON_DEFAULT_TITLE_PREFIX_TYPES = {
1137
+ "epic:": "Epic",
1138
+ "feat:": "Feature",
1139
+ "fix:": "Bug"
1140
+ };
1141
+ var GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX = buildTitlePrefixTypeMap();
1142
+ function githubIssueTypeForTitle(title) {
1143
+ const colon = title.indexOf(":");
1144
+ if (colon === -1) {
1145
+ return DEFAULT_GITHUB_ISSUE_TYPE;
1146
+ }
1147
+ const prefix2 = title.slice(0, colon + 1).trim().toLowerCase();
1148
+ return GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX[prefix2] ?? DEFAULT_GITHUB_ISSUE_TYPE;
1149
+ }
1150
+ var SET_ISSUE_TYPE_HELPER_PATH = ".claude/procedures/set-issue-type.sh";
1151
+ function renderSetIssueTypeFallbackLines() {
1152
+ return [
1153
+ "```bash",
1154
+ "gh api graphql -f query='query($owner:String!,$repo:String!){repository(owner:$owner,name:$repo){issueTypes(first:50){nodes{id name}}}}' -f owner=<owner> -f repo=<repo>",
1155
+ "gh issue view <issue-number> --json id --jq '.id'",
1156
+ "gh api graphql -f query='mutation($issueId:ID!,$typeId:ID!){updateIssueIssueType(input:{issueId:$issueId,issueTypeId:$typeId}){issue{number issueType{name}}}}' -f issueId=<issue-node-id> -f typeId=<issue-type-node-id>",
1157
+ "```"
1158
+ ];
1159
+ }
1160
+ function renderGithubIssueTypeSectionLines() {
1161
+ const defaultPrefixes = Object.keys(GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX).filter(
1162
+ (prefix2) => GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX[prefix2] === DEFAULT_GITHUB_ISSUE_TYPE
1163
+ ).sort().map((prefix2) => `\`${prefix2}\``).join(", ");
1164
+ return [
1165
+ "## GitHub Issue Type",
1166
+ "",
1167
+ "A GitHub **issue type** is a distinct field from the `type:*` **label**:",
1168
+ "the label routes and dedups, the issue type drives triage and Epic",
1169
+ "tracking. Setting one does not set the other. Derive it from the",
1170
+ "**title prefix**:",
1171
+ "",
1172
+ "| Prefix | GitHub Issue Type |",
1173
+ "|--------|------------------|",
1174
+ "| `epic:` | Epic |",
1175
+ "| `feat:` | Feature |",
1176
+ "| `fix:` | Bug |",
1177
+ `| ${defaultPrefixes} | ${DEFAULT_GITHUB_ISSUE_TYPE} |`,
1178
+ "| any bundle-phase prefix (`company:`, `req:`, \u2026), or no prefix | Task |",
1179
+ "",
1180
+ "### Assigning the GitHub issue type",
1181
+ "",
1182
+ "`gh issue create` **cannot** set the issue type \u2014 there is no `--type`",
1183
+ "flag \u2014 so assign it in a follow-up call immediately after creation, for",
1184
+ "every issue an agent files (auto-enqueued downstream issues included).",
1185
+ "An issue that skips that call stays untyped forever. The",
1186
+ "`github-workflow` bundle ships a one-step helper:",
1187
+ "",
1188
+ "```bash",
1189
+ `${SET_ISSUE_TYPE_HELPER_PATH} <issue-number> <${GITHUB_ISSUE_TYPES.join("|")}>`,
1190
+ "```",
1191
+ "",
1192
+ "When that helper is absent (the bundle is excluded), run the",
1193
+ "`updateIssueIssueType` GraphQL flow it wraps:",
1194
+ "",
1195
+ ...renderSetIssueTypeFallbackLines()
1196
+ ];
1197
+ }
1198
+ function renderTitlePrefixTypeBullets(indent = " ") {
1199
+ const nonDefault = Object.entries(NON_DEFAULT_TITLE_PREFIX_TYPES).map(
1200
+ ([prefix2, issueType]) => `${indent}- \`${prefix2}\` \u2192 ${issueType}`
1201
+ );
1202
+ const defaults = Object.keys(GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX).filter(
1203
+ (prefix2) => GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX[prefix2] === DEFAULT_GITHUB_ISSUE_TYPE
1204
+ ).sort().map((prefix2) => `\`${prefix2}\``).join(", ");
1205
+ return [
1206
+ ...nonDefault,
1207
+ `${indent}- ${defaults} \u2192 ${DEFAULT_GITHUB_ISSUE_TYPE}`,
1208
+ `${indent}- any bundle-phase prefix (\`company:\`, \`req:\`, \`bcm:\`, \u2026) \u2192 ${DEFAULT_GITHUB_ISSUE_TYPE}`
1209
+ ];
1210
+ }
1211
+ function renderGithubIssueTypeSection() {
1212
+ return renderGithubIssueTypeSectionLines().join("\n");
1213
+ }
1214
+ function renderIssueTypeAssignmentStep(options = {}) {
1215
+ const indent = options.indent ?? " ";
1216
+ const issueType = options.issueType ?? DEFAULT_GITHUB_ISSUE_TYPE;
1217
+ const lead = options.bullet ? `${indent}- ` : indent;
1218
+ const cont = options.bullet ? `${indent} ` : indent;
1219
+ return [
1220
+ `${lead}Then assign the new issue's **GitHub issue type** (\`${issueType}\`) \u2014`,
1221
+ `${cont}the \`type:*\` label does not set it:`,
1222
+ `${cont}\`${SET_ISSUE_TYPE_HELPER_PATH} <issue-number> ${issueType}\`. See`,
1223
+ `${cont}**Assigning the GitHub issue type** for the no-helper fallback.`
1224
+ ];
1225
+ }
1226
+ function renderIssueTypeAssignmentBlanket(indent = "", issueType = DEFAULT_GITHUB_ISSUE_TYPE) {
1227
+ return [
1228
+ `${indent}**Every** issue this phase files needs a **GitHub issue type**`,
1229
+ `${indent}assigned right after creation \u2014 the \`type:*\` label does not set`,
1230
+ `${indent}it, and \`gh issue create\` cannot:`,
1231
+ `${indent}\`${SET_ISSUE_TYPE_HELPER_PATH} <issue-number> ${issueType}\`. See`,
1232
+ `${indent}**Assigning the GitHub issue type** for the prefix table (nearly`,
1233
+ `${indent}every downstream issue is a \`${issueType}\`) and the no-helper fallback.`
1234
+ ];
1235
+ }
1236
+ function buildTitlePrefixTypeMap() {
1237
+ const map = {};
1238
+ for (const label of CONVENTIONAL_COMMIT_TYPE_LABELS) {
1239
+ map[`${label.slice("type:".length)}:`] = DEFAULT_GITHUB_ISSUE_TYPE;
1240
+ }
1241
+ for (const [prefix2, issueType] of Object.entries(
1242
+ NON_DEFAULT_TITLE_PREFIX_TYPES
1243
+ )) {
1244
+ map[prefix2] = issueType;
1245
+ }
1246
+ return map;
1247
+ }
1248
+
641
1249
  // src/agent/bundles/project-context.ts
642
1250
  var PROJECT_CONTEXT_PATH = "docs/src/content/docs/project-context.md";
643
1251
  var SUBPROJECT_ROLE_GUIDANCE = [
@@ -1230,6 +1838,9 @@ var draftAgendaSkill = {
1230
1838
  ' - `desired_outcomes` \u2014 1\u20133 bullets describing "done"',
1231
1839
  " - `pre_reads` \u2014 relative paths to existing docs (optional)",
1232
1840
  " - `related_docs` \u2014 relative paths for context (optional)",
1841
+ "",
1842
+ ...renderIssueTypeAssignmentStep(),
1843
+ "",
1233
1844
  "2. Execute Phase 1 (Draft) of the agenda-analyst agent.",
1234
1845
  "3. If the draft phase inline-finalizes (default path), no",
1235
1846
  " downstream issues are created.",
@@ -1294,6 +1905,9 @@ var finalizeAgendaSkill = {
1294
1905
  " - Path to the draft agenda",
1295
1906
  " (`<meetingsRoot>/<YYYY-MM-DD>-<slug>/agenda.md`)",
1296
1907
  " - Any late-added items to incorporate",
1908
+ "",
1909
+ ...renderIssueTypeAssignmentStep(),
1910
+ "",
1297
1911
  "2. Execute Phase 2 (Finalize) of the agenda-analyst agent.",
1298
1912
  "",
1299
1913
  "## Output",
@@ -1839,6 +2453,14 @@ function renderIssueTemplatesStarterPage(_it) {
1839
2453
  "`status:*` label from the conventions documented in the",
1840
2454
  "`issue-label-conventions` rule.",
1841
2455
  "",
2456
+ "Every recipe also ends with a **GitHub issue type** assignment.",
2457
+ "`gh issue create` cannot set the issue type, so it takes a second",
2458
+ "call \u2014 an issue that skips it stays untyped forever. Derive the type",
2459
+ "from the title prefix (`epic:` \u2192 Epic, `feat:` \u2192 Feature, `fix:` \u2192",
2460
+ "Bug, everything else \u2014 including every bundle-phase prefix \u2014 \u2192",
2461
+ "Task); see **Assigning the GitHub issue type** in the",
2462
+ "`issue-conventions` rule for the command and its fallback.",
2463
+ "",
1842
2464
  "Before creating any profile-style issue (people, companies,",
1843
2465
  "software), run the duplicate check documented in the owning",
1844
2466
  "bundle's rule \u2014 the issue-templates page is a recipe catalogue,",
@@ -1873,6 +2495,13 @@ function renderIssueTemplatesStarterPage(_it) {
1873
2495
  "- [ ] <criterion 1>",
1874
2496
  "- [ ] <criterion 2>",
1875
2497
  '"',
2498
+ "```",
2499
+ "",
2500
+ "Then assign the GitHub issue type (`Task` for a phase-labelled",
2501
+ "title):",
2502
+ "",
2503
+ "```bash",
2504
+ `${SET_ISSUE_TYPE_HELPER_PATH} <issue-number> ${DEFAULT_GITHUB_ISSUE_TYPE}`,
1876
2505
  "```"
1877
2506
  ].join("\n");
1878
2507
  }
@@ -4977,16 +5606,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
4977
5606
  "| `release:` | Release preparation, version bumps |",
4978
5607
  "| `hotfix:` | Urgent production fixes |",
4979
5608
  "",
4980
- "## GitHub Issue Type",
4981
- "",
4982
- "When creating issues, always assign the appropriate **GitHub issue type** based on the title prefix:",
4983
- "",
4984
- "| Prefix | GitHub Issue Type |",
4985
- "|--------|------------------|",
4986
- "| `epic:` | Epic |",
4987
- "| `feat:` | Feature |",
4988
- "| `fix:` | Bug |",
4989
- "| `chore:`, `docs:`, `refactor:`, `release:`, `hotfix:` | Task |",
5609
+ ...renderGithubIssueTypeSectionLines(),
4990
5610
  "",
4991
5611
  "## Prerequisite Issues",
4992
5612
  "",
@@ -5676,6 +6296,8 @@ function buildBcmWriterSubAgent(paths, issueDefaults) {
5676
6296
  ` - \`priority:${labelsForPhase(issueDefaults, "people:research").priority}\``,
5677
6297
  ` - \`status:${labelsForPhase(issueDefaults, "people:research").status}\``,
5678
6298
  "",
6299
+ ...renderIssueTypeAssignmentStep(),
6300
+ "",
5679
6301
  " **Unfamiliar company (enabling vendor, partner, competitor).**",
5680
6302
  " Hand off to the `company-profile` bundle by creating a",
5681
6303
  " `company:research` issue. The issue must carry:",
@@ -5685,6 +6307,8 @@ function buildBcmWriterSubAgent(paths, issueDefaults) {
5685
6307
  ` - \`priority:${labelsForPhase(issueDefaults, "company:research").priority}\``,
5686
6308
  ` - \`status:${labelsForPhase(issueDefaults, "company:research").status}\``,
5687
6309
  "",
6310
+ ...renderIssueTypeAssignmentStep(),
6311
+ "",
5688
6312
  " **Missing research topic (value stream, market sizing, etc.).**",
5689
6313
  " Hand off to the `research-pipeline` bundle by creating a",
5690
6314
  " `research:scope` issue. The issue must carry:",
@@ -5694,6 +6318,8 @@ function buildBcmWriterSubAgent(paths, issueDefaults) {
5694
6318
  ` - \`priority:${labelsForPhase(issueDefaults, "research:scope").priority}\``,
5695
6319
  ` - \`status:${labelsForPhase(issueDefaults, "research:scope").status}\``,
5696
6320
  "",
6321
+ ...renderIssueTypeAssignmentStep(),
6322
+ "",
5697
6323
  " This phase assumes the `people-profile`, `company-profile`, and",
5698
6324
  " `research-pipeline` bundles are enabled in the consuming project.",
5699
6325
  " If a bundle is not enabled, flag the connect issue with",
@@ -5816,6 +6442,8 @@ function buildWriteBcmSkill(paths, issueDefaults) {
5816
6442
  ` \`priority:${labelsForPhase(issueDefaults, "bcm:outline").priority}\`, and \`status:${labelsForPhase(issueDefaults, "bcm:outline").status}\`. Body must include the`,
5817
6443
  " verbatim capability name and any overrides.",
5818
6444
  "",
6445
+ "",
6446
+ ...renderIssueTypeAssignmentStep(),
5819
6447
  "2. Execute Phase 1 (Outline) of the bcm-writer agent.",
5820
6448
  "",
5821
6449
  "3. Phase 1 creates a `bcm:scaffold` issue, which Phase 2 follows with",
@@ -7096,6 +7724,8 @@ function buildScanBusinessModelsSkill(paths, issueDefaults) {
7096
7724
  "1. Create a `business-models:scan` issue with `type:business-model`,",
7097
7725
  ` \`priority:${labelsForPhase(issueDefaults, "business-models:scan").priority}\`, and \`status:${labelsForPhase(issueDefaults, "business-models:scan").status}\`. The body must include the`,
7098
7726
  " industry slug, the source plan path (if any), and any overrides.",
7727
+ "",
7728
+ ...renderIssueTypeAssignmentStep(),
7099
7729
  "2. Execute Phase 1 (Scan) of the business-models-analyst agent.",
7100
7730
  "3. Phase 1 creates one `business-models:canvas` issue per in-scope",
7101
7731
  " segment. Each canvas issue declares its `Depends on:`",
@@ -7170,6 +7800,8 @@ function buildCanvasBusinessModelSkill(paths, issueDefaults) {
7170
7800
  ` \`type:business-model\`, \`priority:${labelsForPhase(issueDefaults, "business-models:canvas").priority}\`, and \`status:${labelsForPhase(issueDefaults, "business-models:canvas").status}\`.`,
7171
7801
  " The body must include the industry slug, segment slug, and any",
7172
7802
  " scan-report reference.",
7803
+ "",
7804
+ ...renderIssueTypeAssignmentStep(),
7173
7805
  "2. Execute Phase 2 (Canvas) of the business-models-analyst agent.",
7174
7806
  "3. Phase 2 writes the nine-block canvas and creates a",
7175
7807
  " `business-models:complete` issue for the downstream Phase 3.",
@@ -7229,6 +7861,8 @@ function buildCompleteBusinessModelSkill(paths, issueDefaults) {
7229
7861
  "1. Create a `business-models:complete` issue with",
7230
7862
  ` \`type:business-model\`, \`priority:${labelsForPhase(issueDefaults, "business-models:complete").priority}\`, and \`status:${labelsForPhase(issueDefaults, "business-models:complete").status}\`.`,
7231
7863
  " Body must include the canvas document path.",
7864
+ "",
7865
+ ...renderIssueTypeAssignmentStep(),
7232
7866
  "2. Execute Phase 3 (Complete) of the business-models-analyst agent.",
7233
7867
  "",
7234
7868
  "## Output",
@@ -7753,6 +8387,8 @@ function buildCompanyProfileAnalystSubAgent(paths, issueDefaults, tier) {
7753
8387
  ` - \`priority:${labelsForPhase(issueDefaults, "people:research").priority}\``,
7754
8388
  ` - \`status:${labelsForPhase(issueDefaults, "people:research").status}\``,
7755
8389
  "",
8390
+ ...renderIssueTypeAssignmentStep(),
8391
+ "",
7756
8392
  " The issue body must include:",
7757
8393
  " - A **discovery source** line naming this company profile",
7758
8394
  " (`Discovered while profiling: <company name>`)",
@@ -7770,6 +8406,8 @@ function buildCompanyProfileAnalystSubAgent(paths, issueDefaults, tier) {
7770
8406
  ` - \`priority:${labelsForPhase(issueDefaults, "software:research").priority}\``,
7771
8407
  ` - \`status:${labelsForPhase(issueDefaults, "software:research").status}\``,
7772
8408
  "",
8409
+ ...renderIssueTypeAssignmentStep(),
8410
+ "",
7773
8411
  " The issue body must include:",
7774
8412
  " - A **discovery source** line naming this company profile",
7775
8413
  " (`Discovered while profiling: <company name>`)",
@@ -8321,6 +8959,8 @@ function buildProfileCompanySkill(paths, issueDefaults) {
8321
8959
  "1. Create a `company:research` issue with `type:company-profile`,",
8322
8960
  ` \`priority:${labelsForPhase(issueDefaults, "company:research").priority}\`, and \`status:${labelsForPhase(issueDefaults, "company:research").status}\`. Body must include the`,
8323
8961
  " company name, selected type, framing, and any overrides.",
8962
+ "",
8963
+ ...renderIssueTypeAssignmentStep(),
8324
8964
  "2. Execute Phase 1 (Research) of the company-profile-analyst agent.",
8325
8965
  "3. Phase 1 creates the `company:draft` issue. Phase 2 may create a",
8326
8966
  " `company:followup` issue **and** a `company:match` issue. Each",
@@ -8395,6 +9035,8 @@ function buildMatchCompanySkill(paths, issueDefaults) {
8395
9035
  "1. Create a `company:match` issue with `type:company-profile`,",
8396
9036
  ` \`priority:${labelsForPhase(issueDefaults, "company:match").priority}\`, and \`status:${labelsForPhase(issueDefaults, "company:match").status}\`. Body must include the`,
8397
9037
  " profile path and any overrides.",
9038
+ "",
9039
+ ...renderIssueTypeAssignmentStep(),
8398
9040
  "2. Execute Phase 4 (Match) of the company-profile-analyst agent.",
8399
9041
  "",
8400
9042
  "## Output",
@@ -8468,6 +9110,8 @@ function buildRefreshCompanySkill(paths, issueDefaults) {
8468
9110
  "1. Create a `company:refresh` issue with `type:company-profile`,",
8469
9111
  ` \`priority:${labelsForPhase(issueDefaults, "company:refresh").priority}\`, and \`status:${labelsForPhase(issueDefaults, "company:refresh").status}\`. Body must include the`,
8470
9112
  " profile path and any overrides.",
9113
+ "",
9114
+ ...renderIssueTypeAssignmentStep(),
8471
9115
  "2. Execute Phase 5 (Refresh) of the company-profile-analyst agent.",
8472
9116
  "",
8473
9117
  "## Output",
@@ -8541,6 +9185,8 @@ function buildAnalyzeSegmentSkill(paths, issueDefaults) {
8541
9185
  "1. Create a `company:analyze` issue with `type:company-profile`,",
8542
9186
  ` \`priority:${labelsForPhase(issueDefaults, "company:analyze").priority}\`, and \`status:${labelsForPhase(issueDefaults, "company:analyze").status}\`. Body must include the`,
8543
9187
  " segment slug and any overrides.",
9188
+ "",
9189
+ ...renderIssueTypeAssignmentStep(),
8544
9190
  "2. Execute Phase 6 (Analyze) of the company-profile-analyst agent.",
8545
9191
  "",
8546
9192
  "## Output",
@@ -9129,6 +9775,8 @@ function buildCustomerProfileAnalystSubAgent(paths, issueDefaults, tier) {
9129
9775
  ` - \`priority:${labelsForPhase(issueDefaults, "company:research").priority}\``,
9130
9776
  ` - \`status:${labelsForPhase(issueDefaults, "company:research").status}\``,
9131
9777
  "",
9778
+ ...renderIssueTypeAssignmentStep(),
9779
+ "",
9132
9780
  "9. **Create `people:research` issues** for notable contacts",
9133
9781
  " (economic buyers, technical buyers, user champions) surfaced in",
9134
9782
  " sources. Keep the list small \u2014 at most three individuals per",
@@ -9140,6 +9788,8 @@ function buildCustomerProfileAnalystSubAgent(paths, issueDefaults, tier) {
9140
9788
  ` - \`priority:${labelsForPhase(issueDefaults, "people:research").priority}\``,
9141
9789
  ` - \`status:${labelsForPhase(issueDefaults, "people:research").status}\``,
9142
9790
  "",
9791
+ ...renderIssueTypeAssignmentStep(),
9792
+ "",
9143
9793
  "10. **Verify all handoffs were created** before committing.",
9144
9794
  "",
9145
9795
  "11. **Commit and push** the archetype page. Close the profile issue.",
@@ -9241,6 +9891,8 @@ function buildCustomerProfileAnalystSubAgent(paths, issueDefaults, tier) {
9241
9891
  ` - \`priority:${labelsForPhase(issueDefaults, "req:scan").priority}\``,
9242
9892
  ` - \`status:${labelsForPhase(issueDefaults, "req:scan").status}\``,
9243
9893
  "",
9894
+ ...renderIssueTypeAssignmentStep(),
9895
+ "",
9244
9896
  " The body must also:",
9245
9897
  " - Link back to the archetype page and the competitor page",
9246
9898
  " - Cite the specific unmet-need row and the originating job",
@@ -9257,6 +9909,8 @@ function buildCustomerProfileAnalystSubAgent(paths, issueDefaults, tier) {
9257
9909
  ` - \`priority:${labelsForPhase(issueDefaults, "company:research").priority}\``,
9258
9910
  ` - \`status:${labelsForPhase(issueDefaults, "company:research").status}\``,
9259
9911
  "",
9912
+ ...renderIssueTypeAssignmentStep(),
9913
+ "",
9260
9914
  "9. **Create `people:research` issues** for notable competitor",
9261
9915
  " leadership or customer-advocacy figures surfaced in the analysis.",
9262
9916
  " Keep this list small \u2014 at most three individuals per competitors",
@@ -9268,6 +9922,8 @@ function buildCustomerProfileAnalystSubAgent(paths, issueDefaults, tier) {
9268
9922
  ` - \`priority:${labelsForPhase(issueDefaults, "people:research").priority}\``,
9269
9923
  ` - \`status:${labelsForPhase(issueDefaults, "people:research").status}\``,
9270
9924
  "",
9925
+ ...renderIssueTypeAssignmentStep(),
9926
+ "",
9271
9927
  "10. **Commit and push** the competitor-analysis page and the",
9272
9928
  " updated archetype page. Close the competitors issue.",
9273
9929
  "",
@@ -9454,6 +10110,8 @@ function buildDiscoverCustomersSkill(paths, issueDefaults) {
9454
10110
  "1. Create a `customer:discover` issue with `type:customer-profile`,",
9455
10111
  ` \`priority:${labelsForPhase(issueDefaults, "customer:discover").priority}\`, and \`status:${labelsForPhase(issueDefaults, "customer:discover").status}\`. The body must include the`,
9456
10112
  " scope slug and any known archetypes to include or exclude.",
10113
+ "",
10114
+ ...renderIssueTypeAssignmentStep(),
9457
10115
  "2. Execute Phase 1 (Discover) of the customer-profile-analyst agent.",
9458
10116
  "3. Phase 1 writes the discovery report and creates one",
9459
10117
  " `customer:profile` issue per prioritized archetype.",
@@ -9529,6 +10187,8 @@ function buildProfileCustomerSkill(paths, issueDefaults) {
9529
10187
  "1. Create a `customer:profile` issue with `type:customer-profile`,",
9530
10188
  ` \`priority:${labelsForPhase(issueDefaults, "customer:profile").priority}\`, and \`status:${labelsForPhase(issueDefaults, "customer:profile").status}\`. The body must include the`,
9531
10189
  " archetype slug and (optionally) the segment slug.",
10190
+ "",
10191
+ ...renderIssueTypeAssignmentStep(),
9532
10192
  "2. Execute Phase 2 (Profile) of the customer-profile-analyst agent.",
9533
10193
  " The agent runs 4\u20138 targeted web searches and populates the",
9534
10194
  " archetype page.",
@@ -9605,6 +10265,8 @@ function buildAnalyzeCustomerCompetitorsSkill(paths, issueDefaults) {
9605
10265
  ` \`status:${labelsForPhase(issueDefaults, "customer:competitors").status}\` (or \`status:blocked\` when still dependent on a`,
9606
10266
  " Phase 2 profile issue). Body must include the archetype page",
9607
10267
  " path.",
10268
+ "",
10269
+ ...renderIssueTypeAssignmentStep(),
9608
10270
  "2. Execute Phase 3 (Competitors) of the customer-profile-analyst",
9609
10271
  " agent. The agent reads `<MATRIX_FILE>` and runs up to 4\u20138 web",
9610
10272
  " searches only to close competitor-coverage gaps the matrix does",
@@ -11314,10 +11976,7 @@ function buildGithubWorkflowBundle(buildPolicy = DEFAULT_BUILD_POLICY) {
11314
11976
  " - If unclear, ask the user which type applies",
11315
11977
  "2. **Compose the issue title** in the format: `<type>: <short description>`",
11316
11978
  "3. **Determine the GitHub issue type** based on the prefix:",
11317
- " - `epic:` \u2192 Epic",
11318
- " - `feat:` \u2192 Feature",
11319
- " - `fix:` \u2192 Bug",
11320
- " - `chore:`, `docs:`, `refactor:`, `release:`, `hotfix:` \u2192 Task",
11979
+ ...renderTitlePrefixTypeBullets(),
11321
11980
  "4. **Identify prerequisite issues** \u2014 if the user mentions dependencies or blockers, include a **Dependencies** section in the body with `Depends on: #<issue-number>`",
11322
11981
  "5. **Determine labels** \u2014 every issue must be created with the following labels:",
11323
11982
  " - **`type:*`** \u2014 derived from the issue title prefix:",
@@ -11803,6 +12462,7 @@ function buildIndustryDiscoveryAnalystSubAgent(paths, issueDefaults) {
11803
12462
  " downstream research needs to answer)",
11804
12463
  " - Cite the evaluation row that justified advancing the vertical",
11805
12464
  " - Reference the plan file path for traceability",
12465
+ ...renderIssueTypeAssignmentStep({ indent: " ", bullet: true }),
11806
12466
  "",
11807
12467
  "5. **Cross-link** \u2014 update the plan file's `## Cleared Verticals`",
11808
12468
  " table so each row references its newly-created `research:scope`",
@@ -11893,6 +12553,8 @@ function buildDiscoverIndustriesSkill(paths, issueDefaults) {
11893
12553
  "1. Create an `industry:discover` issue with `type:industry-discovery`,",
11894
12554
  ` \`priority:${labelsForPhase(issueDefaults, "industry:discover").priority}\`, and \`status:${labelsForPhase(issueDefaults, "industry:discover").status}\`. Body must include the`,
11895
12555
  " verbatim scope, authorized sources, and any overrides.",
12556
+ "",
12557
+ ...renderIssueTypeAssignmentStep(),
11896
12558
  "2. Execute Phase 1 (Discover) of the industry-discovery-analyst",
11897
12559
  " agent.",
11898
12560
  "3. Phase 1 creates an `industry:evaluate` issue, which Phase 2 follows",
@@ -12171,6 +12833,8 @@ function buildMaintenanceAuditSubAgent(paths, tier) {
12171
12833
  "4. Commit, push, open a PR (if applicable), and close the issue per",
12172
12834
  " your project's PR workflow.",
12173
12835
  "",
12836
+ ...renderIssueTypeAssignmentBlanket(),
12837
+ "",
12174
12838
  "---",
12175
12839
  "",
12176
12840
  ...renderClaimVerificationSection(),
@@ -12603,6 +13267,8 @@ function buildMaintenanceAuditSkill(paths, issueDefaults) {
12603
13267
  " - `<AUDIT_ROOT>` \u2014 where to write the audit report",
12604
13268
  " - `<AUDIT_SLUG>` \u2014 short identifier for this audit",
12605
13269
  " - The audit checks to run (defaults to the full check catalog)",
13270
+ "",
13271
+ ...renderIssueTypeAssignmentStep(),
12606
13272
  "2. Execute Phase 1 (Scan) of the maintenance-audit agent.",
12607
13273
  "3. If auto-fixable findings exist, a `maint:fix` issue is created",
12608
13274
  " automatically.",
@@ -12667,6 +13333,8 @@ function buildMaintenanceVerifySkill(issueDefaults) {
12667
13333
  " - The Phase 2 fix report path (if one exists)",
12668
13334
  " - The `Paths Touched` list (copied from the fix report)",
12669
13335
  " - The audit check catalog (copied from the scan issue)",
13336
+ "",
13337
+ ...renderIssueTypeAssignmentStep(),
12670
13338
  "2. Execute Phase 3 (Verify) of the maintenance-audit agent.",
12671
13339
  "",
12672
13340
  "## Output",
@@ -13176,6 +13844,8 @@ function buildMeetingAnalystSubAgent(tier) {
13176
13844
  " - Always create a `meeting:link` issue \u2014 blocked on the draft issue if one",
13177
13845
  " was created, otherwise blocked on the notes issue",
13178
13846
  "",
13847
+ ...renderIssueTypeAssignmentStep(),
13848
+ "",
13179
13849
  "7. **Commit the extraction content first.** Stage and commit",
13180
13850
  " the `insights/{type}/<basename>.md` file (and any newly",
13181
13851
  " created `insights/index.md` / `insights/{type}/index.md`",
@@ -13375,6 +14045,8 @@ function buildMeetingAnalystSubAgent(tier) {
13375
14045
  "",
13376
14046
  "### Steps",
13377
14047
  "",
14048
+ ...renderIssueTypeAssignmentBlanket(),
14049
+ "",
13378
14050
  "1. Read the drafts from Phase 3 (if they exist) and the extraction",
13379
14051
  " from Phase 1 at `<meetingsRoot>/insights/{type}/<basename>.md`.",
13380
14052
  " Re-read the extraction frontmatter for `meeting_kind` and `areas`,",
@@ -13431,707 +14103,228 @@ function buildMeetingAnalystSubAgent(tier) {
13431
14103
  " (the degenerate case above). For the kind `planning`, also",
13432
14104
  " update the active sprint-plan doc directly (when the",
13433
14105
  " sprint-plan doc's area is in-scope, or always when no",
13434
- " `meetingAreas` are declared); for the kind `review`, mark",
13435
- " completed tasks in that same doc.",
13436
- "7. **Update the extraction file** at",
13437
- " `<meetingsRoot>/insights/{type}/<basename>.md` with a",
13438
- " `## Downstream Artifacts` section listing every issue and",
13439
- " document created from this meeting. Note any items that were",
13440
- " deferred to follow-up issues because of the area gate.",
13441
- "8. **Update the meeting notes** at",
13442
- " `<meetingsRoot>/notes/{type}/<basename>.md` with a",
13443
- " `## Related Issues` section listing all issues created from this",
13444
- " meeting.",
13445
- "9. Comment on the parent extract issue with a summary linking to all",
13446
- " created artifacts.",
13447
- "10. Commit and push (if any file changes were made), then close the link issue.",
13448
- "",
13449
- "---",
13450
- "",
13451
- "## GitHub Integration",
13452
- "",
13453
- "- Use `gh` CLI for all GitHub operations",
13454
- "- Apply the appropriate `meeting:*` phase label to each phase issue",
13455
- "- Use `type:docs` for documentation-producing issues, `type:chore` for",
13456
- " maintenance/organizational tasks the pipeline can action",
13457
- "- Do **not** file issues for human-owned action items \u2014 per the",
13458
- " **Action-item filing policy** they live only in the notes",
13459
- " `## Action Items` table",
13460
- "- Use `status:` labels to track progress (`status:ready`, `status:in-progress`, `status:done`)",
13461
- "- Reference the source meeting transcript in all created issues",
13462
- "- Link phase issues with `Depends on: #N` to enforce ordering",
13463
- "",
13464
- "## When to Shorten the Pipeline",
13465
- "",
13466
- "- **Short meetings** (<30 min, <2000 words): combine extract + notes into one session",
13467
- "- **No requirements or decisions**: Phase 1 skips creating the `meeting:draft` issue",
13468
- "- **Only action items**: extract + notes + link (3 phase issues)"
13469
- ].join("\n")
13470
- };
13471
- }
13472
- var processMeetingSkill = {
13473
- name: "process-meeting",
13474
- description: "Process a meeting transcript through the 4-phase meeting analysis pipeline",
13475
- disableModelInvocation: true,
13476
- userInvocable: true,
13477
- context: "fork",
13478
- agent: "meeting-analyst",
13479
- platforms: { cursor: { exclude: true } },
13480
- instructions: [
13481
- "# Process Meeting",
13482
- "",
13483
- "Kick off meeting processing by executing Phase 1 (Extract) and",
13484
- "creating downstream phase issues for the remaining phases. Inputs",
13485
- "live in the **sibling-tree layout**: pass any file from",
13486
- "`<meetingsRoot>/notes/{type}/` or `<meetingsRoot>/transcripts/{type}/`",
13487
- "(both, notes-only, and transcript-only are all valid input cases).",
13488
- "",
13489
- "## Usage",
13490
- "",
13491
- "/process-meeting <path-to-notes-or-transcript>",
13492
- "",
13493
- "## Basename-pairing algorithm",
13494
- "",
13495
- "The user may pass either a notes-tree path or a transcripts-tree",
13496
- "path. Resolve the sibling implicitly via filesystem lookup \u2014",
13497
- "do **not** prompt the user or fail-loud when the sibling is",
13498
- "absent:",
13499
- "",
13500
- "1. Infer the **basename** (filename without the `.md` extension)",
13501
- " and the `{internal,external}` partition from the provided path.",
13502
- " Both segments are usually adjacent in the path \u2014",
13503
- " `\u2026/notes/internal/2026-03-14-sprint-kickoff.md` yields basename",
13504
- " `2026-03-14-sprint-kickoff` and type `internal`.",
13505
- "2. Determine the **input tree** from the path:",
13506
- " - If the path contains `/notes/`, treat it as the notes input",
13507
- " and look for the sibling at",
13508
- " `<meetingsRoot>/transcripts/<type>/<basename>.md`.",
13509
- " - If the path contains `/transcripts/`, treat it as the",
13510
- " transcript input and look for the sibling at",
13511
- " `<meetingsRoot>/notes/<type>/<basename>.md`.",
13512
- " - If the path matches neither (e.g. an ad-hoc location), infer",
13513
- " basename + type as best you can from the filename and",
13514
- " surrounding folder segments, then proceed with whichever",
13515
- " sibling-tree lookups succeed. Best-effort, never fail-loud.",
13516
- "3. Read **every** sibling that exists on disk. The combination",
13517
- " (notes present, transcript present, both present) selects the",
13518
- " Phase-1 input case from the **input-case matrix** in the",
13519
- " `meeting-analyst` agent prompt.",
13520
- "4. A missing sibling is a normal outcome \u2014 proceed with whatever",
13521
- " was found. Notes-only and transcript-only are both valid",
13522
- " Phase-1 input cases; the agent never warns or prompts the",
13523
- " user about the absence.",
13524
- "",
13525
- "## Steps",
13526
- "",
13527
- "1. Resolve the basename, partition, and any sibling files using",
13528
- " the **Basename-pairing algorithm** above",
13529
- "2. Execute Phase 1 (Extract) \u2014 categorize the available content into",
13530
- " decisions, requirements, action items, open questions, and more.",
13531
- " Tag each action item's disposition (`agent-workable` vs",
13532
- " `human-owned`) per the agent's **Action-item filing policy** so",
13533
- " Phase 4 keeps human-owned tasks out of the issue queue",
13534
- "3. Write the extraction to",
13535
- " `<meetingsRoot>/insights/{type}/<basename>.md`",
13536
- "4. Create downstream phase issues using `gh issue create`:",
13537
- " - `meeting:notes` issue (always)",
13538
- " - `meeting:draft` issue (if requirements or decisions were found)",
13539
- " - `meeting:link` issue (always)",
13540
- "5. Each downstream issue includes `Depends on:` linking to its predecessor",
13541
- "6. Commit and push the extraction file",
13542
- "",
13543
- "## Input",
13544
- "",
13545
- "Provide a path to a file in either `notes/` or `transcripts/`. The",
13546
- "agent will look up the sibling automatically via basename match. A",
13547
- "missing sibling is a normal case (notes-only or transcript-only) \u2014",
13548
- "the agent handles all three input cases.",
13549
- "",
13550
- "## Output",
13551
- "",
13552
- "- A structured extraction at",
13553
- " `<meetingsRoot>/insights/{type}/<basename>.md`",
13554
- "- Phase issues with `meeting:*` labels for downstream agent sessions",
13555
- " to pick up (notes, draft, link)"
13556
- ].join("\n")
13557
- };
13558
- function buildMeetingAnalysisBundle(tier = AGENT_MODEL.BALANCED) {
13559
- return {
13560
- name: "meeting-analysis",
13561
- description: "Meeting transcript processing workflow with 4-phase pipeline (extract, notes, draft, link)",
13562
- appliesWhen: () => true,
13563
- rules: [
13564
- {
13565
- name: "meeting-processing-workflow",
13566
- description: "Describes the 4-phase meeting processing pipeline, extraction taxonomy, and labeling conventions",
13567
- scope: AGENT_RULE_SCOPE.ALWAYS,
13568
- content: [
13569
- "# Meeting Processing Workflow",
13570
- "",
13571
- "Use `/process-meeting <path>` to process a meeting through a",
13572
- "4-phase pipeline (extract \u2192 notes \u2192 draft \u2192 link). Each phase runs as a",
13573
- "separate agent session tracked by a GitHub issue with a `meeting:*` label.",
13574
- "See the `meeting-analyst` agent definition for full workflow details.",
13575
- "",
13576
- "Meeting artifacts live in **three parallel trees** rooted at the",
13577
- "project's meetings root:",
13578
- "",
13579
- "- `transcripts/{internal,external}/` \u2014 verbatim transcripts (evidence)",
13580
- "- `notes/{internal,external}/` \u2014 curated narrative summary",
13581
- " (input + Phase 2 output)",
13582
- "- `insights/{internal,external}/` \u2014 structured extraction (Phase 1 output)",
13583
- "",
13584
- "The three trees share identical basenames so a meeting's siblings",
13585
- "are looked up by filename, not by frontmatter cross-references.",
13586
- "Phase 1 accepts every input case (notes + transcript, notes only,",
13587
- "or transcript only) and Phase 2 enhances the notes file in place",
13588
- "rather than authoring it from scratch.",
13589
- "",
13590
- "Notes and insights files may carry optional frontmatter fields",
13591
- "`slug`, `source`, `transcript_available`, and `confidence`",
13592
- "alongside the existing `meeting_type` and `areas` fields. Missing",
13593
- "frontmatter is treated as a normal input case \u2014 never fail-loud.",
13594
- "",
13595
- "Meeting notes may declare a `meeting_type` (one of the project's",
13596
- "recognized types) and an `areas: [...]` list. The `meeting_type`",
13597
- "resolves to a generic kind \u2014 `planning` / `review` / `brainstorm` /",
13598
- "`standup` / `external` / `other` \u2014 that drives type-specific",
13599
- "extraction rules in phases 1\u20132. The `areas` list scopes phase-4",
13600
- "direct edits to the doc-root sub-folders declared in the project's",
13601
- "**Area \u2192 doc-root mapping** table. When both fields are absent",
13602
- "the pipeline falls back to the default workflow (kind `other`,",
13603
- "no area gating).",
13604
- "",
13605
- "Phase 4 applies an **agent-workability split** to action items:",
13606
- "items that yield a documentation deliverable an agent can author",
13607
- "are filed through their dedicated downstream channel",
13608
- "(`req:write`, `docs:write`, `bcm:*`, `research:scope`, roadmap",
13609
- "follow-up); human-owned tasks (send / schedule / install /",
13610
- "decide / communicate / get-access / build-elsewhere) are",
13611
- "recorded **only** in the notes `## Action Items` table, which is",
13612
- "the **system of record for human follow-ups** \u2014 no GitHub issue",
13613
- "is filed for them. Consumers tune or disable the split via",
13614
- "`AgentConfigOptions.meetings.actionItemFiling`."
13615
- ].join("\n"),
13616
- platforms: {
13617
- cursor: { exclude: true }
13618
- },
13619
- tags: ["workflow"]
13620
- }
13621
- ],
13622
- skills: [processMeetingSkill],
13623
- subAgents: [buildMeetingAnalystSubAgent(tier)],
13624
- labels: [
13625
- {
13626
- name: "type:meeting-processing",
13627
- color: "5319E7",
13628
- description: "Meeting transcript processing pipeline"
13629
- },
13630
- {
13631
- name: "meeting:extract",
13632
- color: "C5DEF5",
13633
- description: "Phase 1: raw extraction from a meeting transcript"
13634
- },
13635
- {
13636
- name: "meeting:notes",
13637
- color: "BFDADC",
13638
- description: "Phase 2: curated notes derived from an extraction"
13639
- },
13640
- {
13641
- name: "meeting:draft",
13642
- color: "D4C5F9",
13643
- description: "Phase 3: draft follow-up issues proposed from notes"
13644
- },
13645
- {
13646
- name: "meeting:link",
13647
- color: "FEF2C0",
13648
- description: "Phase 4: linking/reconciling drafted issues with existing work"
13649
- }
13650
- ]
13651
- };
13652
- }
13653
- var meetingAnalysisBundle = buildMeetingAnalysisBundle();
13654
-
13655
- // src/agent/bundles/bundle-ownership.ts
13656
- var BUNDLE_OWNERSHIP = {
13657
- agenda: {
13658
- typeLabels: ["agenda"],
13659
- phaseLabelPrefixes: ["agenda:"],
13660
- scheduledTaskIds: ["worker-agenda"],
13661
- emitsDocs: true,
13662
- downstreamIssueKinds: true
13663
- },
13664
- "bcm-writer": {
13665
- typeLabels: ["bcm-document"],
13666
- phaseLabelPrefixes: ["bcm:"],
13667
- scheduledTaskIds: ["worker-bcm-writer"],
13668
- emitsDocs: true,
13669
- downstreamIssueKinds: true
13670
- },
13671
- "business-models": {
13672
- typeLabels: ["business-model"],
13673
- phaseLabelPrefixes: ["business-models:"],
13674
- scheduledTaskIds: ["worker-business-models"],
13675
- emitsDocs: true,
13676
- downstreamIssueKinds: true
13677
- },
13678
- "company-profile": {
13679
- typeLabels: ["company-profile"],
13680
- phaseLabelPrefixes: ["company:"],
13681
- scheduledTaskIds: ["worker-company-profile"],
13682
- emitsDocs: true,
13683
- downstreamIssueKinds: true
13684
- },
13685
- "customer-profile": {
13686
- typeLabels: ["customer-profile"],
13687
- phaseLabelPrefixes: ["customer:"],
13688
- scheduledTaskIds: ["worker-customer-profile"],
13689
- emitsDocs: true,
13690
- downstreamIssueKinds: true
13691
- },
13692
- "docs-sync": {
13693
- typeLabels: ["docs-sync"],
13694
- phaseLabelPrefixes: ["docs-sync:"],
13695
- scheduledTaskIds: ["worker-docs-sync"],
13696
- emitsDocs: true,
13697
- downstreamIssueKinds: true
13698
- },
13699
- "industry-discovery": {
13700
- typeLabels: ["industry-discovery"],
13701
- phaseLabelPrefixes: ["industry:"],
13702
- scheduledTaskIds: ["worker-industry-discovery"],
13703
- emitsDocs: true,
13704
- downstreamIssueKinds: true
13705
- },
13706
- "maintenance-audit": {
13707
- typeLabels: ["maintenance"],
13708
- phaseLabelPrefixes: ["maint:"],
13709
- scheduledTaskIds: ["worker-maintenance"],
13710
- emitsDocs: false,
13711
- downstreamIssueKinds: true
13712
- },
13713
- "meeting-analysis": {
13714
- typeLabels: ["meeting-processing"],
13715
- phaseLabelPrefixes: ["meeting:"],
13716
- scheduledTaskIds: ["worker-meeting-analysis"],
13717
- emitsDocs: true,
13718
- downstreamIssueKinds: true
13719
- },
13720
- orchestrator: {
13721
- // The orchestrator pipeline manager itself ships with the
13722
- // `worker-orchestrator` scheduled task. It owns no `type:*` label
13723
- // (it dispatches every type) and no phase-label prefix.
13724
- typeLabels: [],
13725
- phaseLabelPrefixes: [],
13726
- scheduledTaskIds: ["worker-orchestrator"],
13727
- emitsDocs: false,
13728
- downstreamIssueKinds: false
13729
- },
13730
- "people-profile": {
13731
- typeLabels: ["people-profile"],
13732
- phaseLabelPrefixes: ["people:"],
13733
- scheduledTaskIds: ["worker-people-profile"],
13734
- emitsDocs: true,
13735
- downstreamIssueKinds: true
13736
- },
13737
- "pr-review": {
13738
- typeLabels: ["pr-review"],
13739
- phaseLabelPrefixes: [],
13740
- scheduledTaskIds: ["worker-pr-review"],
13741
- emitsDocs: false,
13742
- downstreamIssueKinds: false
13743
- },
13744
- "regulatory-research": {
13745
- typeLabels: ["regulatory-research"],
13746
- phaseLabelPrefixes: ["regulatory:"],
13747
- scheduledTaskIds: ["worker-regulatory-research"],
13748
- emitsDocs: true,
13749
- downstreamIssueKinds: true
13750
- },
13751
- "requirements-analyst": {
13752
- typeLabels: ["requirement"],
13753
- phaseLabelPrefixes: ["req:"],
13754
- scheduledTaskIds: ["worker-requirements-analyst"],
13755
- emitsDocs: true,
13756
- downstreamIssueKinds: true
13757
- },
13758
- "requirements-reviewer": {
13759
- // Co-owns `type:requirement` with the analyst and writer; owns
13760
- // the `req:review` and `req:deprecate` phase labels exactly.
13761
- typeLabels: ["requirement"],
13762
- phaseLabelPrefixes: ["req:review", "req:deprecate"],
13763
- scheduledTaskIds: ["worker-requirements-reviewer"],
13764
- emitsDocs: true,
13765
- downstreamIssueKinds: true
13766
- },
13767
- "requirements-writer": {
13768
- // Co-owns `type:requirement` with the analyst and reviewer; owns
13769
- // the `req:write` phase label exactly.
13770
- typeLabels: ["requirement"],
13771
- phaseLabelPrefixes: ["req:write"],
13772
- scheduledTaskIds: ["worker-requirements-writer"],
13773
- emitsDocs: true,
13774
- downstreamIssueKinds: true
13775
- },
13776
- "research-pipeline": {
13777
- typeLabels: ["research"],
13778
- phaseLabelPrefixes: ["research:"],
13779
- scheduledTaskIds: ["worker-research"],
13780
- emitsDocs: true,
13781
- downstreamIssueKinds: true
13782
- },
13783
- "software-profile": {
13784
- typeLabels: ["software-profile"],
13785
- phaseLabelPrefixes: ["software:"],
13786
- scheduledTaskIds: ["worker-software-profile"],
13787
- emitsDocs: true,
13788
- downstreamIssueKinds: true
13789
- },
13790
- "standards-research": {
13791
- typeLabels: ["standards-research"],
13792
- phaseLabelPrefixes: ["standards:"],
13793
- scheduledTaskIds: ["worker-standards-research"],
13794
- emitsDocs: true,
13795
- downstreamIssueKinds: true
13796
- }
13797
- };
13798
- var CONVENTIONAL_COMMIT_TYPE_LABELS = [
13799
- "type:chore",
13800
- "type:docs",
13801
- "type:feat",
13802
- "type:fix",
13803
- "type:hotfix",
13804
- "type:refactor",
13805
- "type:release"
13806
- ];
13807
- var PHASE_LABEL_TYPE_MAP = buildPhaseLabelTypeMap();
13808
- function typeLabelForPhaseLabel(phaseLabel) {
13809
- const exact = PHASE_LABEL_TYPE_MAP[phaseLabel];
13810
- if (exact !== void 0 && !phaseLabel.endsWith(":")) {
13811
- return exact;
13812
- }
13813
- let bestMatcher;
13814
- for (const matcher of Object.keys(PHASE_LABEL_TYPE_MAP)) {
13815
- if (!matcher.endsWith(":")) {
13816
- continue;
13817
- }
13818
- if (!phaseLabel.startsWith(matcher)) {
13819
- continue;
13820
- }
13821
- if (bestMatcher === void 0 || matcher.length > bestMatcher.length) {
13822
- bestMatcher = matcher;
13823
- }
13824
- }
13825
- return bestMatcher === void 0 ? void 0 : PHASE_LABEL_TYPE_MAP[bestMatcher];
13826
- }
13827
- function resolveTypeLabelForLabels(labels) {
13828
- const phaseLabels = [];
13829
- const candidates = /* @__PURE__ */ new Set();
13830
- for (const label of labels) {
13831
- const typeLabel = typeLabelForPhaseLabel(label);
13832
- if (typeLabel === void 0) {
13833
- continue;
13834
- }
13835
- phaseLabels.push(label);
13836
- candidates.add(typeLabel);
13837
- }
13838
- const candidateTypeLabels = Array.from(candidates).sort();
13839
- if (candidateTypeLabels.length === 0) {
13840
- return { outcome: "none", candidateTypeLabels: [], phaseLabels: [] };
13841
- }
13842
- if (candidateTypeLabels.length === 1) {
13843
- return {
13844
- outcome: "match",
13845
- typeLabel: candidateTypeLabels[0],
13846
- candidateTypeLabels,
13847
- phaseLabels
13848
- };
13849
- }
13850
- return { outcome: "ambiguous", candidateTypeLabels, phaseLabels };
14106
+ " `meetingAreas` are declared); for the kind `review`, mark",
14107
+ " completed tasks in that same doc.",
14108
+ "7. **Update the extraction file** at",
14109
+ " `<meetingsRoot>/insights/{type}/<basename>.md` with a",
14110
+ " `## Downstream Artifacts` section listing every issue and",
14111
+ " document created from this meeting. Note any items that were",
14112
+ " deferred to follow-up issues because of the area gate.",
14113
+ "8. **Update the meeting notes** at",
14114
+ " `<meetingsRoot>/notes/{type}/<basename>.md` with a",
14115
+ " `## Related Issues` section listing all issues created from this",
14116
+ " meeting.",
14117
+ "9. Comment on the parent extract issue with a summary linking to all",
14118
+ " created artifacts.",
14119
+ "10. Commit and push (if any file changes were made), then close the link issue.",
14120
+ "",
14121
+ "---",
14122
+ "",
14123
+ "## GitHub Integration",
14124
+ "",
14125
+ "- Use `gh` CLI for all GitHub operations",
14126
+ "- Apply the appropriate `meeting:*` phase label to each phase issue",
14127
+ "- Use `type:docs` for documentation-producing issues, `type:chore` for",
14128
+ " maintenance/organizational tasks the pipeline can action",
14129
+ "- Do **not** file issues for human-owned action items \u2014 per the",
14130
+ " **Action-item filing policy** they live only in the notes",
14131
+ " `## Action Items` table",
14132
+ "- Use `status:` labels to track progress (`status:ready`, `status:in-progress`, `status:done`)",
14133
+ "- Reference the source meeting transcript in all created issues",
14134
+ "- Link phase issues with `Depends on: #N` to enforce ordering",
14135
+ "",
14136
+ "## When to Shorten the Pipeline",
14137
+ "",
14138
+ "- **Short meetings** (<30 min, <2000 words): combine extract + notes into one session",
14139
+ "- **No requirements or decisions**: Phase 1 skips creating the `meeting:draft` issue",
14140
+ "- **Only action items**: extract + notes + link (3 phase issues)"
14141
+ ].join("\n")
14142
+ };
13851
14143
  }
13852
- function renderPhaseTypeInvariantSection(excludeBundles = []) {
13853
- const rows = Object.keys(PHASE_LABEL_TYPE_MAP).filter(
13854
- (matcher) => !isPhaseLabelMatcherOwnedByExcluded(matcher, excludeBundles)
13855
- ).sort().map((matcher) => {
13856
- const display = matcher.endsWith(":") ? `${matcher}*` : matcher;
13857
- const kind = matcher.endsWith(":") ? "prefix" : "exact";
13858
- return `| \`${display}\` | ${kind} | \`${PHASE_LABEL_TYPE_MAP[matcher]}\` |`;
13859
- });
13860
- return [
13861
- "## Phase-label \u2192 `type:<bundle>` invariant",
13862
- "",
13863
- "Every phased-pipeline bundle pairs its `<bundle>:<phase>` labels",
13864
- "with exactly one `type:<bundle>` label. That type label is the",
13865
- "**dedup + dispatch signal**: agents de-duplicate downstream work",
13866
- 'with `gh issue list --label "type:<bundle>"`, and Phase E derives',
13867
- "its funnel-tier sort key from the issue's `type:*` label. An issue",
13868
- "that carries the phase label but a conventional-commit `type:*`",
13869
- "(`type:feat`, `type:docs`, \u2026, stamped from its title prefix by the",
13870
- "generic create-issue workflow) is invisible to that dedup query and",
13871
- "mis-tiers in dispatch.",
13872
- "",
13873
- "The pairing below is derived from the canonical bundle-ownership",
13874
- "map \u2014 the same source of truth that generates the label registry.",
13875
- "Matchers ending in `*` match by prefix; the rest match exactly, and",
13876
- "an exact match always beats a prefix match.",
14144
+ var processMeetingSkill = {
14145
+ name: "process-meeting",
14146
+ description: "Process a meeting transcript through the 4-phase meeting analysis pipeline",
14147
+ disableModelInvocation: true,
14148
+ userInvocable: true,
14149
+ context: "fork",
14150
+ agent: "meeting-analyst",
14151
+ platforms: { cursor: { exclude: true } },
14152
+ instructions: [
14153
+ "# Process Meeting",
13877
14154
  "",
13878
- "| Phase label | Match | Required type label |",
13879
- "|-------------|-------|---------------------|",
13880
- ...rows,
14155
+ "Kick off meeting processing by executing Phase 1 (Extract) and",
14156
+ "creating downstream phase issues for the remaining phases. Inputs",
14157
+ "live in the **sibling-tree layout**: pass any file from",
14158
+ "`<meetingsRoot>/notes/{type}/` or `<meetingsRoot>/transcripts/{type}/`",
14159
+ "(both, notes-only, and transcript-only are all valid input cases).",
13881
14160
  "",
13882
- "### Enforcement",
14161
+ "## Usage",
13883
14162
  "",
13884
- "The Phase D maintenance sweep runs the invariant in auto-correct",
13885
- "mode and emits one summary line:",
14163
+ "/process-meeting <path-to-notes-or-transcript>",
13886
14164
  "",
13887
- "```",
13888
- "LABEL_INVARIANT_DONE mode=fix checked=<N> ok=<O> violations=<V> corrected=<C> flagged=<F>",
13889
- "```",
14165
+ "## Basename-pairing algorithm",
13890
14166
  "",
13891
- "A **report-only** audit is available for repos that prefer",
13892
- "report-then-fix over auto-correct \u2014 same sweep, no mutations:",
14167
+ "The user may pass either a notes-tree path or a transcripts-tree",
14168
+ "path. Resolve the sibling implicitly via filesystem lookup \u2014",
14169
+ "do **not** prompt the user or fail-loud when the sibling is",
14170
+ "absent:",
13893
14171
  "",
13894
- "```bash",
13895
- ".claude/procedures/check-blocked.sh label-invariant # report only",
13896
- ".claude/procedures/check-blocked.sh label-invariant --fix # apply",
13897
- "```",
14172
+ "1. Infer the **basename** (filename without the `.md` extension)",
14173
+ " and the `{internal,external}` partition from the provided path.",
14174
+ " Both segments are usually adjacent in the path \u2014",
14175
+ " `\u2026/notes/internal/2026-03-14-sprint-kickoff.md` yields basename",
14176
+ " `2026-03-14-sprint-kickoff` and type `internal`.",
14177
+ "2. Determine the **input tree** from the path:",
14178
+ " - If the path contains `/notes/`, treat it as the notes input",
14179
+ " and look for the sibling at",
14180
+ " `<meetingsRoot>/transcripts/<type>/<basename>.md`.",
14181
+ " - If the path contains `/transcripts/`, treat it as the",
14182
+ " transcript input and look for the sibling at",
14183
+ " `<meetingsRoot>/notes/<type>/<basename>.md`.",
14184
+ " - If the path matches neither (e.g. an ad-hoc location), infer",
14185
+ " basename + type as best you can from the filename and",
14186
+ " surrounding folder segments, then proceed with whichever",
14187
+ " sibling-tree lookups succeed. Best-effort, never fail-loud.",
14188
+ "3. Read **every** sibling that exists on disk. The combination",
14189
+ " (notes present, transcript present, both present) selects the",
14190
+ " Phase-1 input case from the **input-case matrix** in the",
14191
+ " `meeting-analyst` agent prompt.",
14192
+ "4. A missing sibling is a normal outcome \u2014 proceed with whatever",
14193
+ " was found. Notes-only and transcript-only are both valid",
14194
+ " Phase-1 input cases; the agent never warns or prompts the",
14195
+ " user about the absence.",
13898
14196
  "",
13899
- "### Correction policy",
14197
+ "## Steps",
13900
14198
  "",
13901
- "An issue must carry **exactly one** `type:*` label. Merely *adding*",
13902
- "the required type label to an issue that already carries a",
13903
- "conventional-commit one would leave two, making the funnel-tier sort",
13904
- "key (which reads the **first** `type:*` label) non-deterministic and",
13905
- "violating the label conventions. The correction therefore",
13906
- "**replaces**, in a single atomic `gh issue edit`, so the issue is",
13907
- "never observable carrying two `type:*` labels:",
14199
+ "1. Resolve the basename, partition, and any sibling files using",
14200
+ " the **Basename-pairing algorithm** above",
14201
+ "2. Execute Phase 1 (Extract) \u2014 categorize the available content into",
14202
+ " decisions, requirements, action items, open questions, and more.",
14203
+ " Tag each action item's disposition (`agent-workable` vs",
14204
+ " `human-owned`) per the agent's **Action-item filing policy** so",
14205
+ " Phase 4 keeps human-owned tasks out of the issue queue",
14206
+ "3. Write the extraction to",
14207
+ " `<meetingsRoot>/insights/{type}/<basename>.md`",
14208
+ "4. Create downstream phase issues using `gh issue create`:",
14209
+ " - `meeting:notes` issue (always)",
14210
+ " - `meeting:draft` issue (if requirements or decisions were found)",
14211
+ " - `meeting:link` issue (always)",
13908
14212
  "",
13909
- "| Issue state | Action |",
13910
- "|-------------|--------|",
13911
- "| Required type label present, nothing else | none \u2014 compliant, left untouched |",
13912
- "| No `type:*` label at all | **add** the required label |",
13913
- "| Only conventional-commit `type:*` label(s) | **replace** \u2014 remove them, add the required label |",
13914
- "| Required label present **plus** a stray conventional-commit one | **remove** the stray, leaving exactly one |",
13915
- "| A `type:*` label owned by a **different** bundle | **flag** `status:needs-attention` \u2014 never guessed at |",
13916
- "| Phase labels imply **two or more** type labels | **flag** `status:needs-attention` \u2014 never auto-corrected |",
14213
+ ...renderIssueTypeAssignmentStep(),
14214
+ "5. Each downstream issue includes `Depends on:` linking to its predecessor",
14215
+ "6. Commit and push the extraction file",
13917
14216
  "",
13918
- "Only a conventional-commit `type:*` label is ever removed. A",
13919
- "`type:*` label owned by another bundle carries real routing",
13920
- "information, so the sweep refuses to guess and hands the issue to a",
13921
- "human instead. Ambiguity is narrower than it looks: all three",
13922
- "requirements bundles declare `type:requirement`, so every `req:*`",
13923
- "phase label resolves to the same type label \u2014 genuine ambiguity",
13924
- "needs phase labels from two bundles with *different* type labels.",
14217
+ "## Input",
13925
14218
  "",
13926
- "`status:needs-attention` is applied **additively** \u2014 the base",
13927
- "`status:*` label always stays, per the additive-flag rule. Flagging",
13928
- "is idempotent: an issue already carrying the flag is reported but",
13929
- "not re-flagged."
13930
- ].join("\n");
13931
- }
13932
- function isPhaseLabelMatcherOwnedByExcluded(matcher, excludeBundles) {
13933
- if (excludeBundles.length === 0) {
13934
- return false;
13935
- }
13936
- let owned = false;
13937
- for (const [bundleName, ownership] of Object.entries(BUNDLE_OWNERSHIP)) {
13938
- if (!ownership.phaseLabelPrefixes.includes(matcher)) {
13939
- continue;
13940
- }
13941
- owned = true;
13942
- if (!excludeBundles.includes(bundleName)) {
13943
- return false;
13944
- }
13945
- }
13946
- return owned;
13947
- }
13948
- function renderPhaseTypeInvariantShellHelpers() {
13949
- const matchers = Object.keys(PHASE_LABEL_TYPE_MAP);
13950
- const exactMatchers = matchers.filter((m) => !m.endsWith(":")).sort();
13951
- const prefixMatchers = matchers.filter((m) => m.endsWith(":")).sort((a, b) => b.length - a.length || a.localeCompare(b));
13952
- const lines = [
13953
- "# Resolve ONE phase label to the `type:<bundle>` label its owning",
13954
- "# bundle declares. Echoes the label (with the `type:` prefix) or",
13955
- "# nothing when no bundle owns it. Exact-match branches come first,",
13956
- "# so `req:write` (requirements-writer) beats the `req:` prefix",
13957
- "# (requirements-analyst). Generated from BUNDLE_OWNERSHIP \u2014 do not",
13958
- "# hand-edit.",
13959
- "phase_label_type_of() {",
13960
- ' case "${1:-}" in'
13961
- ];
13962
- for (const matcher of exactMatchers) {
13963
- lines.push(` ${matcher}) echo "${PHASE_LABEL_TYPE_MAP[matcher]}" ;;`);
13964
- }
13965
- for (const matcher of prefixMatchers) {
13966
- lines.push(` ${matcher}*) echo "${PHASE_LABEL_TYPE_MAP[matcher]}" ;;`);
13967
- }
13968
- lines.push(
13969
- " *) : ;;",
13970
- " esac",
13971
- "}",
14219
+ "Provide a path to a file in either `notes/` or `transcripts/`. The",
14220
+ "agent will look up the sibling automatically via basename match. A",
14221
+ "missing sibling is a normal case (notes-only or transcript-only) \u2014",
14222
+ "the agent handles all three input cases.",
13972
14223
  "",
13973
- "# Return 0 when the argument is a conventional-commit `type:*`",
13974
- "# label \u2014 the ONLY type labels the invariant correction may remove.",
13975
- "# A `type:*` label owned by another bundle is never removed; the",
13976
- "# issue is flagged for human triage instead.",
13977
- "is_conventional_type_label() {",
13978
- ` case "\${1:-}" in`,
13979
- ` ${CONVENTIONAL_COMMIT_TYPE_LABELS.join("|")}) return 0 ;;`,
13980
- " *) return 1 ;;",
13981
- " esac",
13982
- "}",
14224
+ "## Output",
13983
14225
  "",
13984
- "# Resolve an issue's FULL label list (one label per line on stdin)",
13985
- "# to the type label the phase-label invariant requires. Emits",
13986
- "# KEY=VALUE assignments the caller parses:",
13987
- "# OUTCOME=none \u2014 no recognised phase label; invariant N/A",
13988
- "# OUTCOME=match \u2014 plus TYPE_LABEL=<type:bundle>",
13989
- "# OUTCOME=ambiguous \u2014 plus CANDIDATE_TYPE_LABELS=<space-separated>",
13990
- "# PHASE_LABELS=<space-separated recognised phase labels>",
13991
- "phase_type_of() {",
13992
- " local label resolved",
13993
- ' local matched_types=""',
13994
- ' local matched_phase=""',
13995
- " while IFS= read -r label; do",
13996
- ' [ -z "$label" ] && continue',
13997
- ' resolved=$(phase_label_type_of "$label")',
13998
- ' [ -z "$resolved" ] && continue',
13999
- ' matched_phase="${matched_phase}${label} "',
14000
- ' case " ${matched_types} " in',
14001
- ' *" ${resolved} "*) ;;',
14002
- ' *) matched_types="${matched_types}${resolved} " ;;',
14003
- " esac",
14004
- " done",
14005
- " local count=0",
14006
- " for resolved in $matched_types; do",
14007
- " count=$((count + 1))",
14008
- " done",
14009
- ' if [ "$count" -eq 0 ]; then',
14010
- ' echo "OUTCOME=none"',
14011
- ' elif [ "$count" -eq 1 ]; then',
14012
- ' echo "OUTCOME=match"',
14013
- ' echo "TYPE_LABEL=${matched_types% }"',
14014
- " else",
14015
- ' echo "OUTCOME=ambiguous"',
14016
- ' echo "CANDIDATE_TYPE_LABELS=${matched_types% }"',
14017
- " fi",
14018
- ' echo "PHASE_LABELS=${matched_phase% }"',
14019
- "}"
14020
- );
14021
- return lines.join("\n");
14022
- }
14023
- function buildPhaseLabelTypeMap() {
14024
- const map = {};
14025
- for (const [bundleName, ownership] of Object.entries(BUNDLE_OWNERSHIP)) {
14026
- if (ownership.phaseLabelPrefixes.length === 0) {
14027
- continue;
14028
- }
14029
- if (ownership.typeLabels.length !== 1) {
14030
- throw new Error(
14031
- `BUNDLE_OWNERSHIP["${bundleName}"] declares ${ownership.phaseLabelPrefixes.length} phase-label matcher(s) but ${ownership.typeLabels.length} type label(s); a phase label must pair with exactly one type label`
14032
- );
14033
- }
14034
- const typeLabel = `type:${ownership.typeLabels[0]}`;
14035
- for (const matcher of ownership.phaseLabelPrefixes) {
14036
- const existing = map[matcher];
14037
- if (existing !== void 0 && existing !== typeLabel) {
14038
- throw new Error(
14039
- `Phase-label matcher "${matcher}" resolves to both "${existing}" and "${typeLabel}"; a matcher must imply exactly one type label`
14040
- );
14041
- }
14042
- map[matcher] = typeLabel;
14043
- }
14044
- }
14045
- return map;
14046
- }
14047
- function isTypeLabelOwnedByExcluded(typeLabel, excludedBundles) {
14048
- if (excludedBundles.length === 0) {
14049
- return false;
14050
- }
14051
- for (const bundleName of excludedBundles) {
14052
- const ownership = BUNDLE_OWNERSHIP[bundleName];
14053
- if (!ownership) {
14054
- continue;
14055
- }
14056
- if (ownership.typeLabels.includes(typeLabel)) {
14057
- const owners = findOwnersOfTypeLabel(typeLabel);
14058
- const allExcluded = owners.every(
14059
- (owner) => excludedBundles.includes(owner)
14060
- );
14061
- if (allExcluded) {
14062
- return true;
14226
+ "- A structured extraction at",
14227
+ " `<meetingsRoot>/insights/{type}/<basename>.md`",
14228
+ "- Phase issues with `meeting:*` labels for downstream agent sessions",
14229
+ " to pick up (notes, draft, link)"
14230
+ ].join("\n")
14231
+ };
14232
+ function buildMeetingAnalysisBundle(tier = AGENT_MODEL.BALANCED) {
14233
+ return {
14234
+ name: "meeting-analysis",
14235
+ description: "Meeting transcript processing workflow with 4-phase pipeline (extract, notes, draft, link)",
14236
+ appliesWhen: () => true,
14237
+ rules: [
14238
+ {
14239
+ name: "meeting-processing-workflow",
14240
+ description: "Describes the 4-phase meeting processing pipeline, extraction taxonomy, and labeling conventions",
14241
+ scope: AGENT_RULE_SCOPE.ALWAYS,
14242
+ content: [
14243
+ "# Meeting Processing Workflow",
14244
+ "",
14245
+ "Use `/process-meeting <path>` to process a meeting through a",
14246
+ "4-phase pipeline (extract \u2192 notes \u2192 draft \u2192 link). Each phase runs as a",
14247
+ "separate agent session tracked by a GitHub issue with a `meeting:*` label.",
14248
+ "See the `meeting-analyst` agent definition for full workflow details.",
14249
+ "",
14250
+ "Meeting artifacts live in **three parallel trees** rooted at the",
14251
+ "project's meetings root:",
14252
+ "",
14253
+ "- `transcripts/{internal,external}/` \u2014 verbatim transcripts (evidence)",
14254
+ "- `notes/{internal,external}/` \u2014 curated narrative summary",
14255
+ " (input + Phase 2 output)",
14256
+ "- `insights/{internal,external}/` \u2014 structured extraction (Phase 1 output)",
14257
+ "",
14258
+ "The three trees share identical basenames so a meeting's siblings",
14259
+ "are looked up by filename, not by frontmatter cross-references.",
14260
+ "Phase 1 accepts every input case (notes + transcript, notes only,",
14261
+ "or transcript only) and Phase 2 enhances the notes file in place",
14262
+ "rather than authoring it from scratch.",
14263
+ "",
14264
+ "Notes and insights files may carry optional frontmatter fields",
14265
+ "`slug`, `source`, `transcript_available`, and `confidence`",
14266
+ "alongside the existing `meeting_type` and `areas` fields. Missing",
14267
+ "frontmatter is treated as a normal input case \u2014 never fail-loud.",
14268
+ "",
14269
+ "Meeting notes may declare a `meeting_type` (one of the project's",
14270
+ "recognized types) and an `areas: [...]` list. The `meeting_type`",
14271
+ "resolves to a generic kind \u2014 `planning` / `review` / `brainstorm` /",
14272
+ "`standup` / `external` / `other` \u2014 that drives type-specific",
14273
+ "extraction rules in phases 1\u20132. The `areas` list scopes phase-4",
14274
+ "direct edits to the doc-root sub-folders declared in the project's",
14275
+ "**Area \u2192 doc-root mapping** table. When both fields are absent",
14276
+ "the pipeline falls back to the default workflow (kind `other`,",
14277
+ "no area gating).",
14278
+ "",
14279
+ "Phase 4 applies an **agent-workability split** to action items:",
14280
+ "items that yield a documentation deliverable an agent can author",
14281
+ "are filed through their dedicated downstream channel",
14282
+ "(`req:write`, `docs:write`, `bcm:*`, `research:scope`, roadmap",
14283
+ "follow-up); human-owned tasks (send / schedule / install /",
14284
+ "decide / communicate / get-access / build-elsewhere) are",
14285
+ "recorded **only** in the notes `## Action Items` table, which is",
14286
+ "the **system of record for human follow-ups** \u2014 no GitHub issue",
14287
+ "is filed for them. Consumers tune or disable the split via",
14288
+ "`AgentConfigOptions.meetings.actionItemFiling`."
14289
+ ].join("\n"),
14290
+ platforms: {
14291
+ cursor: { exclude: true }
14292
+ },
14293
+ tags: ["workflow"]
14063
14294
  }
14064
- }
14065
- }
14066
- return false;
14067
- }
14068
- function isPhaseLabelOwnedByExcluded(phaseLabel, excludedBundles) {
14069
- if (excludedBundles.length === 0) {
14070
- return false;
14071
- }
14072
- for (const bundleName of excludedBundles) {
14073
- const ownership = BUNDLE_OWNERSHIP[bundleName];
14074
- if (!ownership) {
14075
- continue;
14076
- }
14077
- for (const entry of ownership.phaseLabelPrefixes) {
14078
- if (entry.endsWith(":")) {
14079
- if (phaseLabel.startsWith(entry)) {
14080
- return true;
14081
- }
14082
- } else if (phaseLabel === entry) {
14083
- return true;
14295
+ ],
14296
+ skills: [processMeetingSkill],
14297
+ subAgents: [buildMeetingAnalystSubAgent(tier)],
14298
+ labels: [
14299
+ {
14300
+ name: "type:meeting-processing",
14301
+ color: "5319E7",
14302
+ description: "Meeting transcript processing pipeline"
14303
+ },
14304
+ {
14305
+ name: "meeting:extract",
14306
+ color: "C5DEF5",
14307
+ description: "Phase 1: raw extraction from a meeting transcript"
14308
+ },
14309
+ {
14310
+ name: "meeting:notes",
14311
+ color: "BFDADC",
14312
+ description: "Phase 2: curated notes derived from an extraction"
14313
+ },
14314
+ {
14315
+ name: "meeting:draft",
14316
+ color: "D4C5F9",
14317
+ description: "Phase 3: draft follow-up issues proposed from notes"
14318
+ },
14319
+ {
14320
+ name: "meeting:link",
14321
+ color: "FEF2C0",
14322
+ description: "Phase 4: linking/reconciling drafted issues with existing work"
14084
14323
  }
14085
- }
14086
- }
14087
- return false;
14088
- }
14089
- function isScheduledTaskOwnedByExcluded(taskId, excludedBundles) {
14090
- if (excludedBundles.length === 0) {
14091
- return false;
14092
- }
14093
- for (const bundleName of excludedBundles) {
14094
- const ownership = BUNDLE_OWNERSHIP[bundleName];
14095
- if (!ownership) {
14096
- continue;
14097
- }
14098
- if (ownership.scheduledTaskIds.includes(taskId)) {
14099
- return true;
14100
- }
14101
- }
14102
- return false;
14103
- }
14104
- function hasAnyDocsEmittingBundle(excludedBundles) {
14105
- for (const [bundleName, ownership] of Object.entries(BUNDLE_OWNERSHIP)) {
14106
- if (!ownership.emitsDocs) {
14107
- continue;
14108
- }
14109
- if (!excludedBundles.includes(bundleName)) {
14110
- return true;
14111
- }
14112
- }
14113
- return false;
14114
- }
14115
- function hasAnyDownstreamIssueKindBundle(excludedBundles) {
14116
- for (const [bundleName, ownership] of Object.entries(BUNDLE_OWNERSHIP)) {
14117
- if (!ownership.downstreamIssueKinds) {
14118
- continue;
14119
- }
14120
- if (!excludedBundles.includes(bundleName)) {
14121
- return true;
14122
- }
14123
- }
14124
- return false;
14125
- }
14126
- function findOwnersOfTypeLabel(typeLabel) {
14127
- const owners = [];
14128
- for (const [bundleName, ownership] of Object.entries(BUNDLE_OWNERSHIP)) {
14129
- if (ownership.typeLabels.includes(typeLabel)) {
14130
- owners.push(bundleName);
14131
- }
14132
- }
14133
- return owners;
14324
+ ]
14325
+ };
14134
14326
  }
14327
+ var meetingAnalysisBundle = buildMeetingAnalysisBundle();
14135
14328
 
14136
14329
  // src/agent/bundles/run-ratio.ts
14137
14330
  var DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO = 4;
@@ -19768,6 +19961,8 @@ function buildPeopleProfileAnalystSubAgent(paths, issueDefaults, tier) {
19768
19961
  ` - \`priority:${labelsForPhase(issueDefaults, "company:research").priority}\``,
19769
19962
  ` - \`status:${labelsForPhase(issueDefaults, "company:research").status}\``,
19770
19963
  "",
19964
+ ...renderIssueTypeAssignmentStep(),
19965
+ "",
19771
19966
  " The issue body must include:",
19772
19967
  " - A **discovery source** line naming this person profile",
19773
19968
  " (`Discovered while profiling: <person name>`)",
@@ -19788,6 +19983,8 @@ function buildPeopleProfileAnalystSubAgent(paths, issueDefaults, tier) {
19788
19983
  ` - \`priority:${labelsForPhase(issueDefaults, "software:research").priority}\``,
19789
19984
  ` - \`status:${labelsForPhase(issueDefaults, "software:research").status}\``,
19790
19985
  "",
19986
+ ...renderIssueTypeAssignmentStep(),
19987
+ "",
19791
19988
  " The issue body must include:",
19792
19989
  " - A **discovery source** line naming this person profile",
19793
19990
  " (`Discovered while profiling: <person name>`)",
@@ -20070,6 +20267,8 @@ function buildProfilePersonSkill(paths, issueDefaults) {
20070
20267
  ` \`priority:${labelsForPhase(issueDefaults, "people:research").priority}\`, and \`status:${labelsForPhase(issueDefaults, "people:research").status}\`. Body must include the`,
20071
20268
  " person's name, selected role, primary company, framing, and any",
20072
20269
  " overrides.",
20270
+ "",
20271
+ ...renderIssueTypeAssignmentStep(),
20073
20272
  "2. Execute Phase 1 (Research) of the people-profile-analyst agent.",
20074
20273
  "3. Phase 1 creates the `people:draft` issue. Phase 2 may create a",
20075
20274
  " `people:followup` issue. Each downstream issue declares its",
@@ -20161,6 +20360,8 @@ function buildRefreshPersonSkill(paths, issueDefaults) {
20161
20360
  "1. Create a `people:refresh` issue with `type:people-profile`,",
20162
20361
  ` \`priority:${labelsForPhase(issueDefaults, "people:refresh").priority}\`, and \`status:${labelsForPhase(issueDefaults, "people:refresh").status}\`. Body must include the`,
20163
20362
  " profile path and any overrides.",
20363
+ "",
20364
+ ...renderIssueTypeAssignmentStep(),
20164
20365
  "2. Execute Phase 4 (Refresh) of the people-profile-analyst agent.",
20165
20366
  "",
20166
20367
  "## Output",
@@ -23857,6 +24058,8 @@ function buildRegulatoryResearchAnalystSubAgent(paths, issueDefaults) {
23857
24058
  ` - \`priority:${labelsForPhase(issueDefaults, "company:research").priority}\``,
23858
24059
  ` - \`status:${labelsForPhase(issueDefaults, "company:research").status}\``,
23859
24060
  "",
24061
+ ...renderIssueTypeAssignmentStep(),
24062
+ "",
23860
24063
  "9. **Verify all handoffs were created** before committing.",
23861
24064
  "",
23862
24065
  "10. **Commit and push** the regulation page. Close the research",
@@ -23967,6 +24170,8 @@ function buildRegulatoryResearchAnalystSubAgent(paths, issueDefaults) {
23967
24170
  ` - \`priority:${labelsForPhase(issueDefaults, "req:scan").priority}\``,
23968
24171
  ` - \`status:${labelsForPhase(issueDefaults, "req:scan").status}\``,
23969
24172
  "",
24173
+ ...renderIssueTypeAssignmentStep(),
24174
+ "",
23970
24175
  " The body must also:",
23971
24176
  " - Identify the target category as **SEC** (security & compliance)",
23972
24177
  " in the body so the downstream writer phase picks up the right",
@@ -23986,6 +24191,8 @@ function buildRegulatoryResearchAnalystSubAgent(paths, issueDefaults) {
23986
24191
  ` - \`priority:${labelsForPhase(issueDefaults, "company:research").priority}\``,
23987
24192
  ` - \`status:${labelsForPhase(issueDefaults, "company:research").status}\``,
23988
24193
  "",
24194
+ ...renderIssueTypeAssignmentStep(),
24195
+ "",
23989
24196
  "6. **Create `people:research` issues** for notable regulatory",
23990
24197
  " leaders (commissioners, agency directors, lead policy authors)",
23991
24198
  " mentioned on the regulation page. Keep this list small \u2014 at most",
@@ -23997,6 +24204,8 @@ function buildRegulatoryResearchAnalystSubAgent(paths, issueDefaults) {
23997
24204
  ` - \`priority:${labelsForPhase(issueDefaults, "people:research").priority}\``,
23998
24205
  ` - \`status:${labelsForPhase(issueDefaults, "people:research").status}\``,
23999
24206
  "",
24207
+ ...renderIssueTypeAssignmentStep(),
24208
+ "",
24000
24209
  "7. **Commit and push** the updated regulation page. Close the",
24001
24210
  " impact issue.",
24002
24211
  "",
@@ -24164,6 +24373,8 @@ function buildScanRegulatoryLandscapeSkill(paths, issueDefaults) {
24164
24373
  ` \`priority:${labelsForPhase(issueDefaults, "regulatory:scan").priority}\`, and \`status:${labelsForPhase(issueDefaults, "regulatory:scan").status}\`. The body must include the`,
24165
24374
  " scope slug, the scope type, and any known regulations to include",
24166
24375
  " or exclude.",
24376
+ "",
24377
+ ...renderIssueTypeAssignmentStep(),
24167
24378
  "2. Execute Phase 1 (Scan) of the regulatory-research-analyst agent.",
24168
24379
  "3. Phase 1 writes the scan report and creates one",
24169
24380
  " `regulatory:research` issue per prioritized regulation.",
@@ -24240,6 +24451,8 @@ function buildResearchRegulationSkill(paths, issueDefaults) {
24240
24451
  ` \`type:regulatory-research\`, \`priority:${labelsForPhase(issueDefaults, "regulatory:research").priority}\`, and`,
24241
24452
  ` \`status:${labelsForPhase(issueDefaults, "regulatory:research").status}\`. The body must include the scope slug and the`,
24242
24453
  " regulation slug.",
24454
+ "",
24455
+ ...renderIssueTypeAssignmentStep(),
24243
24456
  "2. Execute Phase 2 (Research) of the regulatory-research-analyst",
24244
24457
  " agent. The agent runs 6\u201312 targeted web searches and populates",
24245
24458
  " the regulation page.",
@@ -24307,6 +24520,8 @@ function buildImpactRegulationSkill(paths, issueDefaults) {
24307
24520
  ` \`status:${labelsForPhase(issueDefaults, "regulatory:impact").status}\` (or \`status:blocked\` when still dependent on a`,
24308
24521
  " Phase 2 research issue). Body must include the regulation page",
24309
24522
  " path.",
24523
+ "",
24524
+ ...renderIssueTypeAssignmentStep(),
24310
24525
  "2. Execute Phase 3 (Impact) of the regulatory-research-analyst",
24311
24526
  " agent. No web searches \u2014 pure synthesis from the regulation",
24312
24527
  " page and project context.",
@@ -27142,6 +27357,8 @@ function buildWriteRequirementSkill(paths, issueDefaults) {
27142
27357
  " Body must include the category, tier, output path, and a pointer",
27143
27358
  " to the upstream proposal (or a direct user description if no",
27144
27359
  " proposals file exists).",
27360
+ "",
27361
+ ...renderIssueTypeAssignmentStep(),
27145
27362
  "2. Execute the write phase of the requirements-writer agent.",
27146
27363
  "3. The agent writes one requirement document, updates the category",
27147
27364
  " index, opens a PR, and closes the issue.",
@@ -27731,6 +27948,8 @@ function buildRequirementsAnalystSubAgent(paths, issueDefaults) {
27731
27948
  ` \`--label "tier:<tier-slug>"\`, \`--label "status:${labelsForPhase(issueDefaults, "req:write").status}"\`, and`,
27732
27949
  ` \`--label "priority:${labelsForPhase(issueDefaults, "req:write").priority}"\`.`,
27733
27950
  "",
27951
+ ...renderIssueTypeAssignmentStep(),
27952
+ "",
27734
27953
  " The body must carry the three writer-required fields in an",
27735
27954
  " `## Objective` block, written as bold-prefixed lines so the",
27736
27955
  " writer's intake parser can pull them out, plus the reserved-ID",
@@ -27877,6 +28096,8 @@ function buildScanRequirementsSkill(issueDefaults) {
27877
28096
  "",
27878
28097
  `1. Create a \`req:scan\` issue with \`type:requirement\`, \`priority:${labelsForPhase(issueDefaults, "req:scan").priority}\`,`,
27879
28098
  ` and \`status:${labelsForPhase(issueDefaults, "req:scan").status}\`. Body must list the files to read and the scan scope.`,
28099
+ "",
28100
+ ...renderIssueTypeAssignmentStep(),
27880
28101
  "2. Execute Phase 1 (Scan) of the requirements-analyst agent.",
27881
28102
  "3. If gaps are found, a `req:draft-trace` issue is created automatically.",
27882
28103
  "",
@@ -28925,6 +29146,7 @@ function buildRequirementsReviewerSubAgent(paths, issueDefaults) {
28925
29146
  "- Labels: `type:requirement`, `req:write`, the matching",
28926
29147
  " `tier:*` label derived from the referencing document's Tier,",
28927
29148
  ` \`priority:${labelsForPhase(issueDefaults, "req:write").priority}\`, \`status:${labelsForPhase(issueDefaults, "req:write").status}\`.`,
29149
+ ...renderIssueTypeAssignmentStep({ indent: "", bullet: true }),
28928
29150
  "- If any of Category / Tier / Output Path cannot be derived from",
28929
29151
  " the referencing document, open the follow-up with",
28930
29152
  " `status:needs-attention` and a `Missing:` line, per the same",
@@ -29080,6 +29302,8 @@ function buildReviewRequirementsSkill(paths, issueDefaults) {
29080
29302
  ` \`priority:${labelsForPhase(issueDefaults, "req:review").priority}\`, and \`status:${labelsForPhase(issueDefaults, "req:review").status}\`. Body must include the`,
29081
29303
  " scope, the requirements root (or accept the default), and the",
29082
29304
  " output path.",
29305
+ "",
29306
+ ...renderIssueTypeAssignmentStep(),
29083
29307
  "2. Execute the review phase of the requirements-reviewer agent.",
29084
29308
  "3. The agent runs the 12-check audit, writes a structured report",
29085
29309
  " grouped by Critical / Warning / Info (plus a separate",
@@ -29182,6 +29406,8 @@ function buildDeprecateRequirementSkill(_paths, issueDefaults) {
29182
29406
  ` \`priority:${labelsForPhase(issueDefaults, "req:deprecate").priority}\`, and \`status:${labelsForPhase(issueDefaults, "req:deprecate").status}\`. Body must include the`,
29183
29407
  " Targets, Transition, and Reason sections above (optionally",
29184
29408
  " `Pre-approved: yes`).",
29409
+ "",
29410
+ ...renderIssueTypeAssignmentStep(),
29185
29411
  "2. Execute the deprecation phase of the requirements-reviewer",
29186
29412
  " agent.",
29187
29413
  "3. The agent verifies inputs, pauses for human confirmation (or",
@@ -29756,6 +29982,8 @@ function buildResearchSkill(paths, issueDefaults) {
29756
29982
  "1. Create a `research:scope` issue with `type:research`,",
29757
29983
  ` \`priority:${labelsForPhase(issueDefaults, "research:scope").priority}\`, and \`status:${labelsForPhase(issueDefaults, "research:scope").status}\`. Body must include the`,
29758
29984
  " verbatim question, authorized sources, and any overrides.",
29985
+ "",
29986
+ ...renderIssueTypeAssignmentStep(),
29759
29987
  "2. Execute Phase 1 (Scope) of the research-analyst agent.",
29760
29988
  "3. Phase 1 creates `research:slice` issues (one per slice) and a",
29761
29989
  " single `research:verify` issue. Each downstream issue declares",
@@ -30571,6 +30799,8 @@ function buildSoftwareProfileAnalystSubAgent(paths, issueDefaults, tier) {
30571
30799
  ` - \`priority:${labelsForPhase(issueDefaults, "bcm:outline").priority}\``,
30572
30800
  ` - \`status:${labelsForPhase(issueDefaults, "bcm:outline").status}\``,
30573
30801
  "",
30802
+ ...renderIssueTypeAssignmentStep(),
30803
+ "",
30574
30804
  " The issue body must include:",
30575
30805
  " - A **discovery source** line naming this software profile",
30576
30806
  " (`Discovered while mapping: <product name>`)",
@@ -30639,6 +30869,8 @@ function buildSoftwareProfileAnalystSubAgent(paths, issueDefaults, tier) {
30639
30869
  " `software:research` phase with `type:software-profile`,",
30640
30870
  ` \`priority:${labelsForPhase(issueDefaults, "software:research").priority}\`, and \`status:${labelsForPhase(issueDefaults, "software:research").status}\`.`,
30641
30871
  "",
30872
+ ...renderIssueTypeAssignmentStep(),
30873
+ "",
30642
30874
  "3. **Enqueue a `company:research` issue for the vendor company** if",
30643
30875
  " no matching profile already exists under `<COMPANY_PROFILES_DIR>/`.",
30644
30876
  " The vendor (the company that builds or maintains the product)",
@@ -30651,6 +30883,8 @@ function buildSoftwareProfileAnalystSubAgent(paths, issueDefaults, tier) {
30651
30883
  ` - \`priority:${labelsForPhase(issueDefaults, "company:research").priority}\``,
30652
30884
  ` - \`status:${labelsForPhase(issueDefaults, "company:research").status}\``,
30653
30885
  "",
30886
+ ...renderIssueTypeAssignmentStep(),
30887
+ "",
30654
30888
  " The issue body must include:",
30655
30889
  " - A **discovery source** line naming this software profile",
30656
30890
  " (`Discovered while profiling: <product name>`)",
@@ -30670,6 +30904,8 @@ function buildSoftwareProfileAnalystSubAgent(paths, issueDefaults, tier) {
30670
30904
  ` - \`priority:${labelsForPhase(issueDefaults, "people:research").priority}\``,
30671
30905
  ` - \`status:${labelsForPhase(issueDefaults, "people:research").status}\``,
30672
30906
  "",
30907
+ ...renderIssueTypeAssignmentStep(),
30908
+ "",
30673
30909
  " The issue body must include:",
30674
30910
  " - A **discovery source** line naming this software profile",
30675
30911
  " (`Discovered while profiling: <product name>`)",
@@ -30860,6 +31096,8 @@ function buildProfileSoftwareSkill(paths, issueDefaults) {
30860
31096
  "1. Create a `software:research` issue with `type:software-profile`,",
30861
31097
  ` \`priority:${labelsForPhase(issueDefaults, "software:research").priority}\`, and \`status:${labelsForPhase(issueDefaults, "software:research").status}\`. Body must include the`,
30862
31098
  " product name, selected type, framing, and any overrides.",
31099
+ "",
31100
+ ...renderIssueTypeAssignmentStep(),
30863
31101
  "2. Execute Phase 1 (Research) of the software-profile-analyst",
30864
31102
  " agent.",
30865
31103
  "3. Phase 1 creates the `software:profile` issue. Phase 2 creates",
@@ -30948,6 +31186,8 @@ function buildMapSoftwareSkill(paths, issueDefaults) {
30948
31186
  "1. Create a `software:map` issue with `type:software-profile`,",
30949
31187
  ` \`priority:${labelsForPhase(issueDefaults, "software:map").priority}\`, and \`status:${labelsForPhase(issueDefaults, "software:map").status}\`. Body must include the`,
30950
31188
  " profile path and any overrides.",
31189
+ "",
31190
+ ...renderIssueTypeAssignmentStep(),
30951
31191
  "2. Execute Phase 4 (Map) of the software-profile-analyst agent.",
30952
31192
  "3. Phase 4 writes the `## Capability Mapping` section, enqueues",
30953
31193
  " `bcm:outline` issues for unmapped features above the threshold,",
@@ -31560,6 +31800,8 @@ function buildStandardsResearchAnalystSubAgent(paths, issueDefaults) {
31560
31800
  ` - \`priority:${labelsForPhase(issueDefaults, "company:research").priority}\``,
31561
31801
  ` - \`status:${labelsForPhase(issueDefaults, "company:research").status}\``,
31562
31802
  "",
31803
+ ...renderIssueTypeAssignmentStep(),
31804
+ "",
31563
31805
  "6. **Create `people:research` issues** for every entry in *People of",
31564
31806
  " Interest* that does not already have a canonical person profile",
31565
31807
  " or an open `people:research` issue. The `people-profile` bundle",
@@ -31571,6 +31813,8 @@ function buildStandardsResearchAnalystSubAgent(paths, issueDefaults) {
31571
31813
  ` - \`priority:${labelsForPhase(issueDefaults, "people:research").priority}\``,
31572
31814
  ` - \`status:${labelsForPhase(issueDefaults, "people:research").status}\``,
31573
31815
  "",
31816
+ ...renderIssueTypeAssignmentStep(),
31817
+ "",
31574
31818
  "7. **Verify all handoffs were created** before committing. For each",
31575
31819
  " entity in both tables, confirm an existing profile or a fresh",
31576
31820
  " handoff issue exists. Do not commit with missing handoffs.",
@@ -31864,6 +32108,8 @@ function buildStandardsResearchAnalystSubAgent(paths, issueDefaults) {
31864
32108
  ` - \`priority:${labelsForPhase(issueDefaults, "company:research").priority}\``,
31865
32109
  ` - \`status:${labelsForPhase(issueDefaults, "company:research").status}\``,
31866
32110
  "",
32111
+ ...renderIssueTypeAssignmentStep(),
32112
+ "",
31867
32113
  "6. **Hand off canonical people profiles** by creating one",
31868
32114
  " `people:research` issue per leadership entry that does not",
31869
32115
  " already have a canonical profile or an open `people:research`",
@@ -31875,6 +32121,8 @@ function buildStandardsResearchAnalystSubAgent(paths, issueDefaults) {
31875
32121
  ` - \`priority:${labelsForPhase(issueDefaults, "people:research").priority}\``,
31876
32122
  ` - \`status:${labelsForPhase(issueDefaults, "people:research").status}\``,
31877
32123
  "",
32124
+ ...renderIssueTypeAssignmentStep(),
32125
+ "",
31878
32126
  "7. **Commit and push** the organization page. Close the",
31879
32127
  " organizations issue.",
31880
32128
  "",
@@ -32049,6 +32297,8 @@ function buildScopeStandardsResearchSkill(paths, issueDefaults) {
32049
32297
  ` \`priority:${labelsForPhase(issueDefaults, "standards:scope").priority}\`, and \`status:${labelsForPhase(issueDefaults, "standards:scope").status}\`. The body must include the`,
32050
32298
  " standard slug, the list of target versions (or a request to",
32051
32299
  " enumerate), and any known governance bodies.",
32300
+ "",
32301
+ ...renderIssueTypeAssignmentStep(),
32052
32302
  "2. Execute Phase 1 (Scope) of the standards-research-analyst agent.",
32053
32303
  "3. Phase 1 writes the query plan and creates one `standards:research`",
32054
32304
  " issue per target version, one `standards:compare` issue (blocked",
@@ -32113,6 +32363,8 @@ function buildResearchStandardSkill(paths, issueDefaults) {
32113
32363
  "1. Create a `standards:research` issue with",
32114
32364
  ` \`type:standards-research\`, \`priority:${labelsForPhase(issueDefaults, "standards:research").priority}\`, and \`status:${labelsForPhase(issueDefaults, "standards:research").status}\`.`,
32115
32365
  " The body must include the standard slug and the version slug.",
32366
+ "",
32367
+ ...renderIssueTypeAssignmentStep(),
32116
32368
  "2. Execute Phase 2 (Research) of the standards-research-analyst",
32117
32369
  " agent. The agent runs 6\u201310 targeted web searches and populates",
32118
32370
  " the version page.",
@@ -32186,6 +32438,8 @@ function buildCompareStandardsSkill(paths, issueDefaults) {
32186
32438
  ` \`type:standards-research\`, \`priority:${labelsForPhase(issueDefaults, "standards:compare").priority}\`, and \`status:${labelsForPhase(issueDefaults, "standards:compare").status}\`.`,
32187
32439
  " Body must include the standard slug and the list of version",
32188
32440
  " pages under comparison.",
32441
+ "",
32442
+ ...renderIssueTypeAssignmentStep(),
32189
32443
  "2. Execute Phase 3 (Compare) of the standards-research-analyst",
32190
32444
  " agent. No web searches \u2014 pure synthesis from the version pages.",
32191
32445
  "",
@@ -32254,6 +32508,8 @@ function buildExtensionStandardSkill(paths, issueDefaults) {
32254
32508
  " The body must include the standard slug, the entity slug, the",
32255
32509
  " closest resource, and a link to the source BCM or canvas",
32256
32510
  " document.",
32511
+ "",
32512
+ ...renderIssueTypeAssignmentStep(),
32257
32513
  "2. Execute Phase 4 (Extension) of the standards-research-analyst",
32258
32514
  " agent. The agent runs 4\u20138 targeted web searches against",
32259
32515
  " community registries, implementation guides, and working-group",
@@ -32319,6 +32575,8 @@ function buildOrganizationsStandardSkill(paths, issueDefaults) {
32319
32575
  ` \`type:standards-research\`, \`priority:${labelsForPhase(issueDefaults, "standards:organizations").priority}\`, and \`status:${labelsForPhase(issueDefaults, "standards:organizations").status}\`.`,
32320
32576
  " Body must include the standard slug, the organization slug, and",
32321
32577
  " the organization's governance role.",
32578
+ "",
32579
+ ...renderIssueTypeAssignmentStep(),
32322
32580
  "2. Execute Phase 5 (Organizations) of the",
32323
32581
  " standards-research-analyst agent. The agent runs 4\u20138 targeted",
32324
32582
  " web searches.",
@@ -32628,6 +32886,14 @@ var UPSTREAM_CONFIGULATOR_DOCS_SUMMARY = [
32628
32886
  "",
32629
32887
  "Pick the `type:*` and matching prefix that describes the work (`feat:`, `fix:`, `chore:`, `docs:`, `refactor:`). Set `priority:*` from the urgency and use `status:blocked` instead of `status:ready` if the issue depends on another open issue.",
32630
32888
  "",
32889
+ "Then set the new issue's **GitHub issue type** \u2014 a separate field from the `type:*` label, which `gh issue create` cannot set. Derive it from the title prefix (`epic:` \u2192 Epic, `feat:` \u2192 Feature, `fix:` \u2192 Bug, everything else \u2192 Task). The local `.claude/procedures/set-issue-type.sh` helper resolves owner/repo from the **current** checkout, so a cross-repo filing needs the `updateIssueIssueType` GraphQL flow with an explicit owner/repo:",
32890
+ "",
32891
+ "```sh",
32892
+ "gh api graphql -f query='query($owner:String!,$repo:String!){repository(owner:$owner,name:$repo){issueTypes(first:50){nodes{id name}}}}' -f owner=codedrifters -f repo=packages",
32893
+ "gh issue view <issue-number> --repo codedrifters/packages --json id --jq '.id'",
32894
+ "gh api graphql -f query='mutation($issueId:ID!,$typeId:ID!){updateIssueIssueType(input:{issueId:$issueId,issueTypeId:$typeId}){issue{number issueType{name}}}}' -f issueId=<issue-node-id> -f typeId=<issue-type-node-id>",
32895
+ "```",
32896
+ "",
32631
32897
  "See `.claude/rules/upstream-configulator-docs-detail.md` for the full reference \u2014 extended `gh` recipes, common pitfalls (rate limits, base64 decoding, search vs. browse), and a complete issue-creation walkthrough."
32632
32898
  ].join("\n");
32633
32899
  var UPSTREAM_CONFIGULATOR_DOCS_DETAIL = [
@@ -32736,6 +33002,16 @@ var UPSTREAM_CONFIGULATOR_DOCS_DETAIL = [
32736
33002
  "- `priority:*` \u2014 pick exactly one of `priority:critical`, `priority:high`, `priority:medium`, `priority:low`, `priority:trivial`. Default to `priority:medium` when nothing in the request signals urgency. Use `priority:high` for blockers and `priority:critical` only for outages or release-blocking work.",
32737
33003
  "- `status:*` \u2014 `status:ready` for self-contained issues, `status:blocked` when the issue declares `Depends on: #<n>` against another open issue.",
32738
33004
  "",
33005
+ "### GitHub issue type",
33006
+ "",
33007
+ "The GitHub **issue type** (Epic / Feature / Bug / Task) is a separate field from the `type:*` label, and `gh issue create` cannot set it. Derive it from the title prefix \u2014 `epic:` \u2192 Epic, `feat:` \u2192 Feature, `fix:` \u2192 Bug, everything else \u2192 Task \u2014 and apply it in a follow-up call. The bundled `.claude/procedures/set-issue-type.sh` helper resolves owner/repo from the **current** checkout, so a cross-repo filing against `codedrifters/packages` uses the `updateIssueIssueType` GraphQL flow with an explicit owner/repo instead:",
33008
+ "",
33009
+ "```sh",
33010
+ "gh api graphql -f query='query($owner:String!,$repo:String!){repository(owner:$owner,name:$repo){issueTypes(first:50){nodes{id name}}}}' -f owner=codedrifters -f repo=packages",
33011
+ "gh issue view <issue-number> --repo codedrifters/packages --json id --jq '.id'",
33012
+ "gh api graphql -f query='mutation($issueId:ID!,$typeId:ID!){updateIssueIssueType(input:{issueId:$issueId,issueTypeId:$typeId}){issue{number issueType{name}}}}' -f issueId=<issue-node-id> -f typeId=<issue-type-node-id>",
33013
+ "```",
33014
+ "",
32739
33015
  "### Worked example",
32740
33016
  "",
32741
33017
  "Suppose configulator's `software-profile-analyst` agent prompt is missing a step you need. Instead of forking the prompt locally:",
@@ -41535,6 +41811,7 @@ export const collections = {
41535
41811
  DEFAULT_DECOMPOSITION_TEMPLATE,
41536
41812
  DEFAULT_DISPATCH_MODEL,
41537
41813
  DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO,
41814
+ DEFAULT_GITHUB_ISSUE_TYPE,
41538
41815
  DEFAULT_HOUSEKEEPING_MODEL,
41539
41816
  DEFAULT_ISSUE_PRIORITY,
41540
41817
  DEFAULT_ISSUE_STATUS,
@@ -41581,6 +41858,8 @@ export const collections = {
41581
41858
  DEFAULT_UNBLOCK_DEPENDENTS_ENABLED,
41582
41859
  DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED,
41583
41860
  DOCS_SYNC_AUDIT_SCHEMA_VERSION,
41861
+ GITHUB_ISSUE_TYPES,
41862
+ GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX,
41584
41863
  JsiiFaker,
41585
41864
  LAYOUT_ENFORCEMENT,
41586
41865
  LAYOUT_ROOT_BY_PROJECT_TYPE,
@@ -41605,6 +41884,7 @@ export const collections = {
41605
41884
  ResetTask,
41606
41885
  SCHEDULED_TASK_MODEL_VALUES,
41607
41886
  SCOPE_CLASS_VALUES,
41887
+ SET_ISSUE_TYPE_HELPER_PATH,
41608
41888
  SHARED_EDITING_CONFLICT_STRATEGY_VALUES,
41609
41889
  STARLIGHT_ROLE,
41610
41890
  SUPPRESSED_WORKFLOW_RULE_NAMES,
@@ -41690,6 +41970,7 @@ export const collections = {
41690
41970
  formatLayoutViolation,
41691
41971
  formatStarlightSingletonViolation,
41692
41972
  getLatestEligibleVersion,
41973
+ githubIssueTypeForTitle,
41693
41974
  githubWorkflowBundle,
41694
41975
  hasAnyDocsEmittingBundle,
41695
41976
  hasAnyDownstreamIssueKindBundle,
@@ -41747,10 +42028,14 @@ export const collections = {
41747
42028
  renderCustomDocSections,
41748
42029
  renderExtractApiProcedure,
41749
42030
  renderFocusSection,
42031
+ renderGithubIssueTypeSection,
42032
+ renderGithubIssueTypeSectionLines,
41750
42033
  renderIssueTemplatesBundleHook,
41751
42034
  renderIssueTemplatesCheckerScript,
41752
42035
  renderIssueTemplatesRuleContent,
41753
42036
  renderIssueTemplatesStarterPage,
42037
+ renderIssueTypeAssignmentBlanket,
42038
+ renderIssueTypeAssignmentStep,
41754
42039
  renderMeetingTypesSection,
41755
42040
  renderNextRequirementIdProcedure,
41756
42041
  renderPhaseTypeInvariantSection,
@@ -41767,6 +42052,7 @@ export const collections = {
41767
42052
  renderScheduledTasksSection,
41768
42053
  renderScopeGateSection,
41769
42054
  renderScopeGateShellHelpers,
42055
+ renderSetIssueTypeFallbackLines,
41770
42056
  renderSharedEditingBundleHook,
41771
42057
  renderSharedEditingHelperScript,
41772
42058
  renderSharedEditingRuleContent,
@@ -41777,6 +42063,7 @@ export const collections = {
41777
42063
  renderStripToolArtifactTagsProcedure,
41778
42064
  renderTemporalFramingCheckerScript,
41779
42065
  renderTemporalFramingRuleContent,
42066
+ renderTitlePrefixTypeBullets,
41780
42067
  renderUnblockDependentsScript,
41781
42068
  renderUnblockDependentsSection,
41782
42069
  requirementsAnalystBundle,