Skip to content

Commit 948e249

Browse files
committed
chore(config): remove unused review.labeling_rules field
The deterministic label-suggestion parser (review.labeling_rules, #2045) was fully parsed and validated but never wired to any label application path, so it was dead config. Removed the field, its type, and the parser/serializer support from the engine package, along with the corresponding docs and tests. Closes #4146
1 parent cbe243c commit 948e249

9 files changed

Lines changed: 7 additions & 231 deletions

File tree

.gittensory.yml.example

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -544,20 +544,6 @@ review:
544544
# (CI green, gate passing, mergeable-clean, valid linked issue). SURFACE ONLY — never changes the decision.
545545
# auto_merge_summary: false
546546

547-
# Deterministic label suggestions (#2045). Each rule SUGGESTS a non-scoring label when a PR matches ALL of the
548-
# `when` criteria it sets (at least one is required): when_paths (any changed path matches a glob), title_contains,
549-
# description_contains (both case-insensitive). Suggestions are advisory; they are auto-applied only when the repo's
550-
# autoLabelEnabled is on. Reserved `gittensor:` labels (scoring/type) are refused. Empty/unset ⇒ no suggestions.
551-
labeling_rules:
552-
- label: area:docs
553-
when_paths:
554-
- "docs/**"
555-
- "**/*.md"
556-
- label: needs:migration
557-
when_paths:
558-
- "migrations/**"
559-
description_contains: schema
560-
561547
settings:
562548
# Who receives the public PR comment.
563549
# off | detected_contributors_only | all_prs. Default: detected_contributors_only.
@@ -1008,13 +994,6 @@ settings:
1008994
# enrichment:
1009995
# deep-nesting: true
1010996
# error-swallow: false
1011-
# # Deterministic {label, when} rules that SUGGEST a non-scoring label when a PR's changed paths/
1012-
# # title/description match ALL of the rule's `when` criteria. Auto-applied only when the repo's
1013-
# # autoLabelEnabled is set. Reserved gittensor:* labels are refused at parse. Empty ⇒ no suggestion.
1014-
# # (This block is also active, uncommented, near the top of the live `review:` section above.)
1015-
# labeling_rules:
1016-
# - label: area:docs
1017-
# when_paths: ["docs/**", "**/*.md"]
1018997
# # Per-repo self-host reviewer model/effort overrides (claude-code / codex). Self-host only; a hosted
1019998
# # (Workers-AI) repo ignores this entirely. All-null/default ⇒ the operator's global env vars apply.
1020999
# ai_model:

config/examples/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ From highest to lowest priority:
4545
3. **Private shared base** (`${GITTENSORY_REPO_CONFIG_DIR}/_shared/.gittensory.yml`, #1959) — the
4646
lowest-priority private layer, deep-merged under both **1** and **2**. An operator running many
4747
repos writes a house review policy (e.g. a default `review.tone`, `path_filters`, or
48-
`labeling_rules`) here **once** instead of copy-pasting it into every repo's per-repo file or
48+
`exclude_paths`) here **once** instead of copy-pasting it into every repo's per-repo file or
4949
the global default. `.yaml`/`.json` are accepted, same as every other candidate. Absent (the
5050
default, common case) ⇒ byte-identical behavior to the pre-#1959 2-layer chain.
5151
4. When **none** of the three private layers above exists, the loader falls back to the **public
@@ -144,7 +144,7 @@ settings:
144144

145145
An operator running **many** repos through the same self-host instance can express one house
146146
review policy — e.g. a default `review.tone`, a baseline `path_filters`/`wantedPaths` set, or
147-
common `labeling_rules` — **once**, instead of copy-pasting it into every repo's per-repo file or
147+
common `exclude_paths` — **once**, instead of copy-pasting it into every repo's per-repo file or
148148
even the global default. That policy lives at:
149149

150150
```

config/examples/gittensory.full.yml

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -557,20 +557,6 @@ review:
557557
# (CI green, gate passing, mergeable-clean, valid linked issue). SURFACE ONLY — never changes the decision.
558558
# auto_merge_summary: false
559559

560-
# Deterministic label suggestions (#2045). Each rule SUGGESTS a non-scoring label when a PR matches ALL of the
561-
# `when` criteria it sets (at least one is required): when_paths (any changed path matches a glob), title_contains,
562-
# description_contains (both case-insensitive). Suggestions are advisory; they are auto-applied only when the repo's
563-
# autoLabelEnabled is on. Reserved `gittensor:` labels (scoring/type) are refused. Empty/unset ⇒ no suggestions.
564-
labeling_rules:
565-
- label: area:docs
566-
when_paths:
567-
- "docs/**"
568-
- "**/*.md"
569-
- label: needs:migration
570-
when_paths:
571-
- "migrations/**"
572-
description_contains: schema
573-
574560
settings:
575561
# Who receives the public PR comment.
576562
# off | detected_contributors_only | all_prs. Default: detected_contributors_only.
@@ -1021,13 +1007,6 @@ settings:
10211007
# enrichment:
10221008
# deep-nesting: true
10231009
# error-swallow: false
1024-
# # Deterministic {label, when} rules that SUGGEST a non-scoring label when a PR's changed paths/
1025-
# # title/description match ALL of the rule's `when` criteria. Auto-applied only when the repo's
1026-
# # autoLabelEnabled is set. Reserved gittensor:* labels are refused at parse. Empty ⇒ no suggestion.
1027-
# # (This block is also active, uncommented, near the top of the live `review:` section above.)
1028-
# labeling_rules:
1029-
# - label: area:docs
1030-
# when_paths: ["docs/**", "**/*.md"]
10311010
# # Per-repo self-host reviewer model/effort overrides (claude-code / codex). Self-host only; a hosted
10321011
# # (Workers-AI) repo ignores this entirely. All-null/default ⇒ the operator's global env vars apply.
10331012
# ai_model:

packages/gittensory-engine/src/focus-manifest.ts

Lines changed: 3 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -621,11 +621,6 @@ export type FocusManifestReviewConfig = {
621621
/** `review.auto_review`: deterministic eligibility filters that skip the AI review (never a gate failure).
622622
* Empty/default ⇒ every PR is reviewed (byte-identical). (#1954 / #2038–#2041) */
623623
autoReview: AutoReviewConfig;
624-
/** `review.labeling_rules`: deterministic `{label, when}` rules that SUGGEST a non-scoring label when a PR's
625-
* changed paths / title / description match. Surfaced as advisory suggestions, and auto-applied only when the
626-
* repo's `autoLabelEnabled` is set. Reserved `gittensor:` labels are refused at parse. Empty (default) ⇒ no
627-
* suggestion (byte-identical). (#2045, part of #1959) */
628-
labelingRules: LabelingRule[];
629624
/** `review.ai_model`: per-repo self-host reviewer model/effort overrides (claude-code / codex). Self-host only
630625
* — a hosted (Workers-AI) repo ignores this entirely. All-null (default, absent) ⇒ the operator's global
631626
* CLAUDE_AI_MODEL/CLAUDE_AI_EFFORT/CODEX_AI_MODEL/CODEX_AI_EFFORT env vars apply unchanged (byte-identical).
@@ -660,15 +655,6 @@ export type CommentVerbosity = (typeof COMMENT_VERBOSITY_LEVELS)[number];
660655
export const E2E_TEST_DELIVERY_MODES = ["comment", "commit"] as const;
661656
export type E2eTestDeliveryMode = (typeof E2E_TEST_DELIVERY_MODES)[number];
662657

663-
/** One `review.labeling_rules[]` entry: a non-reserved `label` plus the deterministic `when` criteria that must ALL
664-
* match for it to fire. A rule always has at least one criterion (enforced at parse). */
665-
export type LabelingRule = {
666-
label: string;
667-
whenPaths: string[];
668-
titleContains: string | null;
669-
descriptionContains: string | null;
670-
};
671-
672658
/** `review.auto_review.cadence` (#one-shot-review-cadence). `one_shot` = the AI-generated content (main review,
673659
* slop advisory, linked-issue satisfaction) is produced once per PR and never automatically regenerated
674660
* afterward — not on a new push, not on CI-check completion, not on a scheduled sweep tick; only an explicit
@@ -1043,7 +1029,7 @@ const EMPTY_MANIFEST: FocusManifest = {
10431029
publicNotes: [],
10441030
gate: { ...EMPTY_GATE_CONFIG },
10451031
settings: {},
1046-
review: { present: false, footerText: null, note: null, fields: {}, enrichmentAnalyzers: {}, profile: null, tone: null, securityFocus: null, inlineComments: null, fixHandoff: null, autoMergeSummary: null, suggestions: null, changedFilesSummary: null, effortScore: null, impactMap: null, cultureProfile: null, selftune: null, reviewMemory: null, findingCategories: null, inlineCommentsPerCategory: null, minFindingSeverity: null, maxFindings: { ...EMPTY_MAX_FINDINGS_CONFIG }, commentVerbosity: null, e2eTestDelivery: null, e2eTestAutoTrigger: null, pathInstructions: [], instructions: null, excludePaths: [], pathFilters: [], preMergeChecks: [], autoReview: { ...EMPTY_AUTO_REVIEW_CONFIG }, labelingRules: [], aiModel: { ...EMPTY_SELF_HOST_AI_MODEL_CONFIG }, visual: { ...EMPTY_VISUAL_CONFIG }, linkedIssueSatisfaction: null, sharedConfigSource: null },
1032+
review: { present: false, footerText: null, note: null, fields: {}, enrichmentAnalyzers: {}, profile: null, tone: null, securityFocus: null, inlineComments: null, fixHandoff: null, autoMergeSummary: null, suggestions: null, changedFilesSummary: null, effortScore: null, impactMap: null, cultureProfile: null, selftune: null, reviewMemory: null, findingCategories: null, inlineCommentsPerCategory: null, minFindingSeverity: null, maxFindings: { ...EMPTY_MAX_FINDINGS_CONFIG }, commentVerbosity: null, e2eTestDelivery: null, e2eTestAutoTrigger: null, pathInstructions: [], instructions: null, excludePaths: [], pathFilters: [], preMergeChecks: [], autoReview: { ...EMPTY_AUTO_REVIEW_CONFIG }, aiModel: { ...EMPTY_SELF_HOST_AI_MODEL_CONFIG }, visual: { ...EMPTY_VISUAL_CONFIG }, linkedIssueSatisfaction: null, sharedConfigSource: null },
10471033
features: { ...EMPTY_FEATURES_CONFIG },
10481034
contentLane: { ...EMPTY_CONTENT_LANE_CONFIG },
10491035
repoDocGeneration: { ...EMPTY_REPO_DOC_GENERATION_CONFIG },
@@ -1074,7 +1060,7 @@ function emptyManifest(source: FocusManifestSource, warnings: string[] = []): Fo
10741060
warnings,
10751061
gate: { ...EMPTY_GATE_CONFIG },
10761062
settings: {},
1077-
review: { present: false, footerText: null, note: null, fields: {}, enrichmentAnalyzers: {}, profile: null, tone: null, securityFocus: null, inlineComments: null, fixHandoff: null, autoMergeSummary: null, suggestions: null, changedFilesSummary: null, effortScore: null, impactMap: null, cultureProfile: null, selftune: null, reviewMemory: null, findingCategories: null, inlineCommentsPerCategory: null, minFindingSeverity: null, maxFindings: { ...EMPTY_MAX_FINDINGS_CONFIG }, commentVerbosity: null, e2eTestDelivery: null, e2eTestAutoTrigger: null, pathInstructions: [], instructions: null, excludePaths: [], pathFilters: [], preMergeChecks: [], autoReview: { ...EMPTY_AUTO_REVIEW_CONFIG }, labelingRules: [], aiModel: { ...EMPTY_SELF_HOST_AI_MODEL_CONFIG }, visual: { ...EMPTY_VISUAL_CONFIG }, linkedIssueSatisfaction: null, sharedConfigSource: null },
1063+
review: { present: false, footerText: null, note: null, fields: {}, enrichmentAnalyzers: {}, profile: null, tone: null, securityFocus: null, inlineComments: null, fixHandoff: null, autoMergeSummary: null, suggestions: null, changedFilesSummary: null, effortScore: null, impactMap: null, cultureProfile: null, selftune: null, reviewMemory: null, findingCategories: null, inlineCommentsPerCategory: null, minFindingSeverity: null, maxFindings: { ...EMPTY_MAX_FINDINGS_CONFIG }, commentVerbosity: null, e2eTestDelivery: null, e2eTestAutoTrigger: null, pathInstructions: [], instructions: null, excludePaths: [], pathFilters: [], preMergeChecks: [], autoReview: { ...EMPTY_AUTO_REVIEW_CONFIG }, aiModel: { ...EMPTY_SELF_HOST_AI_MODEL_CONFIG }, visual: { ...EMPTY_VISUAL_CONFIG }, linkedIssueSatisfaction: null, sharedConfigSource: null },
10781064
features: { ...EMPTY_FEATURES_CONFIG },
10791065
contentLane: { ...EMPTY_CONTENT_LANE_CONFIG },
10801066
repoDocGeneration: { ...EMPTY_REPO_DOC_GENERATION_CONFIG },
@@ -2179,7 +2165,7 @@ function parsePublicSafeText(value: JsonValue | undefined, field: string, warnin
21792165
* throws; invalid/unsafe values are dropped with warnings.
21802166
*/
21812167
function parseReviewConfig(value: JsonValue | undefined, warnings: string[]): FocusManifestReviewConfig {
2182-
const empty: FocusManifestReviewConfig = { present: false, footerText: null, note: null, fields: {}, enrichmentAnalyzers: {}, profile: null, tone: null, securityFocus: null, inlineComments: null, fixHandoff: null, autoMergeSummary: null, suggestions: null, changedFilesSummary: null, effortScore: null, impactMap: null, cultureProfile: null, selftune: null, reviewMemory: null, findingCategories: null, inlineCommentsPerCategory: null, minFindingSeverity: null, maxFindings: { ...EMPTY_MAX_FINDINGS_CONFIG }, commentVerbosity: null, e2eTestDelivery: null, e2eTestAutoTrigger: null, pathInstructions: [], instructions: null, excludePaths: [], pathFilters: [], preMergeChecks: [], autoReview: { ...EMPTY_AUTO_REVIEW_CONFIG }, labelingRules: [], aiModel: { ...EMPTY_SELF_HOST_AI_MODEL_CONFIG }, visual: { ...EMPTY_VISUAL_CONFIG }, linkedIssueSatisfaction: null, sharedConfigSource: null };
2168+
const empty: FocusManifestReviewConfig = { present: false, footerText: null, note: null, fields: {}, enrichmentAnalyzers: {}, profile: null, tone: null, securityFocus: null, inlineComments: null, fixHandoff: null, autoMergeSummary: null, suggestions: null, changedFilesSummary: null, effortScore: null, impactMap: null, cultureProfile: null, selftune: null, reviewMemory: null, findingCategories: null, inlineCommentsPerCategory: null, minFindingSeverity: null, maxFindings: { ...EMPTY_MAX_FINDINGS_CONFIG }, commentVerbosity: null, e2eTestDelivery: null, e2eTestAutoTrigger: null, pathInstructions: [], instructions: null, excludePaths: [], pathFilters: [], preMergeChecks: [], autoReview: { ...EMPTY_AUTO_REVIEW_CONFIG }, aiModel: { ...EMPTY_SELF_HOST_AI_MODEL_CONFIG }, visual: { ...EMPTY_VISUAL_CONFIG }, linkedIssueSatisfaction: null, sharedConfigSource: null };
21832169
if (value === undefined || value === null) return empty;
21842170
if (typeof value !== "object" || Array.isArray(value)) {
21852171
warnings.push(`Manifest field "review" must be a mapping; ignoring it.`);
@@ -2247,7 +2233,6 @@ function parseReviewConfig(value: JsonValue | undefined, warnings: string[]): Fo
22472233
const pathFilters = parseReviewPathFilters(r.path_filters, warnings);
22482234
const preMergeChecks = parseReviewPreMergeChecks(r.pre_merge_checks, warnings);
22492235
const autoReview = parseAutoReviewConfig(r.auto_review, warnings);
2250-
const labelingRules = parseReviewLabelingRules(r.labeling_rules, warnings);
22512236
const aiModel = parseSelfHostAiModelConfig(r.ai_model, warnings);
22522237
const visual = parseVisualConfig(r.visual, warnings);
22532238
const linkedIssueSatisfaction = normalizeOptionalEnum(r.linkedIssueSatisfaction, "review.linkedIssueSatisfaction", LINKED_ISSUE_SATISFACTION_MODES, warnings);
@@ -2281,7 +2266,6 @@ function parseReviewConfig(value: JsonValue | undefined, warnings: string[]): Fo
22812266
pathFilters.length > 0 ||
22822267
preMergeChecks.length > 0 ||
22832268
autoReviewPresent(autoReview) ||
2284-
labelingRules.length > 0 ||
22852269
selfHostAiModelPresent(aiModel) ||
22862270
visualConfigPresent(visual) ||
22872271
linkedIssueSatisfaction !== null ||
@@ -2320,7 +2304,6 @@ function parseReviewConfig(value: JsonValue | undefined, warnings: string[]): Fo
23202304
excludePaths,
23212305
pathFilters,
23222306
preMergeChecks,
2323-
labelingRules,
23242307
sharedConfigSource: null,
23252308
};
23262309
}
@@ -2421,7 +2404,6 @@ function computeReviewConfigPresent(review: Omit<FocusManifestReviewConfig, "pre
24212404
review.pathFilters.length > 0 ||
24222405
review.preMergeChecks.length > 0 ||
24232406
autoReviewPresent(review.autoReview) ||
2424-
review.labelingRules.length > 0 ||
24252407
selfHostAiModelPresent(review.aiModel) ||
24262408
visualConfigPresent(review.visual) ||
24272409
review.linkedIssueSatisfaction !== null ||
@@ -2467,7 +2449,6 @@ export function overlayReviewConfig(
24672449
pathFilters: pickOverlayStringList(override.pathFilters, base.pathFilters),
24682450
preMergeChecks: override.preMergeChecks.length > 0 ? [...override.preMergeChecks] : [...base.preMergeChecks],
24692451
autoReview: overlayAutoReviewConfig(base.autoReview, override.autoReview),
2470-
labelingRules: override.labelingRules.length > 0 ? [...override.labelingRules] : [...base.labelingRules],
24712452
aiModel: overlaySelfHostAiModelConfig(base.aiModel, override.aiModel),
24722453
visual: overlayVisualConfig(base.visual, override.visual),
24732454
linkedIssueSatisfaction: pickOverlayNullable(override.linkedIssueSatisfaction, base.linkedIssueSatisfaction),
@@ -2501,48 +2482,6 @@ function parseMaxFindingsConfig(value: JsonValue | undefined, warnings: string[]
25012482
};
25022483
}
25032484

2504-
/** The reserved label namespace Gittensor uses for scoring/type/priority (`gittensor:bug`, `gittensor:feature`,
2505-
* `gittensor:priority`, …). A maintainer's `labeling_rules` must not drive these — they're managed by the scorer
2506-
* and the type-labeler, never by ad-hoc manifest rules — so any `gittensor:`-prefixed label is refused at parse. */
2507-
const RESERVED_LABEL_PREFIX = "gittensor:";
2508-
2509-
function parseReviewLabelingRules(value: JsonValue | undefined, warnings: string[]): LabelingRule[] {
2510-
if (value === undefined || value === null) return [];
2511-
if (!Array.isArray(value)) {
2512-
warnings.push(`Manifest "review.labeling_rules" must be a list of rules; ignoring it.`);
2513-
return [];
2514-
}
2515-
const out: LabelingRule[] = [];
2516-
for (const [index, entry] of value.entries()) {
2517-
if (out.length >= MAX_PATH_INSTRUCTIONS) {
2518-
warnings.push(`Manifest "review.labeling_rules" is capped at ${MAX_PATH_INSTRUCTIONS} entries; dropping the rest.`);
2519-
break;
2520-
}
2521-
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
2522-
warnings.push(`Manifest "review.labeling_rules[${index}]" must be a mapping; ignoring it.`);
2523-
continue;
2524-
}
2525-
const e = entry as Record<string, JsonValue>;
2526-
const label = e.label === undefined || e.label === null ? null : parsePublicSafeText(e.label, `review.labeling_rules[${index}].label`, warnings);
2527-
if (label === null) {
2528-
if (e.label === undefined || e.label === null) warnings.push(`Manifest "review.labeling_rules[${index}].label" is required; ignoring the entry.`);
2529-
continue; // non-string / empty / not-public-safe already warned by parsePublicSafeText
2530-
}
2531-
if (label.toLowerCase().startsWith(RESERVED_LABEL_PREFIX)) {
2532-
warnings.push(`Manifest "review.labeling_rules[${index}].label" ("${label}") uses the reserved "${RESERVED_LABEL_PREFIX}" namespace; ignoring the entry.`);
2533-
continue;
2534-
}
2535-
const titleContains = e.title_contains === undefined || e.title_contains === null ? null : parsePublicSafeText(e.title_contains, `review.labeling_rules[${index}].title_contains`, warnings);
2536-
const descriptionContains = e.description_contains === undefined || e.description_contains === null ? null : parsePublicSafeText(e.description_contains, `review.labeling_rules[${index}].description_contains`, warnings);
2537-
const whenPaths = parseManifestGlobList(e.when_paths, `review.labeling_rules[${index}].when_paths`, warnings);
2538-
if (whenPaths.length === 0 && titleContains === null && descriptionContains === null) {
2539-
warnings.push(`Manifest "review.labeling_rules[${index}]" needs at least one of when_paths / title_contains / description_contains; ignoring it.`);
2540-
continue;
2541-
}
2542-
out.push({ label, whenPaths, titleContains, descriptionContains });
2543-
}
2544-
return out;
2545-
}
25462485

25472486
function autoReviewPresent(config: AutoReviewConfig): boolean {
25482487
return (
@@ -3020,15 +2959,6 @@ export function reviewConfigToJson(review: FocusManifestReviewConfig): JsonValue
30202959
}
30212960
if (Object.keys(review.fields).length > 0) out.fields = { ...review.fields } as Record<string, JsonValue>;
30222961
if (Object.keys(review.enrichmentAnalyzers).length > 0) out.enrichment = { ...review.enrichmentAnalyzers } as Record<string, JsonValue>;
3023-
if (review.labelingRules.length > 0) {
3024-
out.labeling_rules = review.labelingRules.map((rule) => {
3025-
const entry: Record<string, JsonValue> = { label: rule.label };
3026-
if (rule.whenPaths.length > 0) entry.when_paths = [...rule.whenPaths];
3027-
if (rule.titleContains !== null) entry.title_contains = rule.titleContains;
3028-
if (rule.descriptionContains !== null) entry.description_contains = rule.descriptionContains;
3029-
return entry;
3030-
});
3031-
}
30322962
if (selfHostAiModelPresent(review.aiModel)) {
30332963
const aiModel: Record<string, JsonValue> = {};
30342964
if (review.aiModel.claudeModel !== null) aiModel.claude_model = review.aiModel.claudeModel;

packages/gittensory-engine/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,6 @@ export {
490490
type FocusManifestMaintainerRecapConfig,
491491
type FocusManifestSettings,
492492
type FocusManifestSource,
493-
type LabelingRule,
494493
type LinkedIssueSatisfactionMode,
495494
type MaxFindingsConfig,
496495
type PreMergeCheck,

src/signals/focus-manifest.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ export {
5959
type FocusManifestMaintainerRecapConfig,
6060
type FocusManifestSettings,
6161
type FocusManifestSource,
62-
type LabelingRule,
6362
type LinkedIssueSatisfactionMode,
6463
type MaxFindingsConfig,
6564
type PreMergeCheck,

0 commit comments

Comments
 (0)