Skip to content

feat(data): split skippable steps into SkippableStepEnum - #286

Merged
Yell-walkalone merged 19 commits into
mainfrom
emin/support-skippable-steps
Sep 15, 2026
Merged

Yell-walkalone merged 19 commits into
mainfrom
emin/support-skippable-steps

Conversation

@Emin017

@Emin017 Emin017 commented Sep 15, 2026

Copy link
Copy Markdown
Member

What Changed

  • Add a configuration-driven skip mechanism for optional RTL-to-GDS flow steps (synthesis LEC, postRouteLec, Timing optimization), preceded by a preparatory file reorganization:

    • File reorganization (pure refactor): new chipcompiler/data/types.py holds the step/state enums and pure predicates; step_dirs.py is merged into data/step.py (with its naming-convention docstring); data/__init__.py re-exports keep from chipcompiler.data import StepEnum working.
    • Enum split: memberless StepBaseEnum (shared behavior) with sibling StepEnum (core chain) and SkippableStepEnum (LEC, POST_ROUTE_LEC, TIMING_OPT); persisted string values are unchanged. New step_from_value() resolves step values across both enums; all 133 former references migrated.
    • Skip policy resolver: one authoritative resolve_skip_steps() — presence-keyed (absent key → code default ("lec",); explicit [] → run everything, the only way to enable the synthesis LEC), alias normalization to canonical order, strict validation against the skippable set.
    • Configuration surfaces: project.json workspaces[].skip_steps (wins for this key only, including explicit []) > ecc.toml [flow] skip_steps > code default. Raw declared spelling is preserved verbatim on manifest round-trip; only the persisted workspace policy is normalized. ecc init materializes the commented default (skip_steps = ["lec"]) into generated ecc.toml; legacy-run migration carries the persisted policy into the manifest entry.
    • Build-time exclusion: skipped steps never enter workspace.flow.json — all creation paths (CLI preset, CLI range, sidecar/GUI flow_config, preset-shaped configs) build identical ledgers through one resolver, fully validated before any filesystem mutation. Input chaining falls through to the previous retained step; the state machine, run loop, and resume/rerun are unchanged.
    • Canonical chain restored: the synthesis LEC returns to the canonical 17-step chain after SYNTHESIS (reverting the revert: disable lec due to yosys lec unstable #280 comment-out), kept out of ledgers by the default policy so post-revert: disable lec due to yosys lec unstable #280 workspaces are byte-identical.
    • Reconcile: policy-driven targets; the legacy_missing_synthesis_lec hack is deleted. Ledgers holding steps the policy skips stay runnable (those entries are inert — never removed or re-inserted); changing the policy can never insert/remove steps in an existing ledger.
    • synthesis_lec conflict: selecting the preset while the effective policy skips lec is a creation-time configuration error naming skip_steps = [] as the fix; existing-ledger resume/rerun is unaffected.
    • Consumers: preflight probes the policy-filtered step list (no sizer probe when TimingOpt is skipped); signoff collector/checklist require postRouteLec only when the ledger contains it; SIGNOFF_REQUIRED_QOR_STEPS pinned by test to contain no skippable step.
    • Docs: development guides (en/cn), CLI spec, config reference, user guides, tutorials, and index updated — precedence chain, [] as the LEC enable, preset conflict, ledger immutability, and the skippable set.

Scope

Select the areas touched by this PR:

  • CLI - command behavior, Typer command surface, output formats, or workspace commands.
  • Flow/runtime - workspace lifecycle, EngineFlow, step execution, logs, metrics, or artifacts.
  • EDA integration - Yosys, ECC-Tools, DreamPlace, KLayout, PDKs, or native/runtime wrappers.
  • Build/package - Nix, PyInstaller, wheels, uv.lock, or release artifacts.
  • CI/release - GitHub Actions, version checks, changelog, or release automation.
  • Tests/docs only

Runtime And Packaging Impact

  • No runtime or packaging impact
  • CLI output or machine-readable contract changed
  • Workspace layout, flow state, or artifact paths changed
  • Native toolchain or wrapper behavior changed
  • ecc-tools or ecc-dreamplace dependency changed
  • PyInstaller, Nix, or release artifact changed

Notes:

  • project.json gains an optional workspaces[].skip_steps list; unknown fields/keys stay forward-compatible (old GUI reads preserve it, new ECC tolerates unknown flow_config keys). Existing projects without the key keep the code default ("lec",) and are never re-filtered.
  • Workspace home/params.toml [flow] sections accept a normalized skip_steps (absent / [] / list round-trip distinctly); ecc config shows the effective policy and its winning layer.
  • Ledgers created under a skip policy omit those steps entirely (no ledger entry, no step directory); existing ledgers are immutable under policy changes.
  • Default behavior for pre-existing projects is identical to post-revert: disable lec due to yosys lec unstable #280 (LEC not run).

Validation

List the commands you ran. Mark checks that are not applicable as N/A.

  • uv run pytest test/
  • uv run ruff check chipcompiler test
  • uv run ruff format --check chipcompiler test
  • PyInstaller smoke: ecc --help, ecc --version, ecc version --json — N/A (no packaging change)
  • Nix smoke: nix run .#cli -- --help — N/A (no packaging change)
  • Manual flow smoke: full-suite runs before/after each milestone; ledger-level chaining tests cover skip on/off for LEC, TimingOpt, and postRouteLec (test/data/test_workspace_skip_policy.py, test/formal/test_file_chaining.py)
  • Other: two codex review loops (gpt-5.5:high) — plan-compliance review converged at round 6 with all 17 acceptance criteria passing; whole-branch quality review converged at round 5 with zero findings

Skipped checks and reason:

  • PyInstaller/Nix smoke: no build, packaging, or dependency changes in this PR.
  • Note: the local environment has 8 pre-existing test failures identical on the base commit (missing Yosys/FlexLexer.h, DreamPlace native rebuild, ANSI help rendering); all other 2260 tests pass.

Checklist

  • I kept the change scoped to ECC.
  • I updated docs or user-facing CLI text where behavior changed.
  • I included lockfile or version metadata updates when dependencies changed. — N/A: no dependency changes.
  • I documented any submodule updates and why they are needed. — N/A: no submodule updates.
  • I did not include local caches, virtual environments, or generated build outputs.
  • I explained skipped validation and remaining risk.

@Emin017 Emin017 added the enhancement New feature or request label Sep 15, 2026
@Emin017 Emin017 added this to the 0.1.0-alpha.13 milestone Sep 15, 2026
…rs into step

Pure file reorganization with zero behavior change:

- new data/types.py holds StepEnum, StateEnum, FINISHED_STEP_STATES,
  is_finished_step_state (enum vocabulary + pure predicates, no IO)
- data/step.py keeps StepMetrics/load_metrics/save_metrics and absorbs
  STEP_DIRECTORIES from data/step_dirs.py (docstring included);
  data/step_dirs.py is deleted
- data/__init__.py re-exports from .types; direct importers repointed
Three-layer step enum: memberless StepBaseEnum carries shared behavior;
StepEnum keeps the core chain members; SkippableStepEnum owns the
optional LEC, postRouteLec and Timing optimization steps (persisted
values unchanged). step_from_value resolves a persisted step value
across both enums; reverse lookups in tools/ecc service and subflow use
it. All former StepEnum.LEC/POST_ROUTE_LEC/TIMING_OPT references are
migrated to SkippableStepEnum
One authoritative resolver owns skip-policy semantics: resolve_skip_steps
reads the skip_steps key from a flow config with presence semantics
(absent key -> code default (lec,); explicit [] -> run everything),
normalizes aliases, validates membership against the skippable step set
in canonical chain order, and rejects invalid values. build_rtl2gds_flow
gains a skip filter and restores the synthesis LEC into the canonical
chain (reverting the #280 comment-out); build_flow_range filters before
slicing so a skipped step cannot bound a range. Ledger creation paths
(dynamic flow data, preset seeding, ledger-less rebuild) resolve the
policy through the same resolver, keeping default behavior identical
project.json workspaces[].skip_steps and ecc.toml [flow] skip_steps are
both parsed and validated (invalid values fail the manifest load /
config validation before any mutation). The declared spelling is
preserved verbatim on round-trip; only the persisted workspace policy is
normalized. Precedence for this one key is skip-specific: project.json
outranks ecc.toml (including an explicit empty list); every other field
keeps the ecc.toml-wins contract. Workspace [flow] sections accept a
normalized skip_steps (three states distinct: absent / [] / list) and
policy-only sections are valid. ecc init materializes the default
skip_steps = ["lec"] with an explanatory comment into generated
ecc.toml; legacy-run migration carries a workspace's persisted policy
into its manifest entry
data.create_workspace resolves the skip policy before touching the
filesystem, so an invalid policy is a clean configuration error instead
of a partial workspace tree. The runtime API validates it before
materializing sidecar artifacts (temp filelist, inline PDK). Creation
path equivalence is pinned by a test: preset, ranged, and sidecar paths
build identical ledgers for one policy, and a policy-only flow config
never yields a ledger
Reconcile targets now apply the [flow] section's skip policy, so
post-revert ledgers reconcile naturally under the default policy and the
legacy_missing_synthesis_lec hack is deleted. A ledger holding steps the
effective policy skips stays runnable: those entries are inert for
comparison and are never removed or re-inserted (append skips entries
the ledger already holds). Selecting the synthesis_lec preset while the
effective policy skips lec is a creation-time configuration error naming
skip_steps = [] as the fix; resume/rerun on existing ledgers is
unaffected
… on filtered steps

The signoff collector and the ECC checklist require postRouteLec only
when the workspace ledger actually contains it: a workspace created with
postRouteLec skipped never false-fails, while genuine truncated-signoff
failures are preserved. SIGNOFF_REQUIRED_QOR_STEPS is pinned by a test
to contain no skippable step. Run preflight resolves its probe set from
the policy-filtered step list, so skipping TimingOpt drops the sizer
probe and the default policy never probes the synthesis LEC
Workspace-creation behavior tests pin the input-chaining contract under
each skip policy: the default keeps the synthesis LEC out of the ledger
so preFloorplan directly consumes synthesis outputs; an explicit empty
skip list restores the LEC entry; skipping Timing optimization chains
routing after legalization and skipping postRouteLec chains DRC after
LVS. Ledgers drive step-directory creation and step inputs, so a skipped
step owns neither
Document the skip mechanism on both development guides: the skippable
set (lec, postRouteLec, Timing optimization), the skip-specific
precedence (project.json over ecc.toml over the code default), explicit
empty list as the only LEC enable, the synthesis_lec preset conflict,
and ledger immutability under policy changes. Stale claims that the
rtl2gds preset runs an active synthesis LEC by default are corrected to
the default-skipped behavior. Forward compatibility is pinned: unknown
flow_config keys never become ledger steps
- a skipped step can no longer bound a [flow] range: validate_flow_config
  rejects it, and create_workspace validates the resolved selection
  before any filesystem mutation
- a policy-only flow config (skip_steps without a range) persists its
  normalized policy into _flow so runtime ledger-less rebuilds honor an
  explicit skip_steps = [] instead of silently falling back to the
  default
- the preset-path _flow persistence normalizes skip_steps through
  validate_flow_config instead of copying raw aliases
- run_existing carries the declared skip policy on the preset target so
  existing workspaces classify against the same policy fresh creation
  uses
- ecc config shows the effective flow.skip_steps with its winning layer
  (or the code default)
- EngineFlow.add_step accepts StepBaseEnum like init_flow_step
- docs: corrected remaining stale claims that the synthesis LEC runs by
  default (cli-design, config ref, user guide, tutorial, development)
- extract post-route LEC gating into tools/ecc/lec_gates.py so
  signoff_checklist.py stays under the module size guideline
- move the synthesis_lec preset / skip-policy run tests into
  test/cli/commands/test_run_skip_policy.py
- render_workspace_config persists the validated (normalized) [flow]
  section, so a declared policy lands canonical in params.toml no matter
  which path saves it
- run_existing preset target carries the declared skip policy so
  existing workspaces classify against the creation-time policy
- correct remaining Chinese user-guide/tutorial claims that the
  synthesis LEC runs by default
- fresh manifest registration materializes the workspace's declared
  skip policy onto the project.json entry (declared spelling preserved)
- the existing-workspace run path classifies against the effective
  declared policy (flow_config's resolved skip_steps, falling back to
  ecc.toml), so a per-workspace project.json policy wins over ecc.toml
  as with fresh creation
- preset-shaped flow configs now build the preset's ledger and persist
  {preset, skip_steps} through the data/runtime creation path instead of
  being silently dropped
- docs: the synthesis_lec examples in both user guides note the required
  skip_steps = [], and both tutorials show clearing the list before the
  captured run
…icy on existing runs

- create_workspace fully resolves the ledger (including a preset target
  whose endpoint the effective policy skips) before any filesystem
  mutation, so the conflict surfaces as a clean error with a pristine
  target instead of a partially created workspace
- manifest-mode existing runs now apply the effective declared skip
  policy (project.json workspaces[].skip_steps over ecc.toml) over the
  workspace's own [flow] range, so classification, extension, and
  no-op/resume decisions match fresh creation while the seeded range
  contract is preserved
- validate_flow_config rejects a preset whose endpoint the declared skip
  policy excludes, at config-validation time; manifest entries reject a
  skipped range boundary at load time
- reconcile's skip-tolerant comparison only ignores ledger entries whose
  (name, tool) pair matches the canonical chain, so a corrupted entry is
  never treated as an inert skipped step
- EngineFlow.build_default_steps delegates to the canonical rtl2gds
  builder instead of maintaining a second hand-written chain
- flow-config-to-ledger construction (build_dynamic_flow_data and
  helpers) extracted into data/workspace/flow_data.py, keeping
  workspace/__init__.py's size in check; skip-policy workspace and
  migration tests moved into focused modules
- migration blocks (instead of silently defaulting) when a workspace's
  persisted skip_steps is invalid, naming the offending config
- ecc config reports the winning skip_steps layer recorded by
  effective-config resolution
- shared preset-test helpers moved to test/cli/commands/conftest.py
- user guides show the generated skip_steps line and the exact edit
  before the synthesis_lec example
- manifest loading now rejects a workspace range boundary the declared
  skip policy excludes (display name mapped to the canonical step), with
  regression coverage for boundary and inside-range cases
- migration reads a workspace's params.toml strictly: only a missing
  config reads as 'no policy'; malformed, undecodable, or unreadable
  configs block that workspace's migration with a config-specific
  reason, covered by new tests
- the flow_config selects-steps check lives once in effective_config and
  is imported by run_prepare instead of being duplicated
- ProjectConfig declares _skip_steps_source as a typed field
…states

- migration validates ledger contiguity against the canonical chain as
  filtered by the workspace's resolved skip policy, so ledgers
  legitimately omitting postRouteLec or Timing optimization migrate
  instead of being blocked as gapped
- reconcile evaluates target-prefix states by matching target step names
  in the ledger rather than positional slicing, so a skipped entry before
  the boundary can no longer hide an unfinished in-range step; both
  behaviors carry regression tests
…ontiguity

_is_contiguous_flow now distinguishes an undeclared policy (None: the
full chain and the legacy lec-less chain both migrate) from an explicit
declaration (authoritative: only the full chain and its policy-filtered
form are accepted), so skip_steps = [] with a LEC-less ledger is blocked
instead of producing a manifest entry the next run cannot reconcile
@Emin017
Emin017 force-pushed the emin/support-skippable-steps branch from 0d2ce74 to 1c483bf Compare September 15, 2026 10:03
…solidation

Adaptations for the latest main (9081182) that have no counterpart in the
branch history: repoint STEP_DIRECTORIES imports in analysis/qor/loader.py
and engine/analysis.py to data.step (data.step_dirs was merged into
step.py), and reference SkippableStepEnum.TIMING_OPT in the sizer and
step-storage-name tests.
@Emin017
Emin017 force-pushed the emin/support-skippable-steps branch from 1c483bf to efd39cd Compare September 15, 2026 10:12
@Yell-walkalone
Yell-walkalone merged commit 102353b into main Sep 15, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants