mesh-issues: check every new issue for a duplicate; claude-code: one judgement with no tools (hq ADR 0278)
mesh/merge-gate pass: builds claude-code, mesh-issues → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 o…
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 claude-code, mesh-issues → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 o…
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
The operator asked for duplicate issues to be consolidated as they are opened. The tracker consumes its own opened event and asks the agent on its node, through claude-code's new claude_code_judge, whether an older issue not resolved asks the same question; it closes only the newer issue of a certain duplicate, keeping its report on the older one, and fails loudly when it cannot judge. groom runs the same judgement over the backlog as a dry run, applied on confirm.
This commit is contained in:
@@ -28,6 +28,9 @@ type Tracker struct {
|
||||
Sleep func(time.Duration)
|
||||
// Records is the record the import reads; nil is the records module over the bus.
|
||||
Records Records
|
||||
// Judge is the duplicate check's way to a model (ADR 0278); nil judges nothing.
|
||||
Judge Judgement
|
||||
groom grooming
|
||||
}
|
||||
|
||||
// The events this module says, one per kind of change.
|
||||
@@ -317,7 +320,10 @@ func known(status string) bool {
|
||||
|
||||
// Move changes an issue's status under the cycle's rules (to-be 53 §5). A refused move changes nothing and
|
||||
// says which rule refused it.
|
||||
func (t *Tracker) Move(a MoveAsk) (*Moved, error) {
|
||||
func (t *Tracker) Move(a MoveAsk) (*Moved, error) { return t.move(t.by(), a) }
|
||||
|
||||
// move is Move as the one who acts: a caller, or the duplicate check.
|
||||
func (t *Tracker) move(by string, a MoveAsk) (*Moved, error) {
|
||||
to := strings.TrimSpace(a.Status)
|
||||
if !known(to) {
|
||||
return nil, fmt.Errorf("status %q is not one of %s", to, strings.Join(Statuses, ", "))
|
||||
@@ -403,7 +409,7 @@ func (t *Tracker) Move(a MoveAsk) (*Moved, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := &Moved{Number: is.Number, From: from, To: to, Changed: changed, By: t.by(),
|
||||
out := &Moved{Number: is.Number, From: from, To: to, Changed: changed, By: by,
|
||||
Said: fmt.Sprintf("Issue %d moved from %s to %s.", is.Number, from, to)}
|
||||
body := map[string]any{"number": is.Number, "from": from, "to": to, "by": out.By, "at": t.now()}
|
||||
for _, c := range changed {
|
||||
@@ -466,12 +472,15 @@ type Noted struct {
|
||||
}
|
||||
|
||||
// Note appends a note to an issue; a note is never edited.
|
||||
func (t *Tracker) Note(n uint64, text string) (*Noted, error) {
|
||||
func (t *Tracker) Note(n uint64, text string) (*Noted, error) { return t.note(t.by(), n, text) }
|
||||
|
||||
// note is Note as the one who acts.
|
||||
func (t *Tracker) note(by string, n uint64, text string) (*Noted, error) {
|
||||
text, err := multiLine("text", text, MostNote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
note := Note{By: t.by(), At: t.now(), Text: text}
|
||||
note := Note{By: by, At: t.now(), Text: text}
|
||||
is, err := t.change(n, func(is *Issue) error {
|
||||
is.Notes = append(is.Notes, note)
|
||||
return nil
|
||||
@@ -507,7 +516,10 @@ type Linked struct {
|
||||
}
|
||||
|
||||
// Link adds or removes a typed link.
|
||||
func (t *Tracker) Link(a LinkAsk) (*Linked, error) {
|
||||
func (t *Tracker) Link(a LinkAsk) (*Linked, error) { return t.link(t.by(), a) }
|
||||
|
||||
// link is Link as the one who acts.
|
||||
func (t *Tracker) link(by string, a LinkAsk) (*Linked, error) {
|
||||
kind := strings.TrimSpace(a.Kind)
|
||||
target, err := checkTarget(kind, a.Target)
|
||||
if err != nil {
|
||||
@@ -525,7 +537,7 @@ func (t *Tracker) Link(a LinkAsk) (*Linked, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
by, at := t.by(), t.now()
|
||||
at := t.now()
|
||||
is, err := t.change(a.Number, func(is *Issue) error {
|
||||
for i, l := range is.Links {
|
||||
if l.Kind == kind && l.Target == target {
|
||||
|
||||
Reference in New Issue
Block a user