Session infrastructure: OpenRouter + tree-split reducer + observer→LLM Team + scrum_applier #11
@ -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];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user