Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 19 additions & 12 deletions .gittensory.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,24 @@ review:
skip_drafts: true
skip_docs_only: true

# Linked-issue label propagation (#priority-linked-issue-gate, #priority-linked-issue-gate-ownership): a PR
# that closes/fixes/resolves an issue inherits that issue's point-bearing gittensor:* label onto the PR
# itself, instead of the PR's own label being decided purely by its commit-title prefix. bug/feature are
# `trustMaintainerAuthoredIssue: true` (routine categorization, no reward at stake, and the title-based
# fallback already has zero equivalent verification) so they propagate even when the PR author isn't a
# formal GitHub assignee of the issue — our issues are almost always maintainer-authored for open pickup and
# rarely formally assigned. priority intentionally omits the flag: it is the scarce, maintainer-hand-picked
# reward label, and must still require the PR author to be the issue's actual author/assignee. priority is
# also `removeOtherTypeLabels: false` (additive) -- unlike bug/feature, which are mutually-exclusive TYPE
# categories, priority is a separate reward dimension that coexists WITH whichever type already applies (an
# issue is routinely both gittensor:feature AND gittensor:priority at once); resolvePrTypeLabel composes every
# additive match alongside the one exclusive winner, rather than the two categories competing for a single slot.
# Linked-issue label propagation (#priority-linked-issue-gate, #priority-linked-issue-gate-ownership,
# #priority-reward-maintainer-trust): a PR that closes/fixes/resolves an issue inherits that issue's
# point-bearing gittensor:* label onto the PR itself, instead of the PR's own label being decided purely by
# its commit-title prefix. bug/feature are `trustMaintainerAuthoredIssue: true` (routine categorization, no
# reward at stake, and the title-based fallback already has zero equivalent verification) so they propagate
# even when the PR author isn't a formal GitHub assignee of the issue — our issues are almost always
# maintainer-authored for open pickup and rarely formally assigned. priority is a scarce, maintainer-hand-
# picked reward label, so it defaults to the strict author-or-assignee-only bar everywhere in this codebase
# EXCEPT here, where we explicitly opt it into the SAME relaxation via `trustMaintainerAuthoredIssueForReward:
# true`: our issues are open-pickup by design (see the flag's own doc comment in types.ts), so requiring a
# literal GitHub assignee relationship -- which GitHub silently refuses for a contributor lacking push/triage
# access -- meant this label could structurally never reach the external contributors it exists to reward. The
# maintainer's hand-picking already happened when the issue was labeled `gittensor:priority`, not gated on
# which contributor later closes it. priority is also `removeOtherTypeLabels: false` (additive) -- unlike
# bug/feature, which are mutually-exclusive TYPE categories, priority is a separate reward dimension that
# coexists WITH whichever type already applies (an issue is routinely both gittensor:feature AND
# gittensor:priority at once); resolvePrTypeLabel composes every additive match alongside the one exclusive
# winner, rather than the two categories competing for a single slot.
#
# Review-evasion protection: closing or converting-to-draft your OWN PR while gittensory has an active
# review pass running, a prior recorded gate failure, or a repeated ready<->draft cycle on this PR, is
Expand All @@ -97,6 +103,7 @@ settings:
- issueLabel: "gittensor:priority"
prLabel: "gittensor:priority"
removeOtherTypeLabels: false
trustMaintainerAuthoredIssueForReward: true
reviewEvasionProtection: close

# Repo-doc generation roadmap (#2993/#3002) — opt-in only, off by default. Uncomment to let Gittensory open a
Expand Down
8 changes: 8 additions & 0 deletions .gittensory.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,13 @@ settings:
# which is a reward tag that coexists WITH whichever type already applies, not a type of its own) applies
# the mapped label ADDITIVELY alongside whichever exclusive match (or the normal title-based bug/feature
# label) already won -- every additive match composes together rather than competing for a single slot.
# Each mapping normally requires the PR author to be the linked issue's own author or a GitHub assignee of
# it (`trustMaintainerAuthoredIssue: true` relaxes this for a routine, non-reward category like bug/feature
# -- see the field's own doc comment in `types.ts`). `trustMaintainerAuthoredIssueForReward: true` opts a
# REWARD mapping like `gittensor:priority` into that same relaxation: without it, a mapping like this can
# structurally never propagate to most external contributors, since GitHub silently refuses to assign
# anyone lacking push/triage access to the repo -- so if your issues are open-pickup (maintainer-authored,
# rarely formally assigned to a specific contributor), the reward label needs this flag to ever reach them.
# Disabled by default (no mappings) -- a self-hoster opts in per repo. If your labels carry reward or
# moderation weight, configure this in PRIVATE per-repo/global config (see `config/examples/README.md`)
# rather than the public `.gittensory.yml`, so contributors cannot see the exact mapping rules. A per-repo
Expand All @@ -621,6 +628,7 @@ settings:
# - issueLabel: gittensor:priority
# prLabel: gittensor:priority
# removeOtherTypeLabels: false
# trustMaintainerAuthoredIssueForReward: true

# Create the label if it does not yet exist. Bool. Default: true.
createMissingLabel: true
Expand Down
3 changes: 3 additions & 0 deletions apps/gittensory-ui/public/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -8949,6 +8949,9 @@
},
"trustMaintainerAuthoredIssue": {
"type": "boolean"
},
"trustMaintainerAuthoredIssueForReward": {
"type": "boolean"
}
},
"required": [
Expand Down
8 changes: 8 additions & 0 deletions config/examples/gittensory.full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,13 @@ settings:
# which is a reward tag that coexists WITH whichever type already applies, not a type of its own) applies
# the mapped label ADDITIVELY alongside whichever exclusive match (or the normal title-based bug/feature
# label) already won -- every additive match composes together rather than competing for a single slot.
# Each mapping normally requires the PR author to be the linked issue's own author or a GitHub assignee of
# it (`trustMaintainerAuthoredIssue: true` relaxes this for a routine, non-reward category like bug/feature
# -- see the field's own doc comment in `types.ts`). `trustMaintainerAuthoredIssueForReward: true` opts a
# REWARD mapping like `gittensor:priority` into that same relaxation: without it, a mapping like this can
# structurally never propagate to most external contributors, since GitHub silently refuses to assign
# anyone lacking push/triage access to the repo -- so if your issues are open-pickup (maintainer-authored,
# rarely formally assigned to a specific contributor), the reward label needs this flag to ever reach them.
# Disabled by default (no mappings) -- a self-hoster opts in per repo. If your labels carry reward or
# moderation weight, configure this in PRIVATE per-repo/global config (see `config/examples/README.md`)
# rather than the public `.gittensory.yml`, so contributors cannot see the exact mapping rules. A per-repo
Expand All @@ -634,6 +641,7 @@ settings:
# - issueLabel: gittensor:priority
# prLabel: gittensor:priority
# removeOtherTypeLabels: false
# trustMaintainerAuthoredIssueForReward: true

# Create the label if it does not yet exist. Bool. Default: true.
createMissingLabel: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,17 @@ function normalizeMapping(input: unknown, index: number, warnings: string[]): Li
warnings.push(`settings.linkedIssueLabelPropagation.mappings[${index}].trustMaintainerAuthoredIssue must be a boolean; ignoring it.`);
}
}
return { issueLabel, prLabel, removeOtherTypeLabels: record.removeOtherTypeLabels === true, trustMaintainerAuthoredIssue };
// Same parse contract as trustMaintainerAuthoredIssue just above (#priority-reward-maintainer-trust).
// Mirrors `src/review/linked-issue-label-propagation.ts`'s copy of this normalizer.
let trustMaintainerAuthoredIssueForReward: boolean | undefined;
if (record.trustMaintainerAuthoredIssueForReward !== undefined) {
if (typeof record.trustMaintainerAuthoredIssueForReward === "boolean") {
trustMaintainerAuthoredIssueForReward = record.trustMaintainerAuthoredIssueForReward;
} else {
warnings.push(`settings.linkedIssueLabelPropagation.mappings[${index}].trustMaintainerAuthoredIssueForReward must be a boolean; ignoring it.`);
}
}
return { issueLabel, prLabel, removeOtherTypeLabels: record.removeOtherTypeLabels === true, trustMaintainerAuthoredIssue, trustMaintainerAuthoredIssueForReward };
}

/** Defaults-fill a per-repo `linkedIssueLabelPropagation` override into an always-complete, safe config —
Expand Down
4 changes: 4 additions & 0 deletions packages/gittensory-engine/src/types/manifest-deps-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export type LinkedIssueLabelPropagationMapping = {
* see `review/linked-issue-label-propagation-fetch.ts`'s `isRepoMaintainerLogin` (app-side only, not
* duplicated into this engine package since it needs GitHub/fetch/Env access). */
trustMaintainerAuthoredIssue?: boolean | undefined;
/** Like `trustMaintainerAuthoredIssue`, but for a mapping that DOES carry real reward weight
* (#priority-reward-maintainer-trust) -- e.g. `gittensor:priority`. Mirrors `src/types.ts`'s copy of
* this type; see that copy's doc comment for the full rationale. */
trustMaintainerAuthoredIssueForReward?: boolean | undefined;
};

export type LinkedIssueLabelPropagationMode = "exclusive_type_label";
Expand Down
31 changes: 19 additions & 12 deletions src/config/gittensory-repo-focus-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,24 @@ review:
skip_drafts: true
skip_docs_only: true

# Linked-issue label propagation (#priority-linked-issue-gate, #priority-linked-issue-gate-ownership): a PR
# that closes/fixes/resolves an issue inherits that issue's point-bearing gittensor:* label onto the PR
# itself, instead of the PR's own label being decided purely by its commit-title prefix. bug/feature are
# \`trustMaintainerAuthoredIssue: true\` (routine categorization, no reward at stake, and the title-based
# fallback already has zero equivalent verification) so they propagate even when the PR author isn't a
# formal GitHub assignee of the issue — our issues are almost always maintainer-authored for open pickup and
# rarely formally assigned. priority intentionally omits the flag: it is the scarce, maintainer-hand-picked
# reward label, and must still require the PR author to be the issue's actual author/assignee. priority is
# also \`removeOtherTypeLabels: false\` (additive) -- unlike bug/feature, which are mutually-exclusive TYPE
# categories, priority is a separate reward dimension that coexists WITH whichever type already applies (an
# issue is routinely both gittensor:feature AND gittensor:priority at once); resolvePrTypeLabel composes every
# additive match alongside the one exclusive winner, rather than the two categories competing for a single slot.
# Linked-issue label propagation (#priority-linked-issue-gate, #priority-linked-issue-gate-ownership,
# #priority-reward-maintainer-trust): a PR that closes/fixes/resolves an issue inherits that issue's
# point-bearing gittensor:* label onto the PR itself, instead of the PR's own label being decided purely by
# its commit-title prefix. bug/feature are \`trustMaintainerAuthoredIssue: true\` (routine categorization, no
# reward at stake, and the title-based fallback already has zero equivalent verification) so they propagate
# even when the PR author isn't a formal GitHub assignee of the issue — our issues are almost always
# maintainer-authored for open pickup and rarely formally assigned. priority is a scarce, maintainer-hand-
# picked reward label, so it defaults to the strict author-or-assignee-only bar everywhere in this codebase
# EXCEPT here, where we explicitly opt it into the SAME relaxation via \`trustMaintainerAuthoredIssueForReward:
# true\`: our issues are open-pickup by design (see the flag's own doc comment in types.ts), so requiring a
# literal GitHub assignee relationship -- which GitHub silently refuses for a contributor lacking push/triage
# access -- meant this label could structurally never reach the external contributors it exists to reward. The
# maintainer's hand-picking already happened when the issue was labeled \`gittensor:priority\`, not gated on
# which contributor later closes it. priority is also \`removeOtherTypeLabels: false\` (additive) -- unlike
# bug/feature, which are mutually-exclusive TYPE categories, priority is a separate reward dimension that
# coexists WITH whichever type already applies (an issue is routinely both gittensor:feature AND
# gittensor:priority at once); resolvePrTypeLabel composes every additive match alongside the one exclusive
# winner, rather than the two categories competing for a single slot.
#
# Review-evasion protection: closing or converting-to-draft your OWN PR while gittensory has an active
# review pass running, a prior recorded gate failure, or a repeated ready<->draft cycle on this PR, is
Expand All @@ -101,6 +107,7 @@ settings:
- issueLabel: "gittensor:priority"
prLabel: "gittensor:priority"
removeOtherTypeLabels: false
trustMaintainerAuthoredIssueForReward: true
reviewEvasionProtection: close

# Repo-doc generation roadmap (#2993/#3002) — opt-in only, off by default. Uncomment to let Gittensory open a
Expand Down
1 change: 1 addition & 0 deletions src/openapi/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,7 @@ export const RepositorySettingsSchema = z
prLabel: z.string(),
removeOtherTypeLabels: z.boolean(),
trustMaintainerAuthoredIssue: z.boolean().optional(),
trustMaintainerAuthoredIssueForReward: z.boolean().optional(),
}),
),
})
Expand Down
21 changes: 12 additions & 9 deletions src/review/linked-issue-label-propagation-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,15 @@ async function isRepoMaintainerLogin(env: Env, installationId: number, repoFullN

/** Per-issue label resolution for {@link fetchLinkedIssueLabelsForPropagation}: a direct PR-author-is-
* issue-author-or-assignee match unlocks EVERY label the issue carries (today's original behavior,
* unchanged). Failing that, a mapping explicitly opted into `trustMaintainerAuthoredIssue`
* (#priority-linked-issue-gate-ownership) unlocks JUST that mapping's `issueLabel` when the issue's
* author independently checks out as a repo maintainer/operator via {@link isRepoMaintainerLogin} --
* built so routine bug/feature mirroring doesn't require formal GitHub issue assignment (our own repos
* rarely assign issues), while a scarce, maintainer-hand-picked reward label like `gittensor:priority`
* (which should never set the flag) still requires the contributor to be the actual author/assignee.
* unchanged). Failing that, a mapping explicitly opted into `trustMaintainerAuthoredIssue` OR
* `trustMaintainerAuthoredIssueForReward` (#priority-linked-issue-gate-ownership, #priority-reward-
* maintainer-trust) unlocks JUST that mapping's `issueLabel` when the issue's author independently
* checks out as a repo maintainer/operator via {@link isRepoMaintainerLogin} -- built so routine
* bug/feature mirroring doesn't require formal GitHub issue assignment (our own repos rarely assign
* issues). A reward mapping (e.g. `gittensor:priority`) opting into the SAME relaxation via the
* `...ForReward` flag is a deliberate, per-repo operator choice (see that flag's own doc comment in
* types.ts for why the assignee-only bar is often unsatisfiable in practice); a reward mapping that has
* NOT opted in still requires the contributor to be the actual author/assignee, unchanged.
* `relaxableLabels` is empty whenever the caller passed no mappings or none opted in, which skips the
* maintainer-permission check (and its GitHub API call) entirely -- byte-identical to the pre-fix
* behavior for any caller that hasn't opted in. Logs once per issue when the returned set is smaller
Expand Down Expand Up @@ -104,8 +107,8 @@ async function resolveIssueLabelsForPropagation(
*
* `mappings` (optional, #priority-linked-issue-gate-ownership) is the propagation config's own mapping
* list, used ONLY to know which `issueLabel`s are allowed to unlock via `resolveIssueLabelsForPropagation`'s
* relaxed maintainer-authored-issue path -- omitting it (or a mapping never setting the flag) reproduces
* today's strict author-or-assignee-only behavior exactly. */
* relaxed maintainer-authored-issue path (either trust flag) -- omitting it (or a mapping never setting
* either flag) reproduces today's strict author-or-assignee-only behavior exactly. */
export async function fetchLinkedIssueLabelsForPropagation(args: {
env: Env;
repoFullName: string;
Expand All @@ -128,7 +131,7 @@ export async function fetchLinkedIssueLabelsForPropagation(args: {
const prAuthorLogin = args.prAuthorLogin?.toLowerCase();
const relaxableLabels = new Set(
(args.mappings ?? [])
.filter((mapping) => mapping.trustMaintainerAuthoredIssue === true)
.filter((mapping) => mapping.trustMaintainerAuthoredIssue === true || mapping.trustMaintainerAuthoredIssueForReward === true)
.map((mapping) => mapping.issueLabel.toLowerCase()),
);
const results = await Promise.all(
Expand Down
13 changes: 12 additions & 1 deletion src/review/linked-issue-label-propagation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,18 @@ function normalizeMapping(input: unknown, index: number, warnings: string[]): Li
warnings.push(`settings.linkedIssueLabelPropagation.mappings[${index}].trustMaintainerAuthoredIssue must be a boolean; ignoring it.`);
}
}
return { issueLabel, prLabel, removeOtherTypeLabels: record.removeOtherTypeLabels === true, trustMaintainerAuthoredIssue };
// Same parse contract as trustMaintainerAuthoredIssue just above (#priority-reward-maintainer-trust):
// malformed is warned-and-defaulted to undefined/strict, never silently coerced, never a reason to drop
// an otherwise-valid mapping.
let trustMaintainerAuthoredIssueForReward: boolean | undefined;
if (record.trustMaintainerAuthoredIssueForReward !== undefined) {
if (typeof record.trustMaintainerAuthoredIssueForReward === "boolean") {
trustMaintainerAuthoredIssueForReward = record.trustMaintainerAuthoredIssueForReward;
} else {
warnings.push(`settings.linkedIssueLabelPropagation.mappings[${index}].trustMaintainerAuthoredIssueForReward must be a boolean; ignoring it.`);
}
}
return { issueLabel, prLabel, removeOtherTypeLabels: record.removeOtherTypeLabels === true, trustMaintainerAuthoredIssue, trustMaintainerAuthoredIssueForReward };
}

/** Defaults-fill a per-repo `linkedIssueLabelPropagation` override into an always-complete, safe config —
Expand Down
Loading
Loading