Skip to content

Commit bba4a06

Browse files
authored
docs: spec audit — add github README, update fileutil/constants/timeutil/tty specs (#38848)
1 parent 8a2d014 commit bba4a06

5 files changed

Lines changed: 145 additions & 0 deletions

File tree

pkg/constants/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,14 @@ fmt.Println(constants.GhAwRootDirShell) // "${RUNNER_TEMP}/gh-aw"
483483
dir := constants.GetWorkflowDir() // ".github/workflows"
484484
```
485485

486+
## Dependencies
487+
488+
**Internal**:
489+
- None
490+
491+
**External**:
492+
- None beyond the Go standard library (`io/fs`, `os`, `path/filepath`, `time`).
493+
486494
## Design Notes
487495

488496
- All semantic types implement `String()` and `IsValid()` to allow consistent validation across the codebase.

pkg/fileutil/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ This package focuses on security-conscious file handling: path validation, bound
1818
| `DirExists` | `func(path string) bool` | Returns `true` if `path` exists and is a directory |
1919
| `IsDirEmpty` | `func(path string) bool` | Returns `true` if the directory at `path` contains no entries; also returns `true` if the directory cannot be read |
2020
| `CopyFile` | `func(src, dst string) error` | Copies the file at `src` to `dst` using buffered I/O; calls `Sync` on the destination before closing |
21+
| `EnsureParentDir` | `func(path string, perm os.FileMode) error` | Ensures the parent directory of `path` exists, creating it recursively with the given permissions; returns an error for empty paths |
2122
| `ExtractFileFromTar` | `func(data []byte, path string) ([]byte, error)` | Extracts a single file by `path` from a tar archive; rejects unsafe entry names (absolute or `..`-containing paths) |
2223

2324
## Usage Examples

pkg/github/README.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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.*

pkg/timeutil/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ timeutil.FormatDurationNs(2_000_000_000) // "2s"
8484
timeutil.FormatDurationNs(90_000_000_000) // "1m30s"
8585
```
8686

87+
## Dependencies
88+
89+
**Internal**:
90+
- None
91+
92+
**External**:
93+
- None beyond the Go standard library (`fmt`, `math`, `time`).
94+
8795
## Design Decisions
8896

8997
- `FormatDuration` is used by the `logger` package to display time-diff between consecutive log calls (the `+500ms` suffix in debug output).

pkg/tty/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ if tty.IsStderrTerminal() {
3535
}
3636
```
3737

38+
## Dependencies
39+
40+
**Internal**:
41+
- None
42+
43+
**External**:
44+
- `golang.org/x/term` — TTY detection via `term.IsTerminal`
45+
3846
## Design Notes
3947

4048
- Terminal detection is evaluated at call time, not cached. This is intentional: the streams could be redirected between calls in some testing scenarios.

0 commit comments

Comments
 (0)