diff --git a/bin/gstack-evidence b/bin/gstack-evidence index 4c08ab73b..fc717e017 100755 --- a/bin/gstack-evidence +++ b/bin/gstack-evidence @@ -74,17 +74,24 @@ interface EvidenceRecord { /** Bytes of streamed output kept for the tests_ran parse (the 2MB log would truncate the summary). */ const TAIL_MAX_BYTES = 64 * 1024; -/** Ring of the last TAIL_MAX_BYTES of combined child output, decoded per stream. */ +/** + * Ring of the last TAIL_MAX_BYTES of each child stream, kept per stream: the + * pumps interleave by arrival, so a stderr summary read while stdout still has + * pipe-buffered bytes behind it would otherwise be pushed out of a shared ring. + */ class OutputTail { private readonly decoders = { stdout: new TextDecoder("utf-8"), stderr: new TextDecoder("utf-8") }; - private text = ""; + private readonly text = { stdout: "", stderr: "" }; push(chunk: Uint8Array, origin: "stdout" | "stderr"): void { - this.text += this.decoders[origin].decode(chunk, { stream: true }); - if (this.text.length > TAIL_MAX_BYTES * 2) this.text = this.text.slice(-TAIL_MAX_BYTES); + this.text[origin] += this.decoders[origin].decode(chunk, { stream: true }); + if (this.text[origin].length > TAIL_MAX_BYTES * 2) this.text[origin] = this.text[origin].slice(-TAIL_MAX_BYTES); } end(): string { - this.text += this.decoders.stdout.decode() + this.decoders.stderr.decode(); - return this.text.length > TAIL_MAX_BYTES ? this.text.slice(-TAIL_MAX_BYTES) : this.text; + const tails = (["stdout", "stderr"] as const).map((origin) => { + const text = this.text[origin] + this.decoders[origin].decode(); + return text.length > TAIL_MAX_BYTES ? text.slice(-TAIL_MAX_BYTES) : text; + }); + return tails.join("\n"); } } diff --git a/scripts/resolvers/dispatch-rules.ts b/scripts/resolvers/dispatch-rules.ts index 93313633c..a6f85f63b 100644 --- a/scripts/resolvers/dispatch-rules.ts +++ b/scripts/resolvers/dispatch-rules.ts @@ -3,8 +3,8 @@ * * docs/designs/HONEST_WORK_GATE_INTEGRITY.md W7. Rendered into /spec's issue * body template (the archived spec is the prompt `/spec --execute` pipes to - * `claude -p`, so the rules travel inside it) and into /ship Step 7's - * test-generation child prompt, before its LAST-line JSON protocol. One + * `claude -p`, so the rules travel inside it) and into the /ship Test Coverage + * Audit (Step 7) test-generation child prompt, before its LAST-line JSON protocol. One * constant so the two cannot drift. The child's JSON contracts are untouched: * gate-edit disclosure uses the prose the protocols already permit before the * JSON line. test/dispatch-rules.test.ts pins the rendering sites. diff --git a/scripts/resolvers/plan-gates.ts b/scripts/resolvers/plan-gates.ts index 294456d59..94dbeac3c 100644 --- a/scripts/resolvers/plan-gates.ts +++ b/scripts/resolvers/plan-gates.ts @@ -241,14 +241,14 @@ function planAcceptanceEditedCheck(): string { return `### Acceptance edits (parent-computed) Run this after the child's report and before Gate Logic, with the bound plan path -substituted for \`\`. It compares the plan's acceptance-bearing sections +substituted for \`\`. It compares the plan's acceptance-bearing sections (headings matching acceptance, success criteria, verification, test plan, testing, done when, definition of done) as they stand in the working tree against a baseline: the \`origin/\` version when the plan exists there, else its first commit on this branch, else \`baseline unavailable\` (plans outside the repository or not yet tracked). \`\`\`bash -PLAN_FILE="" +PLAN_FILE="" _REL=$(git ls-files --full-name --error-unmatch -- "$PLAN_FILE" 2>/dev/null | head -1) _BASE_REF=""; _BASE_KIND="" if [ -z "$_REL" ]; then @@ -262,7 +262,7 @@ else fi if [ -n "$_BASE_REF" ]; then echo "ACCEPTANCE_BASELINE: $_BASE_KIND" - _ACC_TMP=$(mktemp -d) && trap 'rm -rf "$_ACC_TMP"' EXIT + _ACC_TMP=$(mktemp -d "\${TMPDIR:-/tmp}/gstack-acceptance.XXXXXX") && trap 'rm -rf "$_ACC_TMP"' EXIT _acc_sections() { awk 'BEGIN{keep=0} /^#+ /{keep = (tolower($0) ~ /${ACCEPTANCE_HEADING_PATTERN}/)} keep{print}'; } git show "$_BASE_REF:$_REL" | _acc_sections > "$_ACC_TMP/base" _acc_sections < "$PLAN_FILE" > "$_ACC_TMP/wtree" diff --git a/ship/sections/plan-completion.md b/ship/sections/plan-completion.md index af5ae3842..0adfa7866 100644 --- a/ship/sections/plan-completion.md +++ b/ship/sections/plan-completion.md @@ -188,14 +188,14 @@ Counts map one-to-one to the classifications above and sum to total_items. No pl ### Acceptance edits (parent-computed) Run this after the child's report and before Gate Logic, with the bound plan path -substituted for ``. It compares the plan's acceptance-bearing sections +substituted for ``. It compares the plan's acceptance-bearing sections (headings matching acceptance, success criteria, verification, test plan, testing, done when, definition of done) as they stand in the working tree against a baseline: the `origin/` version when the plan exists there, else its first commit on this branch, else `baseline unavailable` (plans outside the repository or not yet tracked). ```bash -PLAN_FILE="" +PLAN_FILE="" _REL=$(git ls-files --full-name --error-unmatch -- "$PLAN_FILE" 2>/dev/null | head -1) _BASE_REF=""; _BASE_KIND="" if [ -z "$_REL" ]; then @@ -209,7 +209,7 @@ else fi if [ -n "$_BASE_REF" ]; then echo "ACCEPTANCE_BASELINE: $_BASE_KIND" - _ACC_TMP=$(mktemp -d) && trap 'rm -rf "$_ACC_TMP"' EXIT + _ACC_TMP=$(mktemp -d "${TMPDIR:-/tmp}/gstack-acceptance.XXXXXX") && trap 'rm -rf "$_ACC_TMP"' EXIT _acc_sections() { awk 'BEGIN{keep=0} /^#+ /{keep = (tolower($0) ~ /^#+ *[0-9.]* *(testable )?(acceptance|success criteria|verification|test plan|testing|done when|definition of done)/)} keep{print}'; } git show "$_BASE_REF:$_REL" | _acc_sections > "$_ACC_TMP/base" _acc_sections < "$PLAN_FILE" > "$_ACC_TMP/wtree" diff --git a/test/fixtures/workflow-required-steps.json b/test/fixtures/workflow-required-steps.json index 32e4967ce..4b005e1ee 100644 --- a/test/fixtures/workflow-required-steps.json +++ b/test/fixtures/workflow-required-steps.json @@ -68,7 +68,7 @@ "## Step 2.5: Check for Greptile review comments", "## Step 3: Get the diff", "## Step 3.4: Workspace-aware queue status (advisory)", - "## Step 3.5: Slop scan (advisory)", + "## Step 3.5: Diff scans", "## Step 3.6: Gather review context", "## Step 4: Critical pass (core review)", "### Step 4.7: Exploratory QA (before Fix-First)", diff --git a/test/helpers/placeholder-allowlist.ts b/test/helpers/placeholder-allowlist.ts index 1a0bc058c..56a78ff55 100644 --- a/test/helpers/placeholder-allowlist.ts +++ b/test/helpers/placeholder-allowlist.ts @@ -128,6 +128,7 @@ export const IDENTIFIER_PLACEHOLDERS: Record = { '': { grammar: QUOTED, what: 'staged directory printed by an earlier block', quoted: true }, '': { grammar: PATH, what: 'output directory' }, '': { grammar: PATH, what: 'repository path from discovery' }, + '': { grammar: PATH, what: 'bound plan file path' }, '': { grammar: PATH, what: 'sketch directory printed by an earlier block' }, '': { grammar: PATH, what: 'finalized HTML path' }, '': { grammar: PATH, what: 'finalized HTML path' },