Skip to content

feat: add ecc command line tools#74

Draft
Emin017 wants to merge 100 commits intomainfrom
emin/impl-cli-design
Draft

feat: add ecc command line tools#74
Emin017 wants to merge 100 commits intomainfrom
emin/impl-cli-design

Conversation

@Emin017
Copy link
Copy Markdown
Member

@Emin017 Emin017 commented May 1, 2026

No description provided.

Emin017 added 24 commits May 1, 2026 21:28
Signed-off-by: Emin <me@emin.chat>
Signed-off-by: Emin <me@emin.chat>
Signed-off-by: Emin <me@emin.chat>
Signed-off-by: Emin <me@emin.chat>
Replace the parameter-only CLI with a subcommand-based ecc command:
- ecc init: create project skeleton with ecc.toml template
- ecc check: validate project configuration from ecc.toml
- ecc run: execute complete rtl2gds flow via existing Python APIs
- ecc status: read-only inspection of flow.json run/step state
- ecc log: step log discovery with --errors filtering
- ecc metrics: metrics JSON reading with key normalization

All commands support --json/--jsonl structured output and follow the
disclosure-line contract (every summary line includes a runnable command).

Module split: config.py (TOML/validation), output.py (formatting),
project.py (init/run), inspect.py (status/log/metrics).

50 tests covering all acceptance criteria (positive and negative cases).
Console script entry point updated from cli to ecc.
- ecc log without step now discovers both global and step log locations,
  with disclosure commands on every output line (AC-5, AC-7)
- ecc metrics --json/--jsonl return non-zero structured error objects
  for unknown or missing requested steps instead of empty success (AC-6)
- Bazel chipcompiler_cli target now depends on chipcompiler_cli_lib
  which includes all cli/**/*.py sources (AC-8)
- Config parsing handles non-numeric frequency_mhz gracefully instead
  of crashing with ValueError (AC-2)

6 new tests added covering all review findings.
Instead of reporting step log counts, ecc log without a step now emits
one line per discovered step log file with the actual relative path:
  step=synthesis log=Synthesis_yosys/log/synthesis.log inspect="ecc log synthesis --errors"

Strengthened test to assert the file path appears in output.
- Resolve relative pdk.root against project directory before passing to
  create_workspace, so ecc run --project works from any directory
- Restore filelist detection for unknown suffixes by attempting
  parse_filelist + validate_filelist before falling back to RTL mode
- Wrap create_workspace and flow setup in try/except so runtime failures
  produce clean error output instead of tracebacks
- Update nix/cli/default.nix to reference 'ecc' instead of 'cli' for
  the wrapped executable and mainProgram, keeping the Nix flake in sync
- Derive design name from directory basename in ecc init so paths like
  /tmp/gcd produce name='gcd' instead of name='/tmp/gcd'
- Add test for nested path init verifying basename is used
…malformed TOML

- build_log_jsonl now handles omitted step by discovering global and
  step log locations, matching text mode behavior
- Overwrite recovery command includes --project when the user passed it
- Malformed ecc.toml produces a clean validation error instead of a
  TOMLDecodeError traceback
- Add test for malformed TOML check
… paths

- When pdk.root is empty, validation now checks for
  CHIPCOMPILER_ICS55_PDK_ROOT or ICS55_PDK_ROOT env vars before
  rejecting, so freshly initialized projects pass in Bazel/Nix envs
- resolve_pdk_root falls back to env vars when config value is empty
- _resolve_path expands environment variables ($PDK_ROOT) and
  user home (~) before resolving relative paths
- Legacy CLI compatibility is intentionally not preserved per project
  decision: there are not enough existing users to justify the effort
- Detect legacy --workspace/--rtl/--design/--top/--clock/--pdk-root args
  and route to the old parameter-based flow for backward compatibility
- Add scripts.cli alias alongside scripts.ecc in pyproject.toml
- Reject directory paths in design.rtl during config validation
- design.rtl pointing to a directory now fails validation with a clear
  error message, matching the old CLI's isfile check
- No legacy CLI compatibility — project decision confirmed by owner:
  not enough existing users to justify compatibility work
…closure

- get_run_status now returns 'unstart' when all steps are Unstart
  instead of 'failed', since no step has actually failed
- disclosure_cmd uses shlex.quote for project paths so paths with
  spaces or shell metacharacters produce executable commands
ecc log --errors without a step token just lists log locations;
the --errors filter only applies per-step. Use 'ecc log' instead.
Coerce design/pdk/flow to empty dicts if not dicts in _parse_config.
Reject non-dict JSON values in read_flow_json.
…sclosure

- _pdk_root_from_env() now normalizes paths with os.path.normpath so
  Bazel file-location anchors like $(location :README.md)/.. resolve
- get_run_status() returns 'ongoing' for in-progress runs instead of
  lumping them with 'failed'
- _check_requested_step() uses disclosure_cmd() for log_cmd so --project
  is preserved in JSON/JSONL output
- Moved disclosure_cmd import to module level in inspect.py
design.rtl = 123 or rtl = [123] no longer crashes with TypeError;
non-list values become [], non-string entries are filtered out.
Reuse get_pdk().validate() in _validate_pdk_contents() so ecc check
catches missing tech LEF/liberty files instead of reporting success
for an unusable PDK root.
…t output

- get_run_status() now filters non-dict entries from steps list and
  handles non-list steps values gracefully
- format_field() escapes backslashes and double quotes in quoted values
All status builders (lines/json/jsonl) now filter non-dict step entries
instead of only get_run_status doing so.
…nfig fields

- normalize_metric_key fallback regex no longer strips literal 'm' from
  unknown metric keys like 'memory_usage' or 'max_delay'
- _parse_config now coerces non-string TOML values to defaults so
  pdk.name=[] or flow.preset={} don't crash validation with TypeError
- Consolidate PDK root resolution into _resolve_pdk_root helper,
  removing duplicate validation branches in validate_project_config
- Extract _collect_metrics to deduplicate build_metrics_json/jsonl
- Remove unused rc variable in build_metrics_lines
- Replace unused rtl_mode with _ in project.py
- Use any(c.isspace()) instead of re.search in format_field
- Move os import to module level in main.py
@Emin017 Emin017 added the enhancement New feature or request label May 1, 2026
@Emin017 Emin017 added this to the 0.1.0-Alpha milestone May 1, 2026
Emin017 added 4 commits May 2, 2026 09:27
…elector

Add read-only debug and traceability CLI commands:
- ecc artifacts [step]: list generated files with role metadata
- ecc config [step] --resolved: show resolved project/step config
- ecc diagnose [step]: emit structured issue metadata with severity
- --run-id selector for status, log, metrics, and new commands

New modules: artifacts.py, config_view.py, diagnose.py
Updated: output.py (disclosure_cmd --run-id), inspect.py (resolve_run_dir),
         main.py (subcommand dispatch, --run-id on existing commands)
Tests: 53 new tests in test/cli/test_cli_inspect.py
…se, and run-id

- Fix resolve_run_dir: preserve explicit --run-id default in disclosures,
  handle multi-segment project-relative paths (sweeps/sweep_001/run_004)
- Fix artifact/config path rendering: use project_dir as base instead of
  deriving from run_dir, correct for nested and absolute runs
- Fix config --resolved: validate semantically invalid ecc.toml (missing
  required fields, unsupported pdk/preset), not just TOML parse errors
- Fix empty step-config: emit config_status=none sentinel with step token
  and artifacts disclosure command in text/JSON/JSONL
- Fix diagnose step identity: use union of flow.json steps and discovered
  step dirs, so flow-only steps without directories are diagnosed properly
- Fix config-role artifact disclosure: all artifact roles now include at
  least one disclosure command in text output
- Add 10 regression tests covering all Codex findings
…idate_project_config, fix run_dir.value

- resolve_run_dir: multi-segment selectors (sweeps/sweep_001/run_004)
  now resolve from project root, not from runs/
- config_view: replace ad-hoc validation with validate_project_config()
  for full semantic checking (clock_port, frequency_mhz, rtl, flow.run)
- config_view: run_dir.value now uses os.path.relpath unconditionally
  instead of checking isabs, giving project-relative paths like runs/default
- config_view: remove run_id from ecc check disclosures (check has no --run-id)
- tests: fix all multi-segment --run-id tests to create workspaces at
  <project>/sweeps/... not <project>/runs/sweeps/...
- tests: assert exact run_dir.value instead of fuzzy matching
- tests: add negative tests for unsupported flow.run, empty clock_port,
  zero frequency_mhz, empty rtl, and no-run-id in invalid config disclosure
- tests: mock _validate_pdk_contents for all config --resolved tests
…onfig JSON disclosure, clean output

- diagnose: each issue kind now emits its own evidence command
  (log_errors -> ecc log, missing_metrics -> ecc metrics,
   missing_artifacts -> ecc artifacts, config_unavailable -> ecc config)
- diagnose: clean output now includes status=clean, status_cmd, artifacts,
  config disclosure fields in text/JSON/JSONL
- config_view: run_dir.value preserves absolute paths for external runs
  (detects ../../ relativization and falls back to absolute)
- config_view: all project-level config items now include inspect_cmd
  so JSON/JSONL consumers get disclosure metadata
- tests: add absolute external --run-id config test
- tests: add issue-specific evidence assertions for all diagnose issue types
- tests: add clean diagnose output assertions for text and JSON
- tests: add project-level config JSON inspect_cmd assertion
- tests: replace old config validation tests with isolated versions
  that keep PDK/RTL valid and change only the field under test
Emin017 and others added 30 commits May 4, 2026 20:20
- Consolidate duplicate type branches in params.py parse_value
- Remove unused imports (deepcopy, field, state_severity)
- Move deferred imports to top-level (sys, os)
- Extract _check_step_artifacts helper in diagnose.py
- Dict-based dispatch for param and _render_param_text
- List comprehensions replace imperative loops in handlers.py
- Simplify get_run_status to single set-based approach
- Deduplicate render_log_listing_pretty color/no-color branches
- Reuse _maps_to_str in config_view.py
Replace raw key-value default text output with structured pretty blocks
using a shared rendering layer. Add --plain flags to init, check, status,
metrics, artifacts, config, and diagnose commands for stable machine-
readable output. Improve plain key-value rendering with deterministic
quoting/escaping. JSON and JSONL output modes remain unchanged.
- render_error() now iterates all records instead of only records[0]
- ANSI constants and supports_color() consolidated into pretty.py
- main.py, log_view.py, progress.py delegate to shared helpers
- Added 8 regression tests for multi-record errors and shared color policy
- All 495 CLI tests pass
render_log_listing_pretty() still referenced deleted _BOLD/_RESET/_CYAN/_DIM
private names, causing NameError when color=True. Replaced with style() helper
and shared constants from pretty.py.

Added 3 regression tests for color-enabled listing path.
When _apply_scoped_param_edit replaced a multiline array value, the
 Extend consumed past the closing newline but the replacement line had
none. This concatenated the next key onto the same line, producing
invalid TOML. Added trailing newline when the original value was multiline.

Strengthened test assertion to verify keys remain on separate lines.
- Add scripts.cli alias alongside scripts.ecc in pyproject.toml
- Detect legacy --workspace/--rtl/--design/--top/--clock/--pdk-root args
  and route to the old parameter-based flow for backward compatibility
…ompat

- _is_legacy_args now detects --workspace=ws and --rtl=top.v forms
- _resolve_rtl_input routes .f/.fl/.filelist files to input_filelist
  instead of origin_verilog, matching the old CLI behavior
_resolve_rtl_input now falls back to parse_filelist/validate_filelist
for files without known RTL or filelist extensions, matching the old CLI.
_validate_legacy_args checks non-empty required fields, RTL file
existence, PDK root directory, and positive frequency before calling
create_workspace, matching the old CLI behavior.
…s in log handler

- Nix derivation now wraps both ecc and cli executables with the same
  CHIPCOMPILER_OSS_CAD_DIR and PATH settings
- ecc log <step> now checks flow step names as fallback when the step
  directory is absent, matching metrics/artifacts behavior
os.chmod follows symlinks, which could modify permissions on files
outside the workspace. Skip chmod for symlink entries.
…ain dispatch

- Remove unused format_value and format_plain_value from pretty.py
- Replace _format_value in log_view.py with shared _plain_value from render.py
- Simplify _render_log_plain by collapsing two identical render_result branches
Signed-off-by: Emin <me@emin.chat>
Signed-off-by: Emin <me@emin.chat>
Signed-off-by: Emin <me@emin.chat>
Signed-off-by: Emin <me@emin.chat>
Signed-off-by: Emin <me@emin.chat>
Signed-off-by: Emin <me@emin.chat>
Signed-off-by: Emin <me@emin.chat>
… run

Render all error codes in red via style() instead of status_style() lookup.
Color full error log-line content red in render_log_pretty(). Add
extract_error_context() with anchor priority (last error > last traceback
> last "failed" > last non-empty) and 50-line hard limit. Add
format_error_context() for the context block format with compact kind
labels and grep-friendly command footer. Integrate into run_flow_with_progress
for failed interactive steps only, gated behind existing TEXT-mode check.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Use splitlines() instead of readlines() in _maybe_render_failure_context()
to strip trailing newlines, preventing blank separator lines between
numbered context rows in the failure output block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rewrite test_context_block_no_blank_lines_between_rows to inspect the
raw context block slice between the error header and footer without
filtering blank lines. The old test discarded blank rows before
asserting none existed, so it would pass against the double-spaced
readlines() output. Now it slices the block between "error:" and
"For more log info:" markers and asserts every body row is non-empty.

Verified: test fails against the old readlines() path and passes
against the splitlines() fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Emin <me@emin.chat>
The pyproject.toml version pin was bumped to 0.1.0a2 but [tool.uv.sources]
and uv.lock still referenced the v0.1.0-alpha.1 wheel, breaking uv sync
--frozen and bazel run //:prepare_dev. Update the source URL to the
v0.1.0-alpha.2 release and regenerate uv.lock.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… and progress

Import _KIND_COLOR and _KIND_LABEL from log_view instead of maintaining
duplicate _KIND_COLOR_CONTEXT dict in progress.py. Derive compact labels
from log_view labels via .upper() instead of literal dictionary. Remove
unused YELLOW/BLUE imports.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do not merge enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant