|
| 1 | +# github Package |
| 2 | + |
| 3 | +The `github` package provides label-based objective value mapping for issue prioritization scoring. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +This package defines how GitHub issue labels are translated into numeric objective values. It supports configurable mapping strategies (max, sum, first) and can load its configuration from an environment variable, a repository config file, or built-in defaults. |
| 8 | + |
| 9 | +## Public API |
| 10 | + |
| 11 | +### Types |
| 12 | + |
| 13 | +| Type | Description | |
| 14 | +|------|-------------| |
| 15 | +| `ObjectiveMapping` | Defines how GitHub labels map to numeric objective values, including the combination logic for multiple matching labels | |
| 16 | + |
| 17 | +#### `ObjectiveMapping` Fields |
| 18 | + |
| 19 | +| Field | Type | Description | |
| 20 | +|-------|------|-------------| |
| 21 | +| `LabelToValue` | `map[string]int` | Maps label names (case-insensitive) to numeric values | |
| 22 | +| `MultiLabelLogic` | `string` | How multiple matching labels are combined: `"max"` (default), `"sum"`, or `"first"` | |
| 23 | +| `PriorityLabels` | `[]string` | Evaluation order when `MultiLabelLogic` is `"first"` | |
| 24 | + |
| 25 | +### Methods on `*ObjectiveMapping` |
| 26 | + |
| 27 | +| Method | Signature | Description | |
| 28 | +|--------|-----------|-------------| |
| 29 | +| `ComputeObjectiveValue` | `func(issueLabels []string) int` | Calculates the numeric value for an issue based on its labels; returns `0` if no labels match or if the receiver is `nil` | |
| 30 | +| `GetObjectiveLabels` | `func(issueLabels []string) []string` | Returns the subset of `issueLabels` that have defined objective values, preserving original order | |
| 31 | +| `ValidateLabelExists` | `func(label string) bool` | Reports whether a given label has a defined objective value | |
| 32 | +| `GetAllLabels` | `func() []string` | Returns all labels defined in the mapping, sorted alphabetically | |
| 33 | +| `MarshalJSON` | `func() ([]byte, error)` | Implements `json.Marshaler`; produces indented JSON output | |
| 34 | +| `String` | `func() string` | Returns a human-readable summary: `ObjectiveMapping{labels: N, logic: X, priorities: M}` | |
| 35 | + |
| 36 | +### Functions |
| 37 | + |
| 38 | +| Function | Signature | Description | |
| 39 | +|----------|-----------|-------------| |
| 40 | +| `DefaultObjectiveMapping` | `func() *ObjectiveMapping` | Returns the built-in default label-to-value mapping | |
| 41 | +| `LoadObjectiveMappingFromConfig` | `func() *ObjectiveMapping` | Loads the mapping from environment, config file, or defaults (see precedence below) | |
| 42 | + |
| 43 | +### Constants |
| 44 | + |
| 45 | +The package exports named constants for every label and its default value, grouped by priority tier: |
| 46 | + |
| 47 | +| Group | Label constants | Value constants | |
| 48 | +|-------|----------------|-----------------| |
| 49 | +| Critical | `ObjectiveLabelCritical`, `ObjectiveLabelP0` | `ObjectiveValueCritical` (100), `ObjectiveValueP0` (100) | |
| 50 | +| Security | `ObjectiveLabelSecurityFix` | `ObjectiveValueSecurityFix` (70) | |
| 51 | +| Copilot | `ObjectiveLabelCopilotOpt` | `ObjectiveValueCopilotOpt` (75) | |
| 52 | +| Bug | `ObjectiveLabelBug` | `ObjectiveValueBug` (60) | |
| 53 | +| High | `ObjectiveLabelHighPriority`, `ObjectiveLabelP1` | `ObjectiveValueHighPriority` (35), `ObjectiveValueP1` (35) | |
| 54 | +| Safety | `ObjectiveLabelTesting`, `ObjectiveLabelReliability` | `ObjectiveValueTesting` (50), `ObjectiveValueReliability` (50) | |
| 55 | +| Engine | `ObjectiveLabelWorkflow`, `ObjectiveLabelEngine` | `ObjectiveValueWorkflow` (45), `ObjectiveValueEngine` (40) | |
| 56 | +| Integration | `ObjectiveLabelMCP`, `ObjectiveLabelActions`, `ObjectiveLabelCLI` | `ObjectiveValueMCP` (45), `ObjectiveValueActions` (40), `ObjectiveValueCLI` (40) | |
| 57 | +| Performance | `ObjectiveLabelPerformance` | `ObjectiveValuePerformance` (30) | |
| 58 | +| Medium | `ObjectiveLabelMediumPriority`, `ObjectiveLabelP2` | `ObjectiveValueMediumPriority` (20), `ObjectiveValueP2` (20) | |
| 59 | +| Quality | `ObjectiveLabelLintMonster` | `ObjectiveValueLintMonster` (25) | |
| 60 | +| Enhancement | `ObjectiveLabelEnhancement` | `ObjectiveValueEnhancement` (15) | |
| 61 | +| Dependencies | `ObjectiveLabelDependencies` | `ObjectiveValueDependencies` (10) | |
| 62 | +| Low | `ObjectiveLabelLowPriority`, `ObjectiveLabelP3` | `ObjectiveValueLowPriority` (10), `ObjectiveValueP3` (10) | |
| 63 | +| Documentation | `ObjectiveLabelDocumentation` | `ObjectiveValueDocumentation` (5) | |
| 64 | +| No value | `ObjectiveLabelAIGenerated`, `ObjectiveLabelAIInspected`, `ObjectiveLabelSmokeCopilot`, `ObjectiveLabelQuestion`, `ObjectiveLabelGoodFirstIssue` | 0 | |
| 65 | + |
| 66 | +Multi-label logic option constants: |
| 67 | + |
| 68 | +| Constant | Value | Description | |
| 69 | +|----------|-------|-------------| |
| 70 | +| `MultiLabelLogicMax` | `"max"` | Use the highest matching label value (default) | |
| 71 | +| `MultiLabelLogicSum` | `"sum"` | Sum all matching label values | |
| 72 | +| `MultiLabelLogicFirst` | `"first"` | Use the first match in priority order | |
| 73 | + |
| 74 | +## Configuration Precedence |
| 75 | + |
| 76 | +`LoadObjectiveMappingFromConfig` resolves the mapping in this order: |
| 77 | + |
| 78 | +1. **`OBJECTIVE_MAPPING_JSON` environment variable** — interpreted first as a raw JSON string; if parsing fails, treated as a file path from which JSON is read. |
| 79 | +2. **`.github/objective-mapping.json`** — a repository-level override file. |
| 80 | +3. **Built-in defaults** — returned by `DefaultObjectiveMapping()`. |
| 81 | + |
| 82 | +## Usage Examples |
| 83 | + |
| 84 | +```go |
| 85 | +import "github.com/github/gh-aw/pkg/github" |
| 86 | + |
| 87 | +// Load mapping (env > config file > defaults) |
| 88 | +om := github.LoadObjectiveMappingFromConfig() |
| 89 | + |
| 90 | +// Score an issue by its labels |
| 91 | +score := om.ComputeObjectiveValue([]string{"bug", "high-priority"}) |
| 92 | +// score == 60 (max of bug=60, high-priority=35) |
| 93 | + |
| 94 | +// Check which labels contributed |
| 95 | +objectiveLabels := om.GetObjectiveLabels([]string{"bug", "good first issue"}) |
| 96 | +// objectiveLabels == ["bug"] |
| 97 | + |
| 98 | +// Use the default mapping directly |
| 99 | +defaults := github.DefaultObjectiveMapping() |
| 100 | +fmt.Println(defaults) // ObjectiveMapping{labels: 12, logic: max, priorities: 7} |
| 101 | +``` |
| 102 | + |
| 103 | +## Dependencies |
| 104 | + |
| 105 | +**Internal**: |
| 106 | +- `github.com/github/gh-aw/pkg/logger` — debug logging via `logger.New("github:label_objective_mapping")` |
| 107 | + |
| 108 | +**External**: |
| 109 | +- None beyond the Go standard library (`encoding/json`, `fmt`, `os`, `path/filepath`, `slices`, `strings`). |
| 110 | + |
| 111 | +## Design Notes |
| 112 | + |
| 113 | +- All label comparisons are case-insensitive: labels are normalised with `strings.ToLower(strings.TrimSpace(...))` before lookup. |
| 114 | +- The default `MultiLabelLogic` is `"max"`. Callers that do not set this field get max-value semantics automatically. |
| 115 | +- `PriorityLabels` is only consulted when `MultiLabelLogic` is `"first"`; it establishes evaluation precedence among matching labels. |
| 116 | +- Debug output is controlled by the `DEBUG=github:*` environment variable and is only emitted when that variable is set. |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +*This specification is automatically maintained by the [spec-extractor](../../.github/workflows/spec-extractor.md) workflow.* |
0 commit comments