You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
🔴 Artifact upload fails on early-exit paths — Lines 59, 179: if: always() means upload-artifact runs even when checkout/test steps fail. If benchmark_reports/ was never created, actions/upload-artifact errors with "Path not found", masking the real failure.
Suggestion: Guard with if: always() && hashFiles('benchmark_reports/**') != ''.
🔴 git fetch by SHA is server-dependent — Line 92: git fetch origin "$BASE_SHA" relies on uploadpack.allowReachableSHA1InWant. Works on GitHub, but will fail on many self-hosted git servers (GitLab CE, etc.). Also, if BASE_SHA is the PR base head, it may not be reachable from any ref in the shallow clone.
Suggestion: Fetch the base ref instead (git fetch origin "$BASE_REF" --depth=1), or at minimum add --no-tags and an explicit error message:
git fetch origin "$BASE_SHA" --depth=1 2>&1|| {
echo"::error::Cannot fetch baseline $BASE_SHA; is it reachable from a ref?";exit 1;
}
🟡 GITHUB_REF mismatch on PR-triggered runs — Lines 34, 84: For PR events, github.ref is refs/pull/N/merge (a synthetic ref). If the workflow uses a mirror/checkout strategy, this ref may not exist on the configured origin. The old code fetched main (always exists); the new code may silently miss commits if the ref doesn't resolve.
Suggestion: Use git fetch origin "$GITHUB_SHA" directly (like the BASE_SHA pattern) to avoid ref-resolution ambiguity, or verify the ref exists with a git ls-remote check first.
🟡 Trap registered too late — Line 97: trap ... EXIT is set aftergit worktree add. If worktree add itself fails mid-execution, the partially-created worktree is never cleaned up.
Suggestion: Register the trap immediately after mktemp, before the worktree is created:
🟡 Missing set -euo pipefail — All shell steps: Without pipefail, a failed command in a pipe (e.g., git fetch ... | tee) still exits 0.
Suggestion: Add set -euo pipefail as the first line of every multi-line run: block.
💭 Redundant pytest install — Line 45: pytest>=7,<10 is pinned again after pip install -e ".[all]". If the project's pyproject.toml already declares pytest as a dependency (which the .[all] extra likely includes), this line is dead weight and creates a version-conflict risk if the bounds diverge.
Suggestion: Move the pytest pin into the project's dependency spec instead.
📁 benchmarks/bench_dsl_diagnostics.py
🔴 Bug: Division by zero risk — compare_parsing (line ~181):
ratio=current["median_s"] /baseline["median_s"]
If a pathological checkout parses in 0 µs (e.g. empty corpus edge case), this raises ZeroDivisionError. Guard with a minimum floor or early-exit when baseline["median_s"] <= 0.
🟡 Unformatted floats in markdown table — markdown_report (line ~240):
median_s renders as a raw float (e.g. 0.000123456789) while ratio uses :.3f. Apply :.6f for the seconds column so the table stays readable.
🟡 Incomplete thread-limiting env — run_worker (line ~168):
Only OMP_NUM_THREADS and OPENBLAS_NUM_THREADS are pinned. MKL_NUM_THREADS, VECLIB_MAXIMUM_THREADS, NUMEXPR_NUM_THREADS are not, so NumPy/ONNX threads can still vary across machines, inflating the performance ratio and causing false "target not met" results.
🟡 importlib.metadata.version crashes on missing dep — build_report (line ~220):
If onnx or numpy is absent, PackageNotFoundError is raised. It's caught by the outer try/except, but the report only says "PackageNotFoundError: No package metadata was found for onnx" — no hint that this is an environment issue. Wrap each call in try/except and record "unavailable" instead.
💭 Path.is_relative_to requires Python 3.9+ — line ~72:
The module uses from __future__ import annotations and str | None hints (fine on 3.7+), but is_relative_to is a runtime call that requires 3.9+. Document the minimum version in the docstring or argparse.ArgumentParser description.
💭 revision renders as literal "None" in markdown — line ~240:
When a checkout is an unpacked archive (no git), revision returns None. The table shows | Revision | None | None |. Replace with "(unknown)" for clarity.
Contradictory render_error signature — Task 1 Step 3 defines render_error(err, *, stream: TextIO) -> str, but a function that writes to a stream should return None (or vice versa). This will cause confusion during implementation.
Missing error code in verification — Task 5 Step 4 lists "E100, E101, E110, E111, E200 and E201" but omits E112 (duplicate else), which is tested in Task 3 Step 1. Either add E112 to the requirements or remove the test.
🟡 Suggestions
Ambiguous error limit semantics — Task 1 Step 4: "stop at exactly max_errors" is unclear. Does the collector stop after collecting max_errors, or before reaching max_errors? This is an off-by-one risk. Recommend clarifying: "collect at most max_errors errors; set limit_reached=True if more would have been emitted."
CompileResult field addition risk — Task 4 Step 3 appends defaulted fields to CompileResult. If any code constructs CompileResult positionally (not by keyword), this will break silently. Consider making the new fields keyword-only or verifying no positional usage exists first.
No DoS protection for malicious input — The plan doesn't address pathological inputs: extremely long lines, deeply nested blocks (potential RecursionError), or files with thousands of errors. Consider adding max-depth or max-line-length guards in SourceBuffer/validator.
Complex block recovery may hide errors — Task 3 Step 3: "recover by popping through an outer matching block or treating the closer as recovery for the top frame" — this dual recovery strategy can produce confusing messages where a stray endif silently "fixes" an unrelated unclosed block. Recommend preferring conservative recovery (emit E110, skip the closer, continue validation) over speculative recovery.
Undefined "normal mode" — Task 4 Step 4: "exit 2 without traceback in normal mode" — what triggers "non-normal" mode? A --debug flag? An environment variable? This needs a concrete definition or the implementation will be inconsistent.
💭 Nits
Plan includes execution instructions — Task 5 Step 6 (git add, commit, push) and Step 5 (memory.md merge) are operational steps, not implementation requirements. Consider moving them to a separate "release checklist" to keep the plan focused on what to build.
Good: typo test case — retrun x (Task 2 Step 1) is a smart test for catch-all/typo detection; it ensures the validator doesn't silently accept misspelled keywords.
Good: backward compatibility emphasis — "Keep format_error() available", "retains their previous Program.dump() structure", and "Do not modify ONNX parsing" show careful attention to not breaking existing users.
Minor: error code numbering — The scheme (E100/E101 syntax, E110/E111/E112 blocks, E200/E201 operators) is logical but the plan never defines the full namespace. Consider adding a one-line legend early (e.g., "E1xx = syntax, E2xx = semantic, E3xx = internal").
🟡 Missing purpose/context header — Lines 1-2: File jumps straight into entries. Add a brief header explaining what this file is, who maintains it, and when to add vs. remove entries (e.g., "Engineering decisions and constraints for this repo. Append-only log; entries older than N months may be archived.").
🟡 Dense unstructured prose — Lines 3-5: Each entry is a single run-on paragraph mixing decision, rationale, scope, and caveats. Consider structured fields per entry:
This makes it scannable and prevents information from being lost in a wall of text.
🟡 Insufficient cross-reference — Line 5: References "课题 9" without a link to the issue/PR that defines it. Any team member encountering this later has no path to verify the decision. Consider: (see #1234) or 课题 9 (issue #XXX).
🟡 Future dates (2026-09-13) — If this is intentional (project timeline), fine. If it's a typo, fix it. At minimum, the date format should be ISO 8601 with timezone (2026-09-13T00:00:00Z) to avoid ambiguity for future automation.
🟡 No expiry or verification mechanism — Entries state requirements as present-tense facts ("维护者要求", "benchmark 应用"). Over time these may go stale. Consider adding a status: field (active | superseded | archived) or a review date per entry so stale decisions get pruned.
💭 Inconsistent scope wording — Line 3 says "适用于本仓库课题 PR 的 CI 接入" (applicable scope) while Line 5 has a longer applicability clause. Standardize a consistent Applies to: or Scope: label at the end of each entry.
💭 Consider front-matter metadata — If tooling will ever parse this file (e.g., for dashboards or staleness checks), YAML front-matter with version, owner, last-audited would help. If purely human-facing, the current format is acceptable but the structural issues above still apply.
Summary: The content is valuable and the decisions are well-reasoned. The main risk is readability and maintainability — these entries will become harder to navigate as more are added. Structuring each entry with explicit fields (decision / rationale / scope / caveat) would significantly improve long-term utility.
📁 scratchv/compiler.py
🟡 Double file read — The use_dsl block reads input_path into source for validation, then _parse opens the same file again because it doesn't receive the already-read content. Pass source explicitly to _parse or refactor to avoid the redundant I/O.
🟡 Dropped DSLParser fallback is a silent breaking change — Previously _parse fell back to DSLParser if ExtendedDSLParser failed. Now any DSL file that only the old parser handles will hard-fail. If ExtendedDSLParser is a true superset, add a comment saying so; otherwise this is a regression.
🟡 Inconsistent exception handling — Inside except Exception, only DSLSyntaxError is caught when use_dsl is true. Other DSL-related exceptions (e.g. DSLValidationError, MemoryError from large input) are re-raised instead of being wrapped in a CompileResult. Consider catching a broader DSL exception base class for consistent behavior.
🟡 source or "" conflates None and "" — An empty file ("") and a missing source (None when input_path is also None) both produce "" passed to validate(). An empty DSL file should probably produce a clear error, not silently validate as valid.
💭 diagnostic_limit default vs actual — The dataclass field defaults to 20, but when validation fails the actual limit comes from collector.max_errors, which may differ. Consumers reading result.diagnostic_limit will see 20 on success (meaningless) and the real value on failure. Consider leaving it as 0 (unset) when not applicable, or making it a computed property.
💭 Import inside except block — from scratchv.frontend.dsl_errors import DSLSyntaxError inside the exception handler means an import error there produces a confusing traceback. A top-level import (or module-level __all__ check) would be clearer.
📁 scratchv/frontend/__init__.py
🟡 OP_SIGNATURES exported as public API — 这是一个内部数据常量,暴露在顶层 __all__ 中意味着下游使用者可能依赖它的内部结构。如果它只是给 DSLValidator 用的,考虑不导出或加注释说明其稳定性契约。
其余无问题。
📁 scratchv/frontend/dsl_errors.py
🟡 Marker misaligned for line numbers ≥ 10 — format_error: marker_padding = 6 + display_start hardcodes the prefix length. The prefix f" {err.line} | " is 5 + len(str(err.line)) chars, so for err.line=10 the caret shifts left by 1, for err.line=100 by 2, etc.
🟡 .errors re-sorts on every access — ErrorCollector.errors calls sorted() each time. If a caller accesses .errors in a loop or multiple times, this is redundant O(n log n) work.
Suggestion: Sort once in report() or cache the sorted result, invalidated on add()/clear().
💭 render_error signature is misleading — It accepts stream: TextIO but never writes to it, only returns a string. The stream is used solely for isatty() color detection.
Consider: either write to stream and return None, or rename to detect_color(stream) and keep format_error as the renderer.
💭 NO_COLOR check is strict-presence — "NO_COLOR" not in os.environ disables color even when NO_COLOR="". Some tools treat empty as "allow color". Worth a comment noting the chosen convention.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Scope
Documentation only. No compiler source code or tests are changed.
Validation