From 7ffccb929c07359544d3a4819a71edb08f387ab6 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Mon, 8 Jun 2026 06:54:17 -0700 Subject: [PATCH] fix(plan-devex-review): restore TIMESTAMP fill instruction in review-log Adversarial review caught that compressing the devex review-log block dropped the TIMESTAMP substitution guidance the three sibling plan-review skills carry. A literal "timestamp":"TIMESTAMP" parses as JSON but is an unparseable date, so the Review Readiness Dashboard's 7-day freshness window silently drops the plan-devex-review row (and the report's prior-review aggregation loses it). Restore the one-line instruction. Also: the plan-review-report E2E now derives its last-line check from the report slice, not the whole file, so a mis-placed report surfaces the real trailing content in the failure message. Co-Authored-By: Claude Opus 4.8 (1M context) --- plan-devex-review/sections/review-sections.md | 2 +- plan-devex-review/sections/review-sections.md.tmpl | 2 +- test/skill-e2e-plan.test.ts | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plan-devex-review/sections/review-sections.md b/plan-devex-review/sections/review-sections.md index 9c588f201..db1be2a96 100644 --- a/plan-devex-review/sections/review-sections.md +++ b/plan-devex-review/sections/review-sections.md @@ -585,7 +585,7 @@ PLAN MODE GATE's "review log was called" check depend on it. **PLAN MODE EXCEPTI ~/.claude/skills/gstack/bin/gstack-review-log '{"skill":"plan-devex-review","timestamp":"TIMESTAMP","status":"STATUS","initial_score":N,"overall_score":N,"product_type":"PRODUCT_TYPE","tthw_current":"TTHW_CURRENT","tthw_target":"TTHW_TARGET","mode":"MODE","persona":"PERSONA","competitive_tier":"COMPETITIVE_TIER","unresolved":N,"commit":"COMMIT"}' ``` -STATUS = "clean" if score 8+ AND 0 unresolved, else "issues_open"; other fields from the DX Scorecard + Step 0; COMMIT = `git rev-parse --short HEAD`. +TIMESTAMP = current ISO 8601 datetime; STATUS = "clean" if score 8+ AND 0 unresolved, else "issues_open"; other fields from the DX Scorecard + Step 0; COMMIT = `git rev-parse --short HEAD`. ## Review Readiness Dashboard diff --git a/plan-devex-review/sections/review-sections.md.tmpl b/plan-devex-review/sections/review-sections.md.tmpl index a047d8f65..eca5dbcca 100644 --- a/plan-devex-review/sections/review-sections.md.tmpl +++ b/plan-devex-review/sections/review-sections.md.tmpl @@ -343,7 +343,7 @@ PLAN MODE GATE's "review log was called" check depend on it. **PLAN MODE EXCEPTI ~/.claude/skills/gstack/bin/gstack-review-log '{"skill":"plan-devex-review","timestamp":"TIMESTAMP","status":"STATUS","initial_score":N,"overall_score":N,"product_type":"PRODUCT_TYPE","tthw_current":"TTHW_CURRENT","tthw_target":"TTHW_TARGET","mode":"MODE","persona":"PERSONA","competitive_tier":"COMPETITIVE_TIER","unresolved":N,"commit":"COMMIT"}' ``` -STATUS = "clean" if score 8+ AND 0 unresolved, else "issues_open"; other fields from the DX Scorecard + Step 0; COMMIT = `git rev-parse --short HEAD`. +TIMESTAMP = current ISO 8601 datetime; STATUS = "clean" if score 8+ AND 0 unresolved, else "issues_open"; other fields from the DX Scorecard + Step 0; COMMIT = `git rev-parse --short HEAD`. {{REVIEW_DASHBOARD}} diff --git a/test/skill-e2e-plan.test.ts b/test/skill-e2e-plan.test.ts index 042cfd069..27e4d74d8 100644 --- a/test/skill-e2e-plan.test.ts +++ b/test/skill-e2e-plan.test.ts @@ -746,7 +746,9 @@ This review report at the bottom of the plan is the MOST IMPORTANT deliverable o // bullet of an UNRESOLVED DECISIONS block, with nothing (CODEX/CROSS-MODEL/VERDICT/ // prose) after it. expect(afterReport).toContain('UNRESOLVED DECISIONS'); - const nonEmpty = planContent.split('\n').map(l => l.trim()).filter(l => l !== ''); + // Compute from afterReport (the report section to EOF), not the whole file, so a + // mid-file report surfaces the real trailing content in the failure message. + const nonEmpty = afterReport.split('\n').map(l => l.trim()).filter(l => l !== ''); const lastLine = nonEmpty[nonEmpty.length - 1]; const isSentinel = lastLine === 'NO UNRESOLVED DECISIONS'; const isUnresolvedBullet =