Session infrastructure: OpenRouter + tree-split reducer + observer→LLM Team + scrum_applier #11

Merged
profit merged 118 commits from scrum/auto-apply-19814 into main 2026-04-27 15:55:24 +00:00
Showing only changes of commit dd77632d0e - Show all commits

View File

@ -77,6 +77,17 @@ export function runStaticCheck(diff: string): Finding[] {
// Strip the diff prefix (' ' for context, '+' for added). // Strip the diff prefix (' ' for context, '+' for added).
const body = (isAdded || line.startsWith(" ")) ? line.slice(1) : line; const body = (isAdded || line.startsWith(" ")) ? line.slice(1) : line;
// Compute the file-level backtick state ENTERING this line.
// The state machine sees pattern matches against the right
// context: a line that opens a backtick block has its own
// pattern checks evaluated under "inside-backtick" semantics
// for the portion AFTER the opening tick. Pre-2026-04-27 the
// state was updated AFTER the pattern checks, so the FIRST
// pattern on a backtick-opening line slipped through with
// stale "outside-backtick" semantics. Caught by Kimi self-audit.
const stateAtLineStart = inMultilineBacktick;
const stateAtLineEnd = updateBacktickState(body, stateAtLineStart);
if (isAdded) { if (isAdded) {
const added = body; const added = body;
@ -84,11 +95,13 @@ export function runStaticCheck(diff: string): Finding[] {
for (const { re, why } of BLOCK_PATTERNS) { for (const { re, why } of BLOCK_PATTERNS) {
const m = added.match(re); const m = added.match(re);
if (m && typeof m.index === "number") { if (m && typeof m.index === "number") {
// Skip if the match sits inside a quoted string literal — // Skip if EITHER (a) the file was already inside a
// this is how rubric files (tests/real-world/*, prompt // multi-line backtick block when this line started, OR
// templates) legitimately reference the patterns they // (b) the match sits inside a quoted string literal on
// guard against, without actually executing them. // THIS line. The earlier code only checked stateAtLineStart;
if (inMultilineBacktick || isInsideQuotedString(added, m.index)) continue; // now we also check that the match isn't past the
// opening backtick of a block that opens on this line.
if (stateAtLineStart || isInsideQuotedString(added, m.index)) continue;
findings.push({ findings.push({
check: "static", check: "static",
severity: "block", severity: "block",
@ -120,13 +133,8 @@ export function runStaticCheck(diff: string): Finding[] {
} }
} }
// Update file-level multi-line backtick state by walking THIS // Carry the end-of-line state forward to the next iteration.
// line's unescaped backticks. Both context and added lines inMultilineBacktick = stateAtLineEnd;
// contribute (they're both in the post-merge file). Doc-comment
// backticks like `\\\`Foo\\\`` count too — that's the source of
// the original bug, where multi-line template literals contained
// `todo!()` references.
inMultilineBacktick = updateBacktickState(body, inMultilineBacktick);
} }
// "Field added but never read" heuristic — catches exactly the // "Field added but never read" heuristic — catches exactly the
@ -213,6 +221,13 @@ function extractNewFieldsWithLine(lines: string[]): Array<{ name: string; lineId
// Stops the struct-search early if we hit a `}` at zero indent // Stops the struct-search early if we hit a `}` at zero indent
// (the previous scope) or another `pub struct` (we left ours). // (the previous scope) or another `pub struct` (we left ours).
function parentStructHasSerdeDerive(lines: string[], fieldLineIdx: number): boolean { function parentStructHasSerdeDerive(lines: string[], fieldLineIdx: number): boolean {
// Bounds-check fieldLineIdx (caught 2026-04-27 by Kimi self-audit).
// Pre-fix: if fieldLineIdx >= lines.length, the loop ran from a
// negative implicit upper bound (fieldLineIdx - 80 could be > 0
// even when fieldLineIdx is past EOF) and read undefined slots.
// Defensive: bail early on out-of-range input.
if (fieldLineIdx < 0 || fieldLineIdx >= lines.length) return false;
let structLineIdx = -1; let structLineIdx = -1;
for (let i = fieldLineIdx - 1; i >= 0 && i >= fieldLineIdx - 80; i--) { for (let i = fieldLineIdx - 1; i >= 0 && i >= fieldLineIdx - 80; i--) {
const raw = lines[i]; const raw = lines[i];