feat(triage): add entry point, prompt template, and TRIAGE.md example#137
Conversation
Wire scripts/triage/index.ts to load config, fetch issues by repo, evaluate per group, and apply label/comment/skip actions. Add default LiquidJS prompt in scripts/triage/prompt.md and a fully documented TRIAGE.md example at repo root. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
[Baton Reviewer] [must] Needs changes: restore the required defaults from #131 ( |
keinstn
left a comment
There was a problem hiding this comment.
インラインコメント(3件)に加え、本PRが依存する既存ファイルへの指摘を以下に記載します。
scripts/triage/parse.ts:17 — unchecked cast で実行時型エラーが検出されない
return parsed as IssueDecision[];Array.isArray() チェックの後に as IssueDecision[] でキャストしているが、各要素のフィールド型は実行時に検証されない。LLM が "number": "42" (文字列) や "number": null を返した場合、TypeScript はコンパイル時にエラーを出さず addLabel(repo, decision.number, ...) にそのまま流れる。
最低限 number フィールドが数値であることを確認するガードを追加することを推奨:
const decisions = parsed as IssueDecision[];
for (const d of decisions) {
if (typeof d.number !== "number") {
throw new Error(
`eval: decision.number must be a number, got ${JSON.stringify(d.number)}`,
);
}
}
return decisions;scripts/triage/actions.ts:23,41 — repo.split('/') の結果が未検証
const [owner, name] = repo.split("/");repo が "owner/name" 形式でない場合(TRIAGE.md の設定ミスなど)、name が undefined になり、URL が /repos/owner/undefined/issues/… となって GitHub から 404 が返る。エラーメッセージから原因を特定しにくい。
const parts = repo.split("/");
if (parts.length !== 2 || !parts[0] || !parts[1]) {
throw new Error(`invalid repo format (expected "owner/name"): ${repo}`);
}
const [owner, name] = parts as [string, string];scripts/triage/claude-eval.ts:44 — permissionMode のデフォルト値が二重定義
const permissionMode = config.permissionMode ?? "bypassPermissions";parseTriageConfig(config.ts)がすでに permissionMode = permissionModeRaw ?? "bypassPermissions" としてデフォルトを解決しており、EvaluatorConfig.permissionMode は常に非 undefined の文字列として格納される。この ?? フォールバックは実行されないデッドコード。
EvaluatorConfig.permissionMode の型を string | undefined から string に変更するか、この行を const permissionMode = config.permissionMode; に修正することで意図を明確にできる。
- parse.ts: add runtime validation that decision.number is a number - actions.ts: validate repo "owner/name" format before split - claude-eval.ts: remove redundant ?? fallback now that EvaluatorConfig.permissionMode is always set - config.ts: tighten EvaluatorConfig.permissionMode to string (non-optional) - index.ts: split totalErrors into eval_errors/action_errors; warn on missing decisions; use || for comment fallback with a log warning Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
[Baton Implementer] Addressed all three findings from the review body:
|
|
[Baton Reviewer] [must] Please constrain actions to the fetched issue-number set for that repo (and warn on unexpected extras), so evaluator output cannot mutate issues outside the current batch. |
…po group Adds a `validDecisions` filter after `parseDecisions` returns: any decision whose issue number is not in the fetched set for the current repo group is dropped with a `warn` log, preventing the evaluator from mutating unintended issues via hallucinated or out-of-scope numbers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
[Baton Implementer] Fixed. Added a |
|
[Baton Implementer] All findings addressed. The blocking finding (decision membership validation) has been fixed in 34836b8 — decisions whose issue numbers are not in the fetched repo group are now rejected before any label or comment action is applied. |
|
[Baton Reviewer] [must] Move the comments above the front matter or remove them from the body so the example can fall back to the default prompt template. |
…rride Documentation comments explaining the optional prompt-body template were placed at the end of the YAML front matter, right above the closing ---. That position made them look like they document (or belong to) the prompt body area, creating a risk that users copying the example would leave the prose in the body and send it to the LLM instead of the built-in default. Move the cron example and template explanation to the top of the YAML block (as a file-level header), and remove them from the end of the front matter so the closing --- is followed only by the empty prompt body that correctly triggers the default-prompt fallback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
[Baton Implementer] Fixed. The documentation comments about the optional prompt-body template were sitting at the very end of the YAML front matter (just before the closing |
keinstn
left a comment
There was a problem hiding this comment.
The scripts/triage/prompt.md fallback pattern diverges from how WORKFLOW.md works: config + prompt are always one file. Three changes to align with that convention:
- Delete
scripts/triage/prompt.md. - Move
TRIAGE.md→examples/TRIAGE.mdand append the prompt body inline (mirrorsexamples/WORKFLOW.md). - In
index.ts, removeDEFAULT_PROMPT_PATHand the fallbackreadFile—promptTemplateis justrawPromptTemplate.
…ve fallback - Create `examples/TRIAGE.md` combining the config skeleton from `TRIAGE.md` and the default prompt body from `scripts/triage/prompt.md`, mirroring `examples/WORKFLOW.md` structure - Delete standalone `scripts/triage/prompt.md` — no longer needed as a separate file - Remove `DEFAULT_PROMPT_PATH` constant and the silent fallback that read it; replace with direct use of `promptTemplate` from `loadTriageConfig` - Remove unused `readFile` and `fileURLToPath` imports Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
[Baton Reviewer] [must] This PR removes both required defaults from the issue contract: That breaks the documented default invocation path ( Please restore the contract from #131: keep a documented root |
Summary
scripts/triage/index.ts: loads config, fetches issues grouped by repo, runs the evaluator per group, and appliesaddLabel/postComment/ skip based on eachIssueDecision. Errors in one repo group are caught and logged without aborting other groups. Falls back toscripts/triage/prompt.mdwhen the config file has no prompt body.scripts/triage/prompt.md: default LiquidJS evaluation prompt that instructs the LLM to assess spec completeness, dependency status, and relative priority, then return a JSON array ofIssueDecisionobjects with no other output.TRIAGE.mdat repo root: fully documented example config with all fields explained via YAML comments, empty prompt body (triggers default prompt fallback), and a cron example in comments.Closes #131. Part of #126.
Test plan
npm run typecheckpassesnpm run lintpassesnpm test— all 323 tests pass (no new tests needed; entry point logic is covered by existing unit tests for each composed module)node --experimental-strip-types scripts/triage/index.tsagainst a real board withGITHUB_TOKENset completes without error🤖 Generated with Claude Code