-
Notifications
You must be signed in to change notification settings - Fork 455
docs: resolve spec audit — pkg/intent spec, actionpins Mappings field, linters 4 new subpackages #41723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
docs: resolve spec audit — pkg/intent spec, actionpins Mappings field, linters 4 new subpackages #41723
Changes from all commits
a08bdc4
a18778a
970dbf3
c68a14c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,7 +24,7 @@ Resolution supports two modes: | |
| | `SHAResolver` | interface | Resolves a SHA for `repo@version` dynamically | | ||
| | `ResolutionErrorType` | string | Classifies unresolved action-ref pinning outcomes for auditing | | ||
| | `ResolutionFailure` | struct | Captures an unresolved action-ref pinning event (repo, ref, error type) | | ||
| | `PinContext` | struct | Runtime context for resolution (resolver, strict mode, warning dedupe map) | | ||
| | `PinContext` | struct | Runtime context for resolution (resolver, strict mode, warning dedupe map, action-pin mappings) | | ||
|
|
||
| ### Functions | ||
|
|
||
|
|
@@ -75,6 +75,23 @@ actionpins.ResolveActionPin("unknown/action", "v1", ctx) | |
| // failures[0].ErrorType == actionpins.ResolutionErrorTypePinNotFound | ||
| ``` | ||
|
|
||
| ### Action Pin Mappings | ||
|
|
||
| `PinContext.Mappings` redirects `owner/repo@ref` references to replacement references before pin resolution. This is used to substitute private or mirror repositories for well-known public actions (set from `aw.json` `action_pins`). | ||
|
|
||
| Keys and values use the format `"owner/repo@ref"`. When a key matches the incoming `actionRepo@version`, resolution proceeds against the mapped value instead. An informational message is emitted once per mapping via `PinContext.Warnings`. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "via 💡 Suggested fixReplace:
With:
As written, a reader may expect that querying |
||
|
|
||
| ```go | ||
| ctx := &actionpins.PinContext{ | ||
| Warnings: make(map[string]bool), | ||
| Mappings: map[string]string{ | ||
| "actions/checkout@v4": "acme-corp/checkout@v4", | ||
| }, | ||
| } | ||
| reference, err := actionpins.ResolveActionPin("actions/checkout", "v4", ctx) | ||
| // reference resolves against acme-corp/checkout@v4 pins | ||
| ``` | ||
|
|
||
| ### Container Pins | ||
|
|
||
| `ContainerPin` provides a pinned image reference for container images: | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,105 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # intent Package | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > Intent attribution and resolution utilities for mapping pull requests and issues to labelled intent records. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Overview | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The `intent` package resolves the intent behind a pull request or issue by inspecting labels, closing issues, and explicit metadata. It produces `IntentRecord` values that classify attribution status and source, allowing downstream consumers to route work items or report on coverage. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Resolution is performed by a `Resolver`, which holds a label matcher function and a version string. The resolver applies a priority chain: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1. Explicit intent metadata (`PullRequestData.ExplicitIntent`) — used as-is. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inaccurate spec — "used as-is" does not match the implementation: the resolver modifies the explicit intent record by stamping 💡 Suggested fixThe implementation in if pr.ExplicitIntent != nil {
intent := *pr.ExplicitIntent
if intent.ResolverVersion == "" {
intent.ResolverVersion = r.ResolverVersion
}
return intent
}The spec test even contradicts this README line — its doc comment says Otherwise downstream implementers may incorrectly assume the record passes through without mutation. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 2. A single closing issue — resolved from the issue's labels. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 3. PR labels — used as an artifact fallback when no closing issues are present. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 4. No supported sources — returns an `AttributionUnlinked` record. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Priority chain is incomplete — the multiple-closing-issues → ambiguous path is undocumented: the chain lists 4 outcomes but the implementation handles a 5th case (two or more closing issues) that produces 💡 Suggested fixAdd the missing case between items 2 and 3: A reader building on this package who only reads the priority chain will be blindsided by |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Public API | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Types | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | Type | Kind | Description | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |------|------|-------------| | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `AttributionStatus` | string | Classifies the outcome of intent attribution | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `AttributionSource` | string | Identifies the data source used for attribution | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `IntentRecord` | struct | Holds the attribution result for a pull request or issue | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/grill-with-docs] 💡 Suggested additionAdd a
This is especially important since
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
💡 Suggested fixAdd a
Without this, the spec tests are enforcing undocumented behaviour, and the |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `RootReference` | struct | Represents a referenced issue or artifact root (node ID, type, URL, labels) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `PullRequestData` | struct | Input data for pull request resolution (node ID, URL, labels, explicit intent, closing issues) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `Resolver` | struct | Stateless resolver that maps labels to intent records | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### AttributionStatus constants | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | Constant | Value | Description | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |----------|-------|-------------| | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `AttributionMapped` | `"mapped"` | Labels matched a known intent category | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `AttributionUnmapped` | `"unmapped"` | Labels were present but matched no category | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `AttributionUnlinked` | `"unlinked"` | No supported intent source was found | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `AttributionAmbiguous` | `"ambiguous"` | Multiple competing sources were found | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `AttributionSuggested` | `"suggested"` | Attribution was inferred by suggestion (not confirmed) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### AttributionSource constants | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | Constant | Value | Description | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |----------|-------|-------------| | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `SourceExplicitMetadata` | `"explicit_metadata"` | Explicitly provided intent metadata | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `SourceClosingIssue` | `"closing_issue"` | Derived from a closing issue | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `SourceParentIssue` | `"parent_issue"` | Derived from a parent issue | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `SourceReferencedIssue` | `"referenced_issue"` | Derived from a referenced issue | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `SourceProject` | `"project"` | Derived from a project assignment | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `SourceMilestone` | `"milestone"` | Derived from a milestone assignment | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `SourceIssueLabels` | `"issue_labels"` | Derived from labels on an issue | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `SourceArtifactLabels` | `"artifact_labels"` | Derived from labels on the artifact (pull request) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `SourceSuggestion` | `"suggestion"` | Derived from a suggestion | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `SourceNone` | `"none"` | No source was used | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Resolver methods | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | Method | Signature | Description | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |--------|-----------|-------------| | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `ResolvePullRequest` | `func (r Resolver) ResolvePullRequest(pr PullRequestData) IntentRecord` | Resolves intent for a pull request using explicit intent, closing issues, or PR labels | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `ResolveIssue` | `func (r Resolver) ResolveIssue(nodeID, url string, labels []string) IntentRecord` | Resolves intent for an issue using its labels | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Usage Examples | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```go | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resolver := intent.Resolver{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResolverVersion: "v1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MatchLabels: func(labels []string) []string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, l := range labels { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if l == "security" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return []string{l} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| record := resolver.ResolvePullRequest(intent.PullRequestData{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| NodeID: "PR_kwDOAAABCD4", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| URL: "https://github.com/owner/repo/pull/42", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Labels: []string{"security"}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fmt.Println(record.Status) // "mapped" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fmt.Println(record.Source) // "artifact_labels" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Resolving an issue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```go | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| record := resolver.ResolveIssue( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "I_kwDOAAABCQ4", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "https://github.com/owner/repo/issues/1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| []string{"security"}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fmt.Println(record.Status) // "mapped" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fmt.Println(record.Source) // "issue_labels" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Thread Safety | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `Resolver` holds no mutable state and is safe for concurrent use. `IntentRecord` values are returned by value and do not share mutable state with the caller. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/grill-with-docs] The thread-safety claim "Resolver holds no mutable state and is safe for concurrent use" is only half the story. If 💡 Suggested wording`Resolver` holds no mutable state of its own and is safe for concurrent use,
provided the supplied `MatchLabels` function is goroutine-safe.
`IntentRecord` values are returned by value and do not share mutable state with the caller.This mirrors how the Go standard library phrases similar caveats (e.g., |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| *This specification is automatically maintained by the [spec-extractor](../../.github/workflows/spec-extractor.md) workflow.* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[/grill-with-docs] The section says keys and values use
"owner/repo@ref"format, but it's unclear whether the value undergoes full SHA pin resolution. The comment// reference resolves against acme-corp/checkout@v4 pinsimplies pinning still happens, but a reader may wonder: does the mapped target need a pre-existing pin entry, or does the resolver look it up dynamically?💡 Suggested clarification
Add a sentence clarifying the lifecycle, e.g.:
This prevents confusion when users map to a mirror that has no existing pin.