@jwilger/pi-development-system 0.32.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,7 +31,7 @@ You are a reviewer. You find defects you can demonstrate from the code and propo
31
31
 
32
32
  ## What counts
33
33
 
34
- Look for correctness bugs, regressions in existing behavior, security issues, and changed behavior with no test. Each finding needs a realistic triggering input or state. Before reporting, try to refute it: a covering test, a nearby guard, a type that rules the case out. Drop it if the refute holds; if it nearly holds, say what evidence is missing. Do not report untraced speculation or nits. Report style only if asked, labeled as style, after defects. The verdict is `blocking` when any finding is blocking or should-fix, otherwise `no-blocking`. Zero findings is a valid result: write `- none` under Findings.
34
+ Look for correctness bugs, regressions in existing behavior, security issues, and changed behavior with no test. Each finding needs a realistic triggering input or state. Before reporting, try to refute it: a covering test, a nearby guard, a type that rules the case out. Drop it if the refute holds; if it nearly holds, say what evidence is missing. Do not report untraced speculation or nits. Report style only if asked, labeled as style, after defects. The verdict is `blocking` when any finding is blocking or should-fix, otherwise `no-blocking`. Zero findings is a valid result: write `- none` under Findings. Findings holds only finding lines; put what you checked and refuted under Sources inspected.
35
35
 
36
36
  A fix is the smallest local change or deletion that removes the demonstrated defect. Do not propose new abstractions, frameworks, or broad refactors.
37
37
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jwilger/pi-development-system",
3
- "version": "0.32.0",
3
+ "version": "0.34.0",
4
4
  "description": "A pi extension package representing a seasoned approach to software development using a full AI SDLC.",
5
5
  "keywords": [
6
6
  "pi-package"
@@ -6,5 +6,5 @@ Review the work in progress with the code-review skill.
6
6
 
7
7
  1. Call `devsys_review_start` (slice and diff range: $ARGUMENTS; omit either to use the active slice and `HEAD`; only the `HEAD` range clears the commit gate).
8
8
  2. Run the `agent_spawn` payload it returns, unchanged.
9
- 3. Pass the reviewer's packet verbatim to `devsys_review_record` with the same `diffDigest` and `diffRange` the start reply gave.
9
+ 3. Pass the reviewer's packet verbatim to `devsys_review_record` with the same `slice`, `diffDigest` and `diffRange` the start reply gave.
10
10
  4. Report `review: N/R clean` and the next action. If the next action is `fix-findings`, list each blocking and should-fix finding with its `path:line`.
@@ -18,7 +18,7 @@ Review is a soft gate (`review.unsatisfied`): skipping it needs a recorded depar
18
18
  2. Run that `agent_spawn` unchanged. The reviewer is a top-level agent, not a
19
19
  child of this conversation, so it does not inherit your context.
20
20
  3. Pass the reviewer's packet, verbatim, to `devsys_review_record` with the
21
- `diffDigest` the start reply gave, and the same `diffRange` if you gave one
21
+ `slice` and `diffDigest` the start reply gave, and the same `diffRange` if you gave one
22
22
  (`packets: [...]`; all lens packets of one
23
23
  round in one call). A packet is recorded once, in the round its header names;
24
24
  if the diff changed since the start, the round is refused: start again.
@@ -177,6 +177,6 @@ export function reviewerTask(input: {
177
177
  "### Verdict",
178
178
  "no-blocking | blocking",
179
179
  "",
180
- "Severity: blocking = demonstrable defect or broken non-negotiable; should-fix = real defect or missing test with a realistic trigger; nit = style or report-only. The verdict is `blocking` when any finding is blocking or should-fix, otherwise `no-blocking`. With zero findings (a valid result) write `- none` under Findings.",
180
+ "Severity: blocking = demonstrable defect or broken non-negotiable; should-fix = real defect or missing test with a realistic trigger; nit = style or report-only. The verdict is `blocking` when any finding is blocking or should-fix, otherwise `no-blocking`. With zero findings (a valid result) write `- none` under Findings. Findings holds only finding lines: put what you checked and refuted under Sources inspected or after the verdict.",
181
181
  ].join("\n");
182
182
  }
@@ -18,15 +18,19 @@ const HEADER = new RegExp(
18
18
  // Reviewers add text after the location (`fn`, a second location); the first backticked one is the finding's.
19
19
  const FINDING = /^-\s*\[([^\]]+)\]\s+(\S+)\s+(?:`([^`]+)`[^—–]*?\s+)?[—–-]\s+(.+)$/;
20
20
  // An indented line under a finding is its detail (trigger, fix), unless it is itself a finding.
21
- const isDetail = (line: string): boolean => /^\s+/.test(line) && !/^\s*-\s*\[/.test(line);
21
+ const isDetail = (line: string): boolean =>
22
+ /^\s+/.test(line) && !/^\s*(?:[-*+]|\d+[.)])\s*\[/.test(line);
22
23
 
23
- const section = (text: string, name: string): string | undefined => {
24
+ // `until` ends a section; by default any heading does. Findings ends only at a known section so a stray
25
+ // heading inside it (### Nits) makes its lines errors instead of silently cutting the findings off.
26
+ const section = (text: string, name: string, until = /^###?\s/m): string | undefined => {
24
27
  const match = new RegExp(`^###\\s+${name}\\s*$`, "im").exec(text);
25
28
  if (match === null) return undefined;
26
29
  const rest = text.slice(match.index + match[0].length);
27
- const next = /^###?\s/m.exec(rest);
30
+ const next = until.exec(rest);
28
31
  return next === null ? rest : rest.slice(0, next.index);
29
32
  };
33
+ const AFTER_FINDINGS = /^###?\s+(?:Verdict|Sources inspected|Not verified)\b/im;
30
34
 
31
35
  const isSeverity = (value: string): value is Severity =>
32
36
  (SEVERITIES as readonly string[]).includes(value);
@@ -82,7 +86,13 @@ export function parseReviewPacket(text: string): ReviewPacket | ParseError {
82
86
  );
83
87
  }
84
88
  const findings: Finding[] = [];
85
- const lines = (section(text, "Findings") ?? "")
89
+ const findingsText = section(text, "Findings", AFTER_FINDINGS);
90
+ if (findingsText === undefined) {
91
+ return parseError(
92
+ 'missing "### Findings" section: write "- none" under it when there are no findings',
93
+ );
94
+ }
95
+ const lines = findingsText
86
96
  .split("\n")
87
97
  .filter((l) => !isDetail(l))
88
98
  .map((l) => l.trim())
@@ -157,7 +157,7 @@ export function createReviewStartTool(
157
157
  [
158
158
  `${reviewLabel(review)}; round ${round}; diff ${snap.value.digest}. ${basis}`,
159
159
  `lenses: ${lenses.join(", ")}`,
160
- `Spawn the reviewer with agent_spawn using exactly this payload, then pass its packet to devsys_review_record with diffDigest "${snap.value.digest}"${range === "HEAD" ? "" : ` and diffRange "${range}"`}:`,
160
+ `Spawn the reviewer with agent_spawn using exactly this payload, then pass its packet to devsys_review_record with slice "${slice}", diffDigest "${snap.value.digest}"${range === "HEAD" ? "" : ` and diffRange "${range}"`}:`,
161
161
  JSON.stringify(spawn),
162
162
  ].join("\n"),
163
163
  );