Documentation
¶
Overview ¶
Package ticket computes and applies ticket-source synchronization plans: comparing a requirement's rollup CANARY status against its owning non-flatfile source's remote status (proposing "transition" actions), and codifying flatfile-to-ticket promotion as paired "create_issue" + "remap" actions. Plan computation (ComputePlan) is pure and side-effect free; JiraClient (jira.go) is the only piece that talks to the network, and only when the CLI layer (pkg/cmds/ticket) is told to --apply. CANARY: REQ=CP-279; FEATURE="TicketSync"; ASPECT=Engine; STATUS=TESTED; TEST=TestCANARY_CBIN_306_ComputePlan_FlatfileCreateAndRemapPairing,TestCANARY_CBIN_306_ComputePlan_FlatfileNoNonFlatfileSource_NoAction,TestCANARY_CBIN_306_ComputePlan_JiraStatusMismatch_Transition,TestCANARY_CBIN_306_ComputePlan_MatchingStatus_NoAction,TestCANARY_CBIN_306_ComputePlan_EmptyRemoteStatus_AllTransitionsProposed,TestCANARY_CBIN_306_ComputePlan_StatusMapOverrideHonored,TestCANARY_CBIN_306_ComputePlan_RollupIsWorstOfTokens,TestCANARY_CBIN_306_ComputePlan_UnresolvedPrefixSkipped,TestCANARY_CBIN_306_ComputePlan_DeterministicOrdering,TestCANARY_CBIN_306_RollupStatus; UPDATED=2026-08-29 CANARY: REQ=ENG-3958; FEATURE="TicketDestination"; ASPECT=Engine; STATUS=TESTED; TEST=TestCANARY_ENG_3958_ComputePlan_CreateIssueStampsDestinationProject,TestCANARY_ENG_3958_ComputePlan_CreateIssueProjectEmptyWhenDestinationUnset; UPDATED=2026-08-29
Index ¶
Constants ¶
This section is empty.
Variables ¶
var DefaultStatusMap = map[string]string{
"STUB": "To Do",
"IMPL": "In Progress",
"TESTED": "Done",
"BENCHED": "Done",
}
DefaultStatusMap is the CANARY-status -> remote-status-name mapping used when a source's StatusMap doesn't override a given status.
Functions ¶
func FetchRemoteStatus ¶
func FetchRemoteStatus(c *JiraClient, project string) (map[string]string, error)
FetchRemoteStatus pages through JIRA's search endpoint (GET /rest/api/3/search) for every issue in project, returning a map of issue key -> current status name. Used only under `--apply`, immediately before ComputePlan, so transition actions are proposed against real remote state.
func RollupStatus ¶
RollupStatus returns the worst (least advanced) status among tokens per STUB < IMPL < TESTED < BENCHED. Tokens with an unrecognized status are ignored. An empty slice, or a slice with no recognized statuses, returns "".
Types ¶
type Action ¶
type Action struct {
Type string `json:"type"` // create_issue | transition | remap
ReqID string `json:"req_id,omitempty"` // the CANARY requirement ID this action concerns
Issue string `json:"issue,omitempty"` // remote issue key; "" on a pending remap until applied
To string `json:"to,omitempty"` // JIRA status name (transition target)
Summary string `json:"summary,omitempty"` // for create_issue
Description string `json:"description,omitempty"`
Source string `json:"source"` // owning source's Name
Project string `json:"project,omitempty"` // create_issue only: the destination source's Project (empty if unset)
}
Action is one proposed (or, once applied, completed) ticket-sync operation.
func ComputePlan ¶
func ComputePlan(tokens []*storage.Token, reg *sources.Registry, remoteStatus map[string]string) ([]Action, error)
ComputePlan computes the deterministic, pure set of proposed sync actions.
tokens are grouped by ReqID; reg resolves each requirement's owning source; remoteStatus maps a non-flatfile issue key to its current remote status name (nil or empty means "unknown" — every eligible transition is then proposed, since no remote state contradicts it).
Rules:
- A requirement owned by a non-flatfile source gets a "transition" action when its rollup status (RollupStatus: worst of its tokens, STUB<IMPL<TESTED<BENCHED), mapped through the source's StatusMap (falling back to DefaultStatusMap), differs from remoteStatus[reqID].
- A requirement owned by a flatfile source gets a paired "create_issue"
- "remap" action, but only when the registry configures at least one non-flatfile source — otherwise there's nothing to promote it to. The create_issue's Summary is "<ReqID>: <primary feature>"; Description is a bounded feature/aspect/status/file list. The paired remap's Issue is left "" — the apply step fills in the created key.
- A requirement whose prefix resolves to no configured source is skipped.
Ordering is deterministic: requirements are visited in sorted ReqID order; a flatfile requirement's create_issue action is immediately followed by its paired remap action.
type JiraClient ¶
type JiraClient struct {
BaseURL string
Email string
Token string
// HTTPClient lets tests point the client at an httptest server with a
// short timeout; nil defaults to a client with a bounded timeout.
HTTPClient *http.Client
}
JiraClient talks to the JIRA Cloud REST API (v3) using basic auth (email + API token). Every method is safe for concurrent use. CANARY: REQ=CP-279; FEATURE="TicketSync"; ASPECT=Engine; STATUS=TESTED; TEST=TestCANARY_CBIN_306_JiraClient_CreateIssue,TestCANARY_CBIN_306_JiraClient_CreateIssue_ErrorStatus,TestCANARY_CBIN_306_JiraClient_TransitionIssue_ResolvesIDByName,TestCANARY_CBIN_306_JiraClient_TransitionIssue_NoMatch,TestCANARY_CBIN_306_FetchRemoteStatus_Paged; UPDATED=2026-08-29
func (*JiraClient) CreateIssue ¶
func (c *JiraClient) CreateIssue(project, issueType, summary, description string) (string, error)
CreateIssue creates a new issue of issueType in project with summary and description (wrapped as an ADF paragraph), returning the created issue's key (e.g. "CP-12").
func (*JiraClient) TransitionIssue ¶
func (c *JiraClient) TransitionIssue(key, toStatusName string) error
TransitionIssue moves key to the transition whose target status name (transitions[].to.name) matches toStatusName case-insensitively.