mesh-issues: read merged from the pull request, not the state, so a closed one is withdrawn (hq issue 483)
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds mesh-issues → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds mesh-issues → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh-delivery says stopped for a pull request closed unmerged too; read as merged, it was said stuck to the operator for good. Cite ADR 0284 for the worker rules, which the comments gave as 0283.
This commit is contained in:
@@ -30,13 +30,13 @@ what it refuses.
|
||||
| `reserve` | `kind` (`decision` or `research`), optionally `for` (one line: what the record is about) | the next number of that kind, spent forever; answers `kind` and `number` (and `prefix`, the number as the record's name starts with it); see below |
|
||||
|
||||
**The cycle's rules, at the write** (hq playbook 03, which `00-META/checks/cycle.py` enforced on the
|
||||
folders until the import, and hq ADR 0283): a status is one of `open`,
|
||||
folders until the import, and hq ADR 0284): a status is one of `open`,
|
||||
`diagnosing`, `located`, `fixing`, `resolved`, `wontfix`; `diagnosing` and `fixing` need `by`; `located` needs
|
||||
`located-in`; `resolved` is reached only from `fixing` and needs `located-in` and `fixed-by`; a core issue (located in the controller, the node-engine, the tool runner,
|
||||
the SDK, or the catalogue's bus, forge or build agent) resolves with `replay` or `replay-none`; `wontfix`
|
||||
needs a `reason`. A refused change changes nothing and says which rule refused it.
|
||||
|
||||
**Who works on it** (hq ADR 0283). A move to `diagnosing` or `fixing` names in `by` the issue's **worker**: who
|
||||
**Who works on it** (hq ADR 0284). A move to `diagnosing` or `fixing` names in `by` the issue's **worker**: who
|
||||
works on it and its node, as `who@node` (such as `fix-399@g14` or `jochen@shanks`, kept in lower case). The issue
|
||||
keeps its worker (`worker`: `by`, `since`, `active`) while it stays at either status; any other status ends the
|
||||
work. `get` shows it, with `worker-stale`, and each `list` row shows `worker`, `since` and `stale`. A move to either
|
||||
@@ -113,7 +113,10 @@ nothing.
|
||||
**The fixes of an issue are its `fixed-by` and the pull requests linked to it** (link kind `pull-request`; hq issue
|
||||
483): agents link a pull request while the issue is in fixing and give `fixed-by` at the resolve, so a delivery read
|
||||
on `fixed-by` alone missed every fix being verified. Readiness still reads `fixed-by` alone, so a pull request linked
|
||||
and abandoned holds no dependent back. A pull request linked after its delivery moved hears no transition again: each
|
||||
and abandoned holds no dependent back. A pull request closed without merging (`stopped`, and never merged) is **withdrawn**: not
|
||||
a fix awaiting delivery, not delivered, not open; the line says "fix withdrawn: …" when it is the only one. Merged is
|
||||
read from the delivery's `merged_as` or the pull request's own `merged`, never from the state, since `stopped` is
|
||||
also what a closed pull request becomes. A pull request linked after its delivery moved hears no transition again: each
|
||||
sweep asks mesh-delivery for at most five fixes never heard of, of issues not resolved.
|
||||
|
||||
The ready-to-verify note names each fix with when it was delivered, and the issue's worker; the event carries
|
||||
@@ -282,7 +285,7 @@ said (hq issue 483).
|
||||
- `decision-floor`, `research-floor` — the highest decision and research numbers taken where the tracker cannot
|
||||
read, such as an open branch of hq (293 and 45 when this was written: 0293 on an open branch, 045 on main).
|
||||
Read once, when that kind's counter starts; see *Decision and research numbers*.
|
||||
- `worker-stale-after` — how long an issue's worker stays current with nothing done on the issue (hq ADR 0283),
|
||||
- `worker-stale-after` — how long an issue's worker stays current with nothing done on the issue (hq ADR 0284),
|
||||
as a duration; `24h` when empty.
|
||||
- `owners` — the registered list `located-in` may name: repositories, and modules as
|
||||
`mesh-catalog modules/<module>`. An entry of `located-in` is an owner, or an owner followed by a space or a
|
||||
|
||||
@@ -523,7 +523,7 @@ func TestAnIssueIsOpenedNotedLinkedMovedAndListedOverTheBus(t *testing.T) {
|
||||
var kinds []string
|
||||
for _, e := range events {
|
||||
kinds = append(kinds, e.Key)
|
||||
// The move to fixing carries who works on it; every other change, who acted (ADR 0283 §4).
|
||||
// The move to fixing carries who works on it; every other change, who acted (ADR 0284 §6).
|
||||
want := Unproven
|
||||
if e.Key == EventMoved && e.Body["to"] == StatusFixing {
|
||||
want = "fix-365@g14"
|
||||
|
||||
@@ -56,7 +56,7 @@ func deliveredNotResolved(is *Issue) bool { return is.Status == StatusFixing &&
|
||||
// deliveredAt is when the last of an issue's delivered fixes was delivered, as the delivery said it.
|
||||
func deliveredAt(is *Issue) time.Time {
|
||||
var last time.Time
|
||||
for _, pr := range fixPullRequests(is) {
|
||||
for _, pr := range liveFixes(is) {
|
||||
if f := fixOf(is, pr); f != nil && contains(is.Delivered, pr) && f.Since.After(last) {
|
||||
last = f.Since
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func (t *Tracker) sayNotResolved(is *Issue, now time.Time) (bool, error) {
|
||||
worker = is.Worker.By
|
||||
}
|
||||
ago := now.Sub(at).Round(time.Minute)
|
||||
prs := strings.Join(fixPullRequests(is), ", ")
|
||||
prs := strings.Join(liveFixes(is), ", ")
|
||||
if err := t.Notify(notResolvedKey(is.Number), "warning",
|
||||
fmt.Sprintf("issue %d: fixed and delivered, not resolved; %s works on it", is.Number, worker),
|
||||
fmt.Sprintf("Fixed and delivered, not resolved (issue %d)", is.Number),
|
||||
@@ -145,7 +145,7 @@ func (t *Tracker) askUnheard() error {
|
||||
if since.IsZero() {
|
||||
since = t.now()
|
||||
}
|
||||
if _, err := t.recordFix(pr, d.State, d.Why, afterMerge(d.State), since, n); err != nil {
|
||||
if _, err := t.recordFix(pr, d.State, d.Why, d.Merged, since, n); err != nil {
|
||||
return err
|
||||
}
|
||||
heard = true
|
||||
@@ -160,9 +160,10 @@ func (t *Tracker) askUnheard() error {
|
||||
}
|
||||
|
||||
// openPullRequest says whether an issue names a pull request not merged yet, or one whose delivery the tracker
|
||||
// never heard of: work in review is work, and a fix unheard of is not taken for none.
|
||||
// never heard of: work in review is work, and a fix unheard of is not taken for none. One closed without merging
|
||||
// is not open.
|
||||
func openPullRequest(is *Issue) bool {
|
||||
for _, pr := range fixPullRequests(is) {
|
||||
for _, pr := range liveFixes(is) {
|
||||
f := fixOf(is, pr)
|
||||
if f == nil || (!f.Merged && !contains(is.Delivered, pr)) {
|
||||
return true
|
||||
|
||||
@@ -44,13 +44,36 @@ func reached(state string) bool {
|
||||
return state == "delivered" || state == "covered" || state == "retired"
|
||||
}
|
||||
|
||||
// afterMerge says whether a state is one a delivery reaches only once merged.
|
||||
func afterMerge(state string) bool {
|
||||
switch state {
|
||||
case "published", "delivering", "held", "delivered", "failed", "stopped", "covered", "retired":
|
||||
return true
|
||||
// Whether a pull request is merged is what the delivery says of the pull request itself — `merged_as` on a
|
||||
// transition, the pull request's `merged` in checks — never read from the state: `stopped` is also what a pull
|
||||
// request closed without merging becomes (mesh-delivery's `closed` event), and read as merged it was said stuck to
|
||||
// the operator for good (novox/hq issue 483).
|
||||
|
||||
// withdrawn says whether a fix is a pull request closed without merging: no fix at all, neither awaiting delivery,
|
||||
// delivered, nor open.
|
||||
func withdrawn(f *Fix) bool { return f != nil && f.State == "stopped" && !f.Merged }
|
||||
|
||||
// liveFixes is an issue's fixes less those withdrawn: what its delivery, readiness to verify, the stuck sweep and a
|
||||
// stale claim are measured on. fixPullRequests still names the withdrawn ones, so a pull request reopened is heard.
|
||||
func liveFixes(is *Issue) []string {
|
||||
var out []string
|
||||
for _, pr := range fixPullRequests(is) {
|
||||
if !withdrawn(fixOf(is, pr)) {
|
||||
out = append(out, pr)
|
||||
}
|
||||
}
|
||||
return false
|
||||
return out
|
||||
}
|
||||
|
||||
// withdrawnFixes is the pull requests of an issue closed without merging.
|
||||
func withdrawnFixes(is *Issue) []string {
|
||||
var out []string
|
||||
for _, pr := range fixPullRequests(is) {
|
||||
if withdrawn(fixOf(is, pr)) {
|
||||
out = append(out, pr)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func fixOf(is *Issue, pr string) *Fix {
|
||||
@@ -64,7 +87,7 @@ func fixOf(is *Issue, pr string) *Fix {
|
||||
|
||||
// allDelivered says whether an issue names a pull request and every one is delivered.
|
||||
func allDelivered(is *Issue) bool {
|
||||
prs := fixPullRequests(is)
|
||||
prs := liveFixes(is)
|
||||
if len(prs) == 0 {
|
||||
return false
|
||||
}
|
||||
@@ -79,8 +102,11 @@ func allDelivered(is *Issue) bool {
|
||||
// DeliveryLine is the derived delivery state of an issue's fix, in words; empty when fixed-by names no pull
|
||||
// request.
|
||||
func DeliveryLine(is *Issue) string {
|
||||
prs := fixPullRequests(is)
|
||||
prs := liveFixes(is)
|
||||
if len(prs) == 0 {
|
||||
if w := withdrawnFixes(is); len(w) > 0 {
|
||||
return "fix withdrawn: " + strings.Join(w, ", ") + " closed without merging"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
var held, failed, merged, open, unheard []string
|
||||
@@ -182,7 +208,7 @@ func (t *Tracker) afterFix(n uint64) error {
|
||||
}
|
||||
if allDelivered(is) {
|
||||
if unresolved(is) {
|
||||
prs := fixPullRequests(is)
|
||||
prs := liveFixes(is)
|
||||
sort.Strings(prs)
|
||||
if strings.Join(is.VerifyFor, ",") != strings.Join(prs, ",") {
|
||||
// The note first, kept once by its words; then the claim; then the event. A failure before the claim
|
||||
@@ -245,7 +271,7 @@ func stillStuck(is *Issue) bool {
|
||||
if is.Status == StatusWontfix {
|
||||
return false
|
||||
}
|
||||
named := fixPullRequests(is)
|
||||
named := liveFixes(is)
|
||||
for _, f := range is.Fixes {
|
||||
if f.StuckSaid && contains(named, f.PullRequest) && !contains(is.Delivered, f.PullRequest) {
|
||||
return true
|
||||
@@ -296,7 +322,7 @@ func (t *Tracker) OnTransition(e stdio.Envelope) error {
|
||||
if at.IsZero() {
|
||||
at = t.now()
|
||||
}
|
||||
merged := body.MergedAs != "" || afterMerge(body.To)
|
||||
merged := body.MergedAs != ""
|
||||
changed, err := t.recordFix(prKey(body.Repository, body.Number), body.To, body.Why, merged, at, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -315,6 +341,8 @@ type Delivery struct {
|
||||
Why string
|
||||
// Since is when the delivery entered its state, as it says; zero when it does not.
|
||||
Since time.Time
|
||||
// Merged is the pull request's own merged flag, as the forge says it.
|
||||
Merged bool
|
||||
}
|
||||
|
||||
// DeliveryState asks mesh-delivery for one pull request's delivery.
|
||||
@@ -333,11 +361,14 @@ func busDeliveryState(owner string) DeliveryState {
|
||||
Waits string `json:"waits"`
|
||||
Since time.Time `json:"since"`
|
||||
} `json:"delivery"`
|
||||
Pull struct {
|
||||
Merged bool `json:"merged"`
|
||||
} `json:"pull"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &out); err != nil {
|
||||
return Delivery{}, fmt.Errorf("mesh-delivery's checks answered no delivery: %.200s", raw)
|
||||
}
|
||||
return Delivery{State: out.Delivery.State, Why: out.Delivery.Waits, Since: out.Delivery.Since.UTC()}, nil
|
||||
return Delivery{State: out.Delivery.State, Why: out.Delivery.Waits, Since: out.Delivery.Since.UTC(), Merged: out.Pull.Merged}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +417,7 @@ func (t *Tracker) OnMoved(e stdio.Envelope) error {
|
||||
if since.IsZero() {
|
||||
since = t.now()
|
||||
}
|
||||
if _, err := t.recordFix(pr, state, d.Why, afterMerge(state), since, body.Number); err != nil {
|
||||
if _, err := t.recordFix(pr, state, d.Why, d.Merged, since, body.Number); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -452,7 +483,7 @@ func (t *Tracker) Sweep() (int, error) {
|
||||
if err := t.clearStuck(is); err != nil {
|
||||
t.Logf("[mesh-issues] the stuck fix of %d could not be cleared on the operator's channel: %v", n, err)
|
||||
}
|
||||
named := fixPullRequests(is)
|
||||
named := liveFixes(is)
|
||||
for _, f := range is.Fixes {
|
||||
if !contains(named, f.PullRequest) || contains(is.Delivered, f.PullRequest) || !f.Merged || f.MergedAt == nil ||
|
||||
reached(f.State) || f.StuckSaid || now.Sub(*f.MergedAt) < bound {
|
||||
|
||||
@@ -125,7 +125,7 @@ type Verdict struct {
|
||||
func (v Verdict) closes() bool { return v.Verdict == VerdictDuplicate && v.Certain }
|
||||
|
||||
// unresolved says whether an issue may be offered, linked or noted by the triage: at open, diagnosing, located or
|
||||
// fixing (ADR 0283 §5).
|
||||
// fixing (ADR 0284 §7).
|
||||
func unresolved(is *Issue) bool {
|
||||
return is.Status == StatusOpen || is.Status == StatusDiagnosing || is.Status == StatusLocated || is.Status == StatusFixing
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ const (
|
||||
IssueCounter = "issue"
|
||||
)
|
||||
|
||||
// The six statuses of an issue, as the development cycle names them (hq playbook 03, ADR 0275, ADR 0283).
|
||||
// The six statuses of an issue, as the development cycle names them (hq playbook 03, ADR 0275, ADR 0284).
|
||||
// Fixing is the status an agent or person moves an issue to when they start its fix; resolved is reached
|
||||
// only from it (ADR 0283 §3).
|
||||
// only from it (ADR 0284 §5).
|
||||
const (
|
||||
StatusOpen = "open"
|
||||
StatusDiagnosing = "diagnosing"
|
||||
@@ -110,7 +110,7 @@ type Issue struct {
|
||||
ReadyFor []uint64 `json:"ready-for,omitempty"`
|
||||
// Triage is the triage's judgement as it was made, kept before it is applied.
|
||||
Triage *Verdict `json:"triage,omitempty"`
|
||||
// Worker is who works on it, while it is diagnosing or fixing (ADR 0283); nil at every other status.
|
||||
// Worker is who works on it, while it is diagnosing or fixing (ADR 0284); nil at every other status.
|
||||
Worker *Worker `json:"worker,omitempty"`
|
||||
// Opening is the id of the call that opened it, so a write whose answer was lost is known as its own.
|
||||
Opening string `json:"opening"`
|
||||
|
||||
@@ -44,7 +44,7 @@ type Settings struct {
|
||||
// StuckAfter is how long a fix may be merged and not delivered before it is said stuck (ADR 0281), as a
|
||||
// duration such as 2h; DefaultStuckAfter when empty.
|
||||
StuckAfter string `json:"stuck-after,omitempty"`
|
||||
// WorkerStaleAfter is how long an issue's worker stays current with nothing done on it (ADR 0283 §2), as a
|
||||
// WorkerStaleAfter is how long an issue's worker stays current with nothing done on it (ADR 0284 §4), as a
|
||||
// duration such as 24h; DefaultWorkerStaleAfter when empty.
|
||||
WorkerStaleAfter string `json:"worker-stale-after,omitempty"`
|
||||
// DeliveredNotResolvedAfter is how long an issue may stay in fixing once every fix it names is delivered before
|
||||
@@ -626,7 +626,8 @@ func Tools(t *Tracker) []stdio.Tool {
|
||||
Description: "Which issues track which conditions: for every condition key an issue links (link kind condition), " +
|
||||
"or only the keys given, the issues that link it, those not closed (resolved or wontfix) first, each with its status, " +
|
||||
"severity, what it needs, who works on it since when and whether they went quiet (stale), and where its " +
|
||||
"fix is (fix: not-merged, merged, held, failed, unheard or delivered; empty when none is named). The " +
|
||||
"fix is (fix: not-merged, merged, held, failed, unheard, delivered, or withdrawn when every pull request named was " +
|
||||
"closed without merging; empty when none is named). The " +
|
||||
"operator's channel asks it to say in a condition's message which issue tracks it. Refused: a key not of a condition " +
|
||||
"key's shape.",
|
||||
Input: schema(nil, map[string]any{
|
||||
|
||||
@@ -203,7 +203,7 @@ func (h *harness) open(t *testing.T, title string, located ...string) uint64 {
|
||||
return o.Number
|
||||
}
|
||||
|
||||
// fixing moves an issue to fixing, as the agent that starts its fix does (ADR 0283).
|
||||
// fixing moves an issue to fixing, as the agent that starts its fix does (ADR 0284).
|
||||
func (h *harness) fixing(t *testing.T, n uint64) {
|
||||
t.Helper()
|
||||
if _, err := h.Move(MoveAsk{Number: n, Status: StatusFixing, By: "fix@g14"}); err != nil {
|
||||
|
||||
@@ -289,7 +289,7 @@ type View struct {
|
||||
Ready bool `json:"ready"`
|
||||
// Delivery is whether its fix is delivered, in words (ADR 0281).
|
||||
Delivery string `json:"delivery,omitempty"`
|
||||
// WorkerStale says nothing was done on it by its worker, or anyone, for worker-stale-after (ADR 0283 §2).
|
||||
// WorkerStale says nothing was done on it by its worker, or anyone, for worker-stale-after (ADR 0284 §4).
|
||||
WorkerStale bool `json:"worker-stale,omitempty"`
|
||||
// DeliveredNotResolved and StaleClaim are list's flags, said of this issue (novox/hq issue 483).
|
||||
DeliveredNotResolved bool `json:"delivered-not-resolved,omitempty"`
|
||||
|
||||
@@ -330,9 +330,9 @@ type MoveAsk struct {
|
||||
Replay string
|
||||
ReplayNone string
|
||||
Reason string
|
||||
// By is who works on it, who@node: what a move to diagnosing or fixing needs (ADR 0283 §1).
|
||||
// By is who works on it, who@node: what a move to diagnosing or fixing needs (ADR 0284 §2).
|
||||
By string
|
||||
// TakeOver is why the issue is taken over from another worker (ADR 0283 §1).
|
||||
// TakeOver is why the issue is taken over from another worker (ADR 0284 §3).
|
||||
TakeOver string
|
||||
}
|
||||
|
||||
@@ -422,7 +422,7 @@ func (t *Tracker) move(by string, a MoveAsk) (*Moved, error) {
|
||||
if reason != "" && to != StatusWontfix {
|
||||
return nil, fmt.Errorf("reason is what wontfix needs; this moves to %s", to)
|
||||
}
|
||||
// Who works on it (ADR 0283 §1): named on a move to diagnosing or fixing, and on no other.
|
||||
// Who works on it (ADR 0284 §2): named on a move to diagnosing or fixing, and on no other.
|
||||
var worker, takeOver string
|
||||
if worked(to) {
|
||||
if strings.TrimSpace(a.By) == "" {
|
||||
@@ -507,7 +507,7 @@ func (t *Tracker) move(by string, a MoveAsk) (*Moved, error) {
|
||||
out := &Moved{Number: is.Number, From: from, To: to, Changed: changed, By: by, TookOverFrom: work.from, Released: work.released,
|
||||
Said: fmt.Sprintf("Issue %d moved from %s to %s.", is.Number, from, to)}
|
||||
// The event's by is who works on it when the move names them, and who acted otherwise; caller is always who
|
||||
// acted, as the runtime proves it (ADR 0283 §4).
|
||||
// acted, as the runtime proves it (ADR 0284 §6).
|
||||
body := map[string]any{"number": is.Number, "from": from, "to": to, "by": by, "caller": by, "at": t.now()}
|
||||
if is.Worker != nil {
|
||||
since := is.Worker.Since
|
||||
@@ -553,7 +553,7 @@ func (t *Tracker) move(by string, a MoveAsk) (*Moved, error) {
|
||||
}
|
||||
|
||||
// cycleRules is what the development cycle asks of an issue moving from one status to another (playbook 03,
|
||||
// to-be 53 §5, ADR 0283), said as the rule that refused it.
|
||||
// to-be 53 §5, ADR 0284), said as the rule that refused it.
|
||||
func cycleRules(is *Issue, from, to string) error {
|
||||
switch to {
|
||||
case StatusLocated:
|
||||
@@ -562,7 +562,7 @@ func cycleRules(is *Issue, from, to string) error {
|
||||
"(a repository, or \"mesh-catalog modules/<module>\")", is.Number)
|
||||
}
|
||||
case StatusResolved:
|
||||
// Resolved only from fixing: whoever fixed it said so when they started (ADR 0283 §3). A resolved issue
|
||||
// Resolved only from fixing: whoever fixed it said so when they started (ADR 0284 §5). A resolved issue
|
||||
// may still have what it holds corrected.
|
||||
if from != StatusFixing && from != StatusResolved {
|
||||
return fmt.Errorf("issue %d is %s, and an issue is resolved only from fixing: move it to fixing with by, "+
|
||||
@@ -740,7 +740,7 @@ type Row struct {
|
||||
// Delivery is whether its fix is delivered, in words (ADR 0281); empty when fixed-by names no pull request.
|
||||
Delivery string `json:"delivery,omitempty"`
|
||||
// Worker is who works on it and Since since when, while it is diagnosing or fixing; Stale says nobody did
|
||||
// anything on it for worker-stale-after, so it may be taken over without a reason (ADR 0283).
|
||||
// anything on it for worker-stale-after, so it may be taken over without a reason (ADR 0284 §4).
|
||||
Worker string `json:"worker,omitempty"`
|
||||
Since *time.Time `json:"since,omitempty"`
|
||||
Stale bool `json:"stale,omitempty"`
|
||||
|
||||
@@ -259,7 +259,7 @@ func TestAnIssueWithAFolderIsNumberedTitledAndPointsAtItsFolder(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// The cycle's rules at the write (to-be 53 §5, ADR 0283): one case per row, each refused case changing nothing.
|
||||
// The cycle's rules at the write (to-be 53 §5, ADR 0284): one case per row, each refused case changing nothing.
|
||||
// A case starts from open, or, with from, from that status reached first (fixing by fix@g14).
|
||||
func TestTheCyclesRulesHoldAtTheWrite(t *testing.T) {
|
||||
const fx = StatusFixing
|
||||
|
||||
@@ -43,8 +43,11 @@ type Tracked struct {
|
||||
// when fixed-by names no pull request; failed, held, merged (not delivered yet), not-merged, unheard (nothing
|
||||
// heard from the delivery), or delivered (every pull request it names).
|
||||
func FixState(is *Issue) string {
|
||||
prs := fixPullRequests(is)
|
||||
prs := liveFixes(is)
|
||||
if len(prs) == 0 {
|
||||
if len(withdrawnFixes(is)) > 0 {
|
||||
return "withdrawn"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
var failed, held, merged, open, unheard bool
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package main
|
||||
|
||||
// Who works on an issue (novox/hq ADR 0283). A move to diagnosing or fixing names, in `by`, the issue's worker:
|
||||
// Who works on an issue (novox/hq ADR 0284). A move to diagnosing or fixing names, in `by`, the issue's worker:
|
||||
// the agent or person and its node, as who@node. The issue keeps its worker while it stays at either status, and
|
||||
// `get` and `list` show it with since. A move to either by someone else while the worker is not stale is refused,
|
||||
// naming the worker; a take-over gives a reason, and the reason is noted. A worker with no move, note, link or set
|
||||
@@ -95,7 +95,7 @@ type workOutcome struct {
|
||||
released string // the worker whose work ended, on a move to a status that has none
|
||||
}
|
||||
|
||||
// workerFor applies a move's worker to an issue, under ADR 0283 §1 and §2: another worker not stale is refused
|
||||
// workerFor applies a move's worker to an issue, under ADR 0284 §3 and §4: another worker not stale is refused
|
||||
// without a take-over reason; a take-over reason with no other worker to take over from is refused.
|
||||
func workerFor(is *Issue, to, by, takeOver string, now time.Time, staleAfter time.Duration) (workOutcome, error) {
|
||||
var out workOutcome
|
||||
@@ -148,5 +148,5 @@ func takeOverNote(o workOutcome, by, reason string, prev *Worker) string {
|
||||
if reason != "" {
|
||||
s += ". Why: " + reason
|
||||
}
|
||||
return s + ". (novox/hq ADR 0283)"
|
||||
return s + ". (novox/hq ADR 0284)"
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ func clocked(h *harness) *time.Time {
|
||||
}
|
||||
|
||||
// A move to diagnosing or fixing by someone else while the worker is current is refused, naming the worker, and
|
||||
// changes nothing (ADR 0283 §1).
|
||||
// changes nothing (ADR 0284 §3).
|
||||
func TestAnotherWorkerIsRefusedNamingTheWorker(t *testing.T) {
|
||||
for _, to := range []string{StatusDiagnosing, StatusFixing} {
|
||||
t.Run(to, func(t *testing.T) {
|
||||
@@ -76,7 +76,7 @@ func TestTheWorkerMovesOn(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// A take-over gives a reason, the reason is noted, and the moved event says from whom (ADR 0283 §1, §4).
|
||||
// A take-over gives a reason, the reason is noted, and the moved event says from whom (ADR 0284 §3, §6).
|
||||
func TestATakeOverNeedsAReasonAndIsNoted(t *testing.T) {
|
||||
h := newHarness(t)
|
||||
clocked(h)
|
||||
@@ -103,7 +103,7 @@ func TestATakeOverNeedsAReasonAndIsNoted(t *testing.T) {
|
||||
}
|
||||
|
||||
// A worker with nothing done on it past worker-stale-after is stale: list and get say so, and it may be taken over
|
||||
// without a reason, noted (ADR 0283 §2). A caller's note keeps it live; the tracker's own does not.
|
||||
// without a reason, noted (ADR 0284 §4). A caller's note keeps it live; the tracker's own does not.
|
||||
func TestAStaleWorkerIsShownAndMayBeTakenOverWithoutAReason(t *testing.T) {
|
||||
h := newHarness(t)
|
||||
now := clocked(h)
|
||||
@@ -193,7 +193,7 @@ func TestAMoveOnEndsTheWork(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// The duplicate check and the triage treat fixing as not closed, like diagnosing and located (ADR 0283 §5).
|
||||
// The duplicate check and the triage treat fixing as not closed, like diagnosing and located (ADR 0284 §7).
|
||||
func TestFixingIsNotClosed(t *testing.T) {
|
||||
for _, s := range Statuses {
|
||||
want := s != StatusResolved && s != StatusWontfix
|
||||
@@ -261,7 +261,7 @@ func TestAResolvedIssueMayBeCorrected(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// A caller's link and set keep the worker current, as a note does (ADR 0283 §4).
|
||||
// A caller's link and set keep the worker current, as a note does (ADR 0284 §4).
|
||||
func TestALinkAndASetKeepTheWorkerCurrent(t *testing.T) {
|
||||
for _, act := range []string{"link", "unlink", "set"} {
|
||||
t.Run(act, func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user