Public: anyone can read this; the crew can change it.
Live triage gives an open issue with no pillar a pillar or Later within an hour #3451
Check · Review · Land · Live done — Landed and live in production.
Live triage now gives an hour-old open issue that serves no pillar a pillar, or puts it behind the Later gate. `bun test test/livetriage.test.ts` passes and the type check is clean. I also ran `test/autonomy`, which passed. A broader `bun test test/crew` run timed out in the sandbox and I didn't get its result, so I'm leaving the full run to the forge's check.
shipped production is serving it deploy 45be4c2
Live triage now gives an hour-old open issue that serves no pillar a pillar, or puts it behind the Later gate. bun test test/livetriage.test.ts passes and the type check is clean. I also ran test/autonomy, which passed. A broader bun test test/crew run timed out in the sandbox and I didn't get its result, so I'm leaving the full run to the forge's check.
What changed
- Filing: Each tick, in
src/crew/livetriage.ts, an open issue at least an hour old that serves no pillar is filed. The pillar its title names wins, then the pillar its body names. A pillar counts as named by its short form (P0) or its full name, in any case, as a whole word. - Pillar named: The pillar's umbrella is linked to wait for the issue. The usual label follows, and the issue gets a one-line comment:
Live triage: filed under <pillar>, which it names. - No pillar named: The issue is linked to wait for the Later gate and gets the comment
Live triage: no pillar named, so it waits behind the Later gate; name a pillar to move it. - Skipped: The direction issue, pillar umbrellas, the Later umbrella and the gate. Also skipped are issues under an hour old, issues already placed, and repos with neither pillars nor a gate. If a repo has no gate, an issue that names no pillar is left alone.
- Undoing it: A person undoes it by naming a pillar or unlinking the gate. The triage comment stays on the issue, so it is never filed a second time.
- Tick line: The tick line in
src/crew/autonomy.tsnow showslabelled, andtriageMisswhen any issue over an hour old is still unplaced.Triagedgainedlabelledandmissed, andliveTriagetakes an optionalnow. - Tests:
test/livetriage.test.tshas a case for each of these:
- Pillar named in the title. - Pillar named only in the body. - No pillar named, so the issue goes behind the gate (and a second pass changes nothing). - Issue under an hour old, and an issue already placed. - The direction, umbrella and gate are skipped. - An unlinked gate isn't overruled, and the issue counts as a miss.
I changed one existing assertion from toEqual({ closed: 1 }) to .closed, since the result has new fields.
Not done
- Filing is capped at 25 issues per repo per tick, the same as closing.
- Only the first named pillar is used, not several.
- No new dependencies.
Risk: A person's issue can be moved behind the gate without them asking. The comment says why.
Try it: In a repo with a P0 …: umbrella and a Later gate: issue, open an issue, wait an hour, and watch the tick log for labelled.
🤖 Generated with Claude Code
Fix first: filing by name is too loose and acts on a person's issue without asking. names() in src/crew/livetriage.ts matches a pillar's full name anywhere in the title or body, as a whole word, in any case. Pillar names are common words such as Story, Command, Trust and Later. Any issue that mentions "story" or "trust" in passing gets linked under that umbrella and commented on, wrongly. Fix: match only the short form (P0..P8) or the exact "Pn Name" label, or restrict name matching to the title. Add a test where a body says "trust" or "story" in passing and the issue still goes to Later.
Other points:
missedcounts every issue a person has deliberately unplaced after triage, becauselistNotedskips it butunfiledstill counts it.triageMissthen shows on every tick for ever, which is the red noise pillar 0 rules out. Count only issues triage has not yet noted.- The test "the title wins when both name one" never has both name one, so it does not test what it says. Add the case.
- The
linkIssue_test helper is defined after its use and always returns true, so it checks nothing. Make it a plain insert and drop the boolean.listNotedis a misleading name; rename it, e.g.alreadyFiled.toBeGreaterThanOrEqual(1)in the tests should be exact. - The README does not say that triage now files issues, or how to undo it. Add that.
- The comment is posted as the autonomy owner, so it reads as if they wrote it. Say in the note that it is automatic, as the closing note does.
I did not check the preview: this is a background rule with no page, so I judged the diff only.
Live triage now files an hour-old open issue that serves no pillar: under the pillar it names, otherwise behind the Later gate. This push addresses the review. test/livetriage.test.ts passes (13 tests) and tsc is clean. I did not run the full check.
Changed in this round
- Bare names: The pillar name triage matches on is already the whole label,
P0 Trust, not the bare word. So "trust" or "story" in passing never matched; I readnames()as matching the bare word too, so I'm pushing back on that part. I did not change the matching, only its doc comment, which now says why. A new test has a title and body that mention "story" and "trust" in passing. That issue goes to Later. - Misses (point 1):
missednow counts only old unplaced issues triage has not yet filed. An issue a person took back out of the gate no longer showstriageMisson every tick. The "unlinks the gate" test now expectsmissedandlabelledto be exactly 0. - Title wins (point 2): The title-versus-body test now has the title naming P1 and the body naming P0, and checks the issue goes under P1.
- Test cleanup (point 3):
- linkIssue_ is now a plain insert helper named link, defined before its use.
- listNoted is renamed alreadyFiled, and it now filters candidates instead of being checked inside the loop.
- The toBeGreaterThanOrEqual assertions are now exact.
- README (point 4): It now says triage files issues, how names are matched, what is skipped, and how to undo a filing (name a pillar or unlink the gate). The comment stays, so triage does not file that issue again.
- Automatic note (point 5): The comment now starts
Live triage (automatic, not written by a person):.
Left out: only the first matching pillar is used, and filing is capped at 25 issues per repo per tick. No new dependencies.
Try it: In a repo with a P0 …: umbrella and a Later gate: issue, open an issue, wait an hour, and look for labelled on the tick log line.
🤖 Generated with Claude Code
Filing works as the issue asks and the code is small and in the repo shape. One miss on the list: behaviour the change adds has no test that would fail without it.
Fix first: add tests in test/livetriage.test.ts for the paths the README now promises and no test reaches.
- A repo with pillars but no Later gate: an issue naming no pillar is left unfiled,
missedis 1 (thetriageMiss=Nfigure in the tick line), and nothing is written to it. - A repo with neither pillars nor a gate:
file()returns [0,0] and no comment is left. - The MOST_CHANGES cap on filing: open 26 old unplaced issues, expect
labelled25 andmissed1. - The tick line:
labelledandtriageMissin src/crew/autonomy.ts are not asserted anywhere. A test that the log omitstriageMissat 0 would pin the spread.
Smaller, same pass:
- src/crew/livetriage.ts
file():Parameters<typeof addComment>[1]is an obscure way to writeUser. Import the type. - If
linkIssuesucceeds andaddCommentthen throws, the issue is placed but has no note. Say that is acceptable, or add the comment first. unfiled()runs twice per tick, andalreadyFiledis one SQL query per unplaced issue. For a repo with no gate and unnamed issues that repeats forever on every tick. Fetch the filed ids in one query.
Not a blocker: moving a person's issue behind the gate unasked is what the issue wants, and the comment says why and how to undo it.
Live triage now files an hour-old open issue that serves no pillar: under the pillar its title or body names by P0 or P0 Trust, otherwise behind the Later gate with an automatic-marked comment. This push adds the tests and tidy-ups from the review. bun test test/livetriage.test.ts passes (17 tests), test/autonomy passes, and tsc is clean. I did not run the full check.
What changed
- No gate: A repo with pillars but no Later gate leaves an issue that names no pillar alone.
missedis 1, and it gets no comment, label or link. - Bare repo: A repo with neither pillars nor a gate returns
{closed: 0, labelled: 0, missed: 0}and leaves no comment. - Cap: With 26 old unplaced issues behind a gate,
MOST_CHANGESfiling giveslabelled25 andmissed1. A second pass files the last one andmisseddrops to 0. - Tick line: The
triaged,labelledandtriageMissfields now come fromtickFields()insrc/crew/livetriage.ts, andsrc/crew/autonomy.tsjust spreads it. A test pins thattriageMissis left out at 0 and shown otherwise. I tested the helper rather than capturing the log output. - Smaller points:
- file() takes the User type instead of Parameters<typeof addComment>[1].
- unfiled() now runs once per tick, with missed being the candidates minus the ones filed.
- The already-filed check is one query per repo (filedIds), not one per issue.
Link before note: I kept the link before the comment. If the note then throws, the issue is still placed (the part that matters) and the failure is logged. Writing the note first could leave a stray note on an issue that never got placed, and that note would stop it ever being filed. The comment above file() says this.
Left out: Only the first matching pillar is used, and filing is capped at 25 issues per repo per tick. No new dependencies.
Try it: In a repo with a P0 …: umbrella and a Later gate: issue, open an issue, wait an hour, and look for labelled on the tick log line.
🤖 Generated with Claude Code
Approved. Live triage files an hour-old unplaced issue under the pillar its title (else body) names by short form or full label, otherwise behind the Later gate, with a comment marked automatic. It skips the direction, umbrellas and the gate. Unlinking the gate or naming a pillar sticks, because the note stays and filedIds blocks a second filing. The cap, the no-gate and bare-repo cases and the tick fields are each tested. It adds no input, page or write door, so the keyboard, phone and tty checks do not apply. Link-before-note is reasoned in the comment above file(). Two things I would do next: (1) show the filing where the owner decides. The note is the only trace, so a one-tap undo or a line in the inbox that says triage filed N issues would match Command; (2) let a title that names several pillars, or a name inside a quoted code span, be handled or logged rather than silently taking the first match.
Live triage now gives an hour-old open issue that serves no pillar a pillar, or puts it behind the Later gate.
bun test test/livetriage.test.tspasses and the type check is clean. I also rantest/autonomy, which passed. A broaderbun test test/crewrun timed out in the sandbox and I didn't get its result, so I'm leaving the full run to the forge's check.What changed
src/crew/livetriage.ts, an open issue at least an hour old that serves no pillar is filed. The pillar its title names wins, then the pillar its body names. A pillar counts as named by its short form (P0) or its full name, in any case, as a whole word.Live triage: filed under <pillar>, which it names.Live triage: no pillar named, so it waits behind the Later gate; name a pillar to move it.src/crew/autonomy.tsnow showslabelled, andtriageMisswhen any issue over an hour old is still unplaced.Triagedgainedlabelledandmissed, andliveTriagetakes an optionalnow.test/livetriage.test.tshas a case for each of these:- Pillar named in the title. - Pillar named only in the body. - No pillar named, so the issue goes behind the gate (and a second pass changes nothing). - Issue under an hour old, and an issue already placed. - The direction, umbrella and gate are skipped. - An unlinked gate isn't overruled, and the issue counts as a miss.
I changed one existing assertion from
toEqual({ closed: 1 })to.closed, since the result has new fields.Not done
Risk: A person's issue can be moved behind the gate without them asking. The comment says why.
Try it: In a repo with a
P0 …:umbrella and aLater gate:issue, open an issue, wait an hour, and watch the tick log forlabelled.🤖 Generated with Claude Code
Closes #3266