dbc8b4077aa2bbd0fcbc8a6e64b09936797f272e
10 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
da7d4dac52 |
fix(#3345): stop counting blocked summaries as completed plans (#3459)
* fix(#3345): stop counting blocked summaries as completed plans * chore(#3345): set changeset pr reference --------- Co-authored-by: sim <sim@local> |
||
|
|
69e7afd0c7 |
chore(#3212): bounded quantifiers over document content — prohibition with teeth — Phase 4 (#3441)
* feat(#3415): ship local/no-unbounded-quantifier, burn down ReDoS class Phase 4 of epic #3212 (ADR-3212 §5/§7, the final phase). New rule flags an unbounded */+/{n,} quantifier over a broad character class ([\s\S], dotAll ., or a 1-2-unit negated class like [^\n]/[^)\n] — the exact #2128-fixed shape) applied to a regex whose match target is data-flow-traced to readFileSync content. eslint-rules/lib/readfilesync-trace.cjs extracts the data-flow tracer shared with no-crlf-fragile-split (Phase 2) rather than a second copy — no-crlf-fragile-split refactored onto it with zero behavior change, parity-tested. Real triage, not 798 mechanical edits: the ADR's census (2026-08-08) screened every unbounded quantifier in the tree unscoped. Correctly scoped to readFileSync-derived content (matching Phase 2's own G2/G3 scoping), the rule found 162 real hits across two detection waves — the second wave (93) surfaced only after a genuine off-by-one bug in this rule's own first draft was caught while writing its RuleTester tests and fixed (the bug silently missed every directly-quantified [\s\S]* with no gap before the quantifier — exactly the class this rule exists to catch). 3 hits landed in production src/ (commands.cts, milestone.cts, roadmap.cts) and were each empirically timed against adversarial input (matching #2128's own measured-not-assumed precedent) — all confirmed linear-time/benign, left unbounded with a measured-evidence comment rather than mechanically bounded. The remaining 159 are test-file fixture parsing (test-author-controlled, fixed-size content, not adversarial input) — each suppressed with a specific, non-generic reason. Zero functional behavior changed anywhere in this diff. tests/no-pending-3212-markers.test.cjs locks the epic's own closing invariant (ADR §7: "assert zero pending #3212 markers remain") — ground truth confirmed trivially true today (no phase left any such marker behind), now regression-locked going forward. Design: .gsd/phase/chore-3415-prohibition-with-teeth/40-design.md Test matrix: .gsd/phase/chore-3415-prohibition-with-teeth/50-test-matrix.md Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(#3415): correct rule category mislabel, add CI test-scope entry An orthogonal Standards-axis review found eslint-rules/no-unbounded-quantifier.cjs mistakenly carried meta.docs.category: 'Portability', copied from a sibling rule without realizing what that implied: docs/contributing/cross-platform- portability-rules.md governs an ADR-1703 rule family under a hard "zero escape hatches" contract (tests/portability-rule-disable-ban.test.cjs's PROTECTED_RULES bans eslint-disable for those rules entirely). This rule is not part of that family — it's ADR-3212 (ReDoS/CWE-1333), a different epic — and its eslint-disable-next-line suppressions (159 of them, added earlier this same phase after empirical benign-verification) are an intentional, correct design, not a bypass. Corrected to category: 'Best Practices', matching the actual precedent (no-adhoc-regex-escape.cjs, Phase 1 of the same epic, which is also correctly outside PROTECTED_RULES), and the rule's own docstring now states this explicitly so a future reader doesn't have to re-derive it. Also registers a new scripts/ci-test-scope.cjs bucket so editing this rule or the shared eslint-rules/lib/readfilesync-trace.cjs helper re-runs their own test suites under targeted CI selection — was previously unregistered and invisible to that fast-path (this PR's own gsd-test checkpoint runs the full suite regardless, so this only affects future narrowly-scoped PRs). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(#3415): bound no-unbounded-quantifier's own scanner (CWE-1333, ironic) Security review found the rule meant to catch algorithmic-complexity bugs had one of its own: hasUnboundedBroadQuantifier's negated-class inner scan walked from each `[^` occurrence to the next `]` (or EOF) with no bound, while the outer loop only ever advanced by one character — O(n²) total work on a pattern with many unclosed `[^` runs. Runs unconditionally inside checkPattern on any `new RegExp('literal string')` argument in any linted file, before the (cheap) readFileSync data-flow gate — so a single crafted string literal, no valid regex syntax required, could make `npm run lint` / CI hang. Empirically confirmed both the bug and the fix: pre-fix, n=4000/8000/ 16000/32000 chars took 30.8/115.6/463.8/1874.3ms (~4x work per 2x n, quadratic); extrapolated, the 300000-char repro from the finding would run ~165s. Post-fix (bail the inner scan once units exceeds the rule's own 1-2-unit scope, rather than continuing to hunt for a closing `]`), the same 300000-char input runs in 8.7ms via the real rule module, independently reconfirmed at 18ms via a fresh Linter.verify() call. New regression row in tests/no-unbounded-quantifier.rule.test.cjs asserts the RuleTester run on a 50000-char adversarial pattern completes and returns a defined result — no wall-clock assertion (CLAUDE.md Clock Seams / local/no-elapsed-assertion). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(#3415): triage 3 new sites, re-raise ceiling after upstream batch next merged 12 more PRs during this PR's review. Two consequences: - tests/edit-phase.test.cjs (fix #3262, unrelated) added 3 new content.match(/<tag>([\s\S]*?)<\/tag>/) reads of this repo's own workflow .md content — the same Class A pattern as the ~159 sites already triaged elsewhere in this PR. Suppressed with the same established reason. - lint-allow-test-rule-refs' ratchet ceiling needed re-raising again (301 -> 303) for the same reason as the two prior bumps: organic growth from unrelated, already-reviewed PRs landing concurrently, not a defect in this branch's own diff. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: sim <sim@local> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
011153c076 |
fix(#3300): guard build_prompt optional sections against nullglob (#3454)
* fix(#3300): guard build_prompt optional sections against nullglob * chore(#3300): backfill changeset pr number 3454 --------- Co-authored-by: sim <sim@local> |
||
|
|
1d5d77951c |
fix(#3190): commit review.md in --auto loop; fix report env var (#3434)
* fix(#3190): commit review.md in --auto loop; fix report env var Three coupled defects in gsd-core/workflows/code-review-fix.md: - The --auto re-review loop overwrote REVIEW.md each iteration but the single docs commit staged only REVIEW-FIX.md, so the committed REVIEW.md stayed at iteration 1 and contradicted the committed REVIEW-FIX.md. The --auto commit now stages the converged REVIEW.md alongside REVIEW-FIX.md (guarded on AUTO_MODE; non-auto single-pass runs unchanged). - The two inline frontmatter validators (HAS_STATUS, FIX_FRONTMATTER) exported REVIEW_PATH into a node -e body that reads process.env. FIX_REPORT_PATH, so the status check was always empty and REVIEW-FIX.md was never committed. Both now export FIX_REPORT_PATH. - On successful convergence the spent .iterN.md backups are removed so the phase directory is clean; they are retained on degradation for post-mortem. Regression test: tests/code-review-fix-pipeline-regression.test.cjs. * chore(#3190): set changeset pr to 3434 --------- Co-authored-by: sim <sim@local> |
||
|
|
b77b7f8e56 |
fix(#1526): delegate auto-chain post-completion to transition workflow (#3419)
* fix(#1526): delegate auto-chain post-completion to transition workflow execute-phase's auto-chain completion called phase.complete then a light inline set (partial PROJECT.md update + offer-next) and never invoked the transition workflow, silently skipping graduation scan, session-continuity, project-reference, accumulated-context, and current-position updates — so a phase completed via auto-chain left different project state than a normal transition. Fix (delegate, user decision 2026-08-13): replace execute-phase's update_project_md + offer_next with a delegation step that @-includes transition.md in post-completion mode. Add a post_completion_mode step to transition.md that skips verify_completion + update_roadmap_and_state (phase.complete already ran; avoids double-write) and begins at evolve_project. Standalone transition (mode 1) is unchanged. Regression: tests/auto-chain-transition-delegation.test.cjs (source-text-is-the- product) asserts the delegation, the skip-set, the removed inline step, and the mode. Ack fragment 1526 covers execute-phase.md + transition.md growth (spent 2930 fragment removed — same-path owner conflict, like #3025/#3024). * docs(#1526): backfill changeset PR number (#3419) --------- Co-authored-by: sim <sim@local> |
||
|
|
622c10b2c6 |
fix(#3025): refuse cross-runtime skill sync in sync-skills (#3404)
* fix(#3025): refuse cross-runtime skill sync in sync-skills Skill content and directory layout are runtime-specific — the installer applies per-runtime converters, adapter headers, brand swaps, and layout rules at install time, and grok/gemini resolve to ANOTHER runtime's skills root. A verbatim cross-runtime cp -r therefore produces content the installer would never have written for the destination, and can damage a runtime the user never named. #3024 (closed) un-masked this, making the corruption live. Fix (option b, user decision): add a functional Step 1 guard that refuses any --to != --from with an actionable installer pointer, before any resolution or copy. Identity sync (--from == --to) remains a no-op. The non-functional Step 5 comment is replaced; Arguments/Limitations updated. Regression: tests/sync-skills-cross-runtime-refuse.test.cjs (source-text- is-the-product) asserts the guard exits non-zero for cross-runtime, points at the installer, precedes the cp -r copy, and preserves identity. * docs(#3025): backfill changeset PR number (#3404) --------- Co-authored-by: sim <sim@local> |
||
|
|
4a0b5e26b4 |
test(#3338): fold the verify/validate & workflow-text issue-* cluster — Wave 6 (#3383)
* test(#3338): fold the verify/validate & workflow-text issue-* cluster — Wave 6 Folds 10 legacy issue-*.test.cjs regression files (75 test() blocks) into their module's main suite, per H3 (#3315) of the test-hygiene epic (#3053). Third of 4 issue-* waves. - issue-2701-nul-corrupted-validators.test.cjs (9) + issue-429-comment- text-gate.test.cjs (31, incl. fast-check property tests): both target verify.cjs/validate.cjs via different calling styles (CLI vs direct- require) — merged jointly into verify.test.cjs, 0 dropped. - issue-2762-plan-reviews-chunked.test.cjs (3) merged into plan-phase-drift-guard.test.cjs. - issue-2771-advisor-subagent-type.test.cjs (1) + issue-2772-discuss- phase-text-inconsistencies.test.cjs (6) merged jointly into discuss-phase-power.test.cjs. - issue-498-update-backup-runtime-dir.test.cjs (3, rename basis) + issue-815-update-next-channel.test.cjs (7, merged in): both concern update.md workflow-text contracts, now update-workflow.test.cjs. - issue-498-update-context.test.cjs (13): pure rename to update-context.test.cjs, sole comprehensive suite for its module. - issue-2765-brace-expansion-lockfile.test.cjs (1, rename basis) + issue-3238-js-yaml-lockfile.test.cjs (1, merged in): two distinct CVE regression pins against package-lock.json, now lockfile-cve-audit.test.cjs, shared ROOT/npmLs helpers deduped instead of double-declared. Ratchet upkeep to keep this wave's own gates green: pruned 3 stale allow-test-rule allowlist entries, cited 2 previously-uncited comments that surfaced in the folded content (#3338), tightened the exemption-file ceiling 305 -> 297. Fixed one stale filename reference in production code (src/init.cts) plus one in docs/reference/workflow-fragments.md. Zero net test-coverage loss. No production code BEHAVIOR changed. * test(#3338): fix orthogonal-review findings — Wave 6 fold Standards-axis review found a real structural defect in two files, both the same root cause and both fixed here: - tests/update-workflow.test.cjs: the folded:issue-815-update-next-channel wrapper's closing brace was placed after the file's pre-existing tail instead of before it, making the already-established folded:bug-2470 and folded:bug-3130 wrappers CHILDREN of issue-815's block in the test hierarchy instead of independent siblings — confirmed via an actual node --test run showing the mislabeled TAP nesting. Moved the closing brace to the correct position; all three fold wrappers are now top-level siblings again (verified via node --test, TAP hierarchy correct, 10/10 tests, 5 suites, identical count before and after). - tests/plan-phase-drift-guard.test.cjs: same mistake in the other direction — folded:issue-2762-plan-reviews-chunked was spliced inside the pre-existing folded:bug-2492-context-coverage-gate wrapper instead of after it. Fixed the same way (227/227 tests, 38 suites, identical count before and after). - Reverted unnecessary 815-suffixed local renames (assert815/fs815/etc.) introduced by the fold — the wrapper is genuinely block-scoped once correctly closed, so no collision existed (same class as Wave 4's __foldSetNested finding). No test() count changed in either file. No production code touched. --------- Co-authored-by: sim <sim@local> |
||
|
|
ad07f76a31 |
test(#3336): fold the installer & runtime surface issue-* cluster — Wave 4 (#3376)
* test(#3336): fold the installer & runtime surface issue-* cluster — Wave 4 Folds 10 legacy issue-*.test.cjs regression files (79 test() blocks) into their module's main suite, per H3 (#3315) of the test-hygiene epic (#3053). First of 4 issue-* waves (following the 3 fix-* waves, all merged). - 1 file with no prior target coverage: renamed (git mv) into legacy-cleanup.test.cjs (sole comprehensive suite for that module). - 9 files merged into 6 pre-existing suites: golden-parity-single-source, runtime-artifact-layout-surface, codex-config (4 sources merged jointly in one pass per the issue's own instruction, to catch overlap between the 4 sources themselves, not just against the pre-existing target — zero overlap found, all 20 blocks additive), runtime-config-adapter-registry (1 of 10 source blocks dropped as a proven subset of existing coverage), cline-install, install.test.cjs. Incidental fixes required to keep this wave's own ratchets green: - Fixed a stale ADR doc reference (docs/adr/1235) to a folded-away filename. - scripts/lint-allow-test-rule-refs: pruned 4 stale allowlist entries for renamed/merged-away files, cited 2 previously-uncited allow-test-rule comments that surfaced as "new" only because their file path changed, added 1 fresh allowlist entry for a pre-existing uncited comment that predates this PR, and tightened the exemption-file ceiling 309 -> 305 to match the real post-fold high-water mark. Zero net test-coverage loss. No production code changed. * test(#3336): fix orthogonal-review findings — Wave 4 fold Standards-axis review + Memtrace graph pass found real issues in the just-folded suites, all fixed here: - Standardized the fold-wrapper convention (block-scoped __foldDescribe) across golden-parity-single-source.test.cjs, runtime-artifact-layout- surface.test.cjs, runtime-config-adapter-registry.test.cjs, and cline-install.test.cjs to match the pattern already used by codex-config.test.cjs and install.test.cjs in this same wave (and by earlier folds elsewhere in the epic) — repeats the exact inconsistency Wave 3 (#3335) already fixed once in this epic. - Fixed a stale allowlist entry's alphabetical position (cosmetic, not tool-gated, caught by review anyway). - Fixed two stale test-filename references in PRODUCTION code comments (src/capability-writer.cts, src/runtime-config-adapter-registry.cts) caught by lint-removed-but-needed — a class of stale reference this wave's fold agents didn't check for, since they were scoped to docs/ and gsd-core/references/ only, not src/. First fix attempt wrongly edited the gitignored gsd-core/bin/lib/*.cjs BUILD OUTPUT instead of the tracked .cts source; caught and corrected before commit. - Fixed one remaining stale doc reference in docs/adr/1235 (a prior partial fix in this same wave missed it). No test() count changed in any file. No production code BEHAVIOR changed — comment-only fixes in src/. --------- Co-authored-by: sim <sim@local> |
||
|
|
9341d8b8d3 |
test(#3334): fold the workflow-dispatch & review-lane fix-* cluster — Wave 2 (#3342)
Folds 15 tests/fix-*.test.cjs regression files (191 test() blocks) into their module's main suite, per the wave decomposition of #3315 (H3 of epic #3053). 187 blocks land in 8 existing suites (4 exact-duplicate cases dropped, documented inline); 4 blocks move via git mv into 2 new suite files with no prior coverage to merge into. Zero production behavior change. Also tightens two H1 (#3313) ratchets that the fold's own file-count reduction moved past their grace window, per the ratchets' documented dual failure mode (a stale/too-loose baseline fails exactly like a novel violation): - lint-test-file-count.allowlist.json: removes the stale "audit" entry (folding fix-2766 into tests/uat.test.cjs drops that module back to its 2-file cap). - lint-allow-test-rule-refs.ceiling.json: lowers maxFiles 314 -> 309, the real post-fold high-water mark (gsd-test's own repo-baseline test caught this — CI, not a human, found it). Two orthogonal review passes (Standards+Spec code-review, isolated security-review) found and this commit fixes two issues before push: a genuinely-distinct #2287 test case (file-absent vs. file-present- resolved) that a prior fold pass had wrongly dropped as a duplicate — restored verbatim into tests/uat.test.cjs; and a missing same-line allow-test-rule citation on the #2196 block in tests/debug-session-management.test.cjs, added for consistency with its sibling #2257 block. lint-removed-but-needed also caught two stale doc references to the now-folded-away fix-2285-claude-orchestration-wiring.test.cjs filename (docs/adr/1143-claude-orchestration-capability.md, gsd-core/references/execute-phase-response-language.md) — updated both to point at tests/claude-orchestration.test.cjs, its new home. Co-authored-by: sim <sim@local> |
||
|
|
5339dd60e5 |
feat(#3313): allow-test-rule total-file-count ratchet, F17 promotions (#3326)
Extends lint-allow-test-rule-refs.cjs with a second, independent check alongside the existing uncited-citation identity ratchet: the total number of distinct test files carrying any allow-test-rule marker (cited or not) is now checked against a tight ceiling via the previously-unwired assertTightCeiling primitive (allowlist-ratchet.cjs, 0 prior callers). A cited exemption is legitimate under ADR-456 but nothing stopped the raw total from growing forever - this closes that gap without duplicating the file walk (both checks consume one shared walkTestFiles pass). Ceiling introduced at the exact measured high-water mark (314 files, grace 3) rather than a padded estimate, per "budgets may only decrease." Also lands the two F17 pieces (absorbed from the now-closed #1885) that had no precondition: - --max-warnings 0 added to lint/lint:ci - local/no-source-grep promoted warn->error in the scripts/bin/ eslint-rules glob block (already error in the tests/ glob) Both promotions were pre-verified against a zero-warning tree (fresh non-cached eslint run) before flipping, per the maintainer's clean- tree-first decision. Not included: local/no-elapsed-assertion promotion, which stays warn pending #3314 (H2) - 10 of 19 clock-touching src modules have no sanctioned time-control mechanism until ADR-456 is amended there. H1 of epic #3053, absorbing #1885 F17. Co-authored-by: sim <sim@local> |