diff --git a/.agents/README.md b/.agents/README.md new file mode 100644 index 0000000000..c1ee9c5b22 --- /dev/null +++ b/.agents/README.md @@ -0,0 +1,15 @@ +# Portable agent skills + +Project skills live here in the open +[Agent Skills](https://agentskills.io/) format. Each subdirectory contains a +`SKILL.md` with YAML frontmatter (`name`, `description`) and instructions. + +``` +.agents/skills//SKILL.md +``` + +Do not add parallel copies under tool-specific paths (`.cursor/skills/`, +`.claude/skills/`, etc.); keep a single tree under `.agents/skills/`. + +Always-on project guidance for all agents: [`AGENTS.md`](../AGENTS.md) at the +repository root. Changelog encoding: skill `edg-changes-example`. diff --git a/.agents/skills/acknowledg-bench/SKILL.md b/.agents/skills/acknowledg-bench/SKILL.md new file mode 100644 index 0000000000..4c02c5d2ed --- /dev/null +++ b/.agents/skills/acknowledg-bench/SKILL.md @@ -0,0 +1,88 @@ +--- +name: acknowledg-bench +description: >- + Reads Cachegrind .cgout Ir totals, compares benchmark baselines to runs, + and supports AcknowlEDG bench review / update-baseline flows. Use when + editing edg-bench-run-delta, edgacknowledg.client.bench, .cgout summary + parsing, or benchmark UI ops fields. +--- + +# AcknowlEDG / Cachegrind benchmarks + +## Metric + +**Ir** (instruction reads) is the first `summary:` integer in a `.cgout`. +CLIs and the UI call it **OPs**. Lower run Ir vs baseline = faster. + +```text +events: Ir I1mr ILmr Dr ... +... +summary: 3696685 6148 4719 ... +``` + +With cache-sim enabled there are multiple counts; **always use the first** +(Ir). Do not sum fields or take a later column. + +Shared regex (keep both call sites in sync): + +```python +_REGEX_CGOUT_SUMMARY = re.compile(r'^summary: ([0-9]+)(?: [0-9]+)*\n$') +``` + +## Readers + +| Location | Function | +| --- | --- | +| [`dev_tools/bin/edg-bench-run-delta`](../../../dev_tools/bin/edg-bench-run-delta) | `read_program_totals` | +| [`dev_tools/pylibs/edgacknowledg/client/bench/__init__.py`](../../../dev_tools/pylibs/edgacknowledg/client/bench/__init__.py) | same | + +Readers scan the last ~10 lines for `summary:`. Missing/malformed → skip that +benchmark. Do not invent a third independent parser. + +Percent change: + +```text +((baseline - run) / max(1, baseline)) * 100 +``` + +## Layout + +Bench home: `$EDG_BENCH_HOME` or `/benchmarks`. + +| Path | Role | +| --- | --- | +| `benchmarks/benchmarks/**/*.bnch.cpp` | Sources | +| `benchmarks/runs//…/*.bnch.cgout` | Runs | +| `benchmarks/baselines/project//` | Project baselines | +| `benchmarks/baselines/user//` | User baselines (`@name` tag) | +| `benchmarks/.acknowledg-cache/` | `cg_annotate` disk cache | + +Relative `.cgout` paths must exist under both run and baseline dirs to compare. + +## Review / UI principles + +Wire payloads should carry raw `baseline_ops` / `run_ops` (and baseline tag +when needed). Derive in the client: + +- op delta, percent change, faster/slower badges +- mean / median / population stdev over the list +- sort order + +Do **not** ship redundant counts, samples arrays, or precomputed change +comments when the UI already has the ops list. + +Bench annotate detail uses `cg_annotate --no-annotate --diff` (delta output, +not a line-oriented source diff). + +## Update baseline + +Bench editor copies the selected run `.cgout` into the session’s baseline tag +directory and refreshes statuses (`BenchEditHandler` / +`edg-bench-review`). + +## Protocol + +Tags `bench.src.*` / `bench.edit.*`: see +[PROTOCOL.md](../../../dev_tools/services/acknowledg/PROTOCOL.md). +Client architecture: skill **acknowledg-client**. +Python under `dev_tools/`: skill **edg-python-style**. diff --git a/.agents/skills/acknowledg-client/SKILL.md b/.agents/skills/acknowledg-client/SKILL.md new file mode 100644 index 0000000000..f666d6df4c --- /dev/null +++ b/.agents/skills/acknowledg-client/SKILL.md @@ -0,0 +1,114 @@ +--- +name: acknowledg-client +description: >- + Works on the AcknowlEDG Python client: BatchClient vs DirectClient, + ReviewClient Protocol, resilient websocket reconnect, and test/bench + provider handlers. Use when editing edgacknowledg.client, edgy-review, + edg-bench-review, edg-acknowledg-cli, or section subscribe/reconnect logic. +--- + +# AcknowlEDG client + +Canonical package: [`dev_tools/pylibs/edgacknowledg/`](../../../dev_tools/pylibs/edgacknowledg/). +`dev_tools/services/acknowledg/server/edgacknowledg` is a **symlink** — edit +pylibs only. + +Wire protocol (tags, routes, auth, occupancy): +[PROTOCOL.md](../../../dev_tools/services/acknowledg/PROTOCOL.md). Keep +`PROTOCOL_REVISION` in sync with +`dev_tools/services/acknowledg/client/src/sockets.js`. + +## Public API + +From `edgacknowledg.client` (`__all__`): + +- `BatchClient` / `DirectClient` — async context managers; constructor takes + endpoint settings and optional `status_listener` +- `BatchClientSession` / `DirectClientSession` — yielded by client `__aenter__`; + `create_review` / `remove_review`, `close()`, `stopped()`, `start()`, `stop()` + (client `__aexit__` awaits `stopped()` then `stop()`) +- `ConnectionStatusListener` — `connected` / `attempting_reconnect` / + `disconnected` +- `ReviewClient` (structural **Protocol**): `review_id`, `name`, `timestamp`, + `sections()`, `add_section` / `remove_section` +- URL/env helpers: `add_endpoint_arguments`, `form_section_http_url`, + `default_*` host/port helpers +- Shared: `BackgroundWorkerPool`, errors + +Usage: + +```python +async with BatchClient( + api_host=..., api_port=..., status_listener=listener +) as session: + review = await session.create_review(...) + # Block ends when session.stop() is called, or on cancellation. +``` + +Private (leading `_`): `_ClientReview`, `_BatchClientReview`, +`_DirectClientReview`, channel helpers. Callers should type against +`ReviewClient`, not private classes. + +## Batch vs Direct + +| | Batch | Direct | +| --- | --- | --- | +| Socket | One resilient `/batch` | Per-section `/source|edit///` | +| Mux | `batch.subscribe` / `unsubscribe` / `event` | Dedicated WS per section | +| CLI | `edg-acknowledg-cli` | `edgy-review`, `edg-bench-review` | +| Reconnect | Drop local subs, `_restore_subscriptions()` | Each section's `ResilientWebsocket` loop | + +Direct URL mapping uses handler `subscription_type` (e.g. `test.src` → +`/source/.../test/`, `bench.edit` → `/edit/.../bench/`). + +## Reconnect + +[`client/websocket.py`](../../../dev_tools/pylibs/edgacknowledg/client/websocket.py): +`ResilientWebsocket`, exponential backoff + jitter on connect failure; +jitter-only delay after a drop. Pattern: + +```python +async for websocket in ResilientWebsocket(...): + try: + ... + except ConnectionClosed: + continue +``` + +Auth header: `X-AcknowlEDG-API-Key` (never put the key in query strings). +`RoleOccupied`: discard that handler / stop reconnecting that section; keep +other sections running. + +## Batch subscription maps + +`_BatchClientReview` tracks: + +- `_subscriptions: Dict[int, uuid.UUID]` — `id(handler)` → server subscription id +- `_subscription_tasks: Dict[uuid.UUID, asyncio.Task]` — subscription id → task + +Unsubscribe needs the server id; the handler-key map provides it after +reconnect bookkeeping. + +## Providers + +| Package | Handlers | +| --- | --- | +| `client/test/__init__.py` | `TestSourceHandler` (`test.src`), `TestEditHandler` (`test.edit`) | +| `client/bench/__init__.py` | `BenchSourceHandler` (`bench.src`), `BenchEditHandler` (`bench.edit`) | + +Shared: `ReviewClientHandler`, `Channel`, `announce_section`, +`trunc_display_content` in `client/shared.py`. + +## Env / URLs + +- `EDG_ACKNOWLEDG_API_{HOST,PORT}`, `EDG_ACKNOWLEDG_UI_{HOST,PORT}`, + `EDG_ACKNOWLEDG_API_KEY` +- Defaults: API `localhost:2727`, UI `localhost:5173`; non-localhost hosts + default port 80 +- Print UI section URLs as soon as the section exists (`form_section_http_url`) +- Timestamps: `utc_now_iso` / `is_iso_timestamp` — no `Z` / `+00:00` munging + +## Python style here + +Follow skill **edg-python-style** (2-space indent, ` = ` in kwargs, +`f"..."` / `'...'`, match neighbors). diff --git a/.agents/skills/edg-changes-example/SKILL.md b/.agents/skills/edg-changes-example/SKILL.md new file mode 100644 index 0000000000..3ebe70f96f --- /dev/null +++ b/.agents/skills/edg-changes-example/SKILL.md @@ -0,0 +1,108 @@ +--- +name: edg-changes-example +description: >- + Encodes and decodes non-UTF-8 example bytes in Changes changelogs with + edg-changes-example («HHHH...» markers) and writes Changes files as UTF-8. + Use when editing any Changes file (src/Changes, util/Changes, …), adding + examples with Latin-1/EUC-JP/Shift-JIS/other non-UTF-8 bytes, or verifying + changelog encoding. +--- + +# `edg-changes-example` and Changes files + +## Policy (all `Changes` files) + +Any path named `Changes` under the monorepo (for example `src/Changes`, +`util/Changes`, `lib_src/Changes`, `include_c++/Changes`, `nt_util/Changes`) +must remain **valid UTF-8** on disk. + +- Write and save Changes entries as **UTF-8**. +- Ordinary Unicode that is valid UTF-8 may appear as real characters (or as + C/C++ escapes such as `\u00b6` in examples). Do **not** wrap valid UTF-8 + in `«…»` unless you intentionally need a raw-byte example + (`encode --escape-all-non-ascii`). +- Examples that need **non-UTF-8** bytes (legacy Latin-1, EUC-JP, Shift-JIS, + etc.) must use `«HHHH…»` markers produced by `edg-changes-example`, never + raw non-UTF-8 bytes in the committed file. + +CLI: `dev_tools/bin/edg-changes-example` (on `PATH` when direnv is active). + +## Note line (required for encoded examples) + +When an entry’s example contains one or more `«…»` hex markers, add this +note on its own line after the example, matching existing changelog style: + +```text +Note: The above example must be decoded using the edg-changes-example tool. +``` + +Use that exact wording. Place it immediately after the example block (blank +line before the next dated entry is fine). + +## Encode (raw → UTF-8 Changes text) + +Given a fragment or file that still contains raw non-UTF-8 bytes: + +```bash +edg-changes-example encode raw-fragment.txt > utf8-fragment.txt +# or: +edg-changes-example encode -o utf8-fragment.txt raw-fragment.txt +printf '...' | edg-changes-example encode +``` + +Default encode keeps maximal runs of bytes `>= 0x80` that form valid UTF-8, +and replaces only non-UTF-8 runs with one marker `«` + hex + `»` (two hex +digits per byte, no separators). `\uXXXX` in the text is left alone. + +Optional: `--escape-all-non-ascii` escapes every byte `>= 0x80`, including +valid UTF-8 sequences (rare; only when the example must show raw bytes). + +## Decode (UTF-8 Changes text → raw bytes) + +To rebuild the real example bytes (for local reproduction, not for commit): + +```bash +edg-changes-example decode utf8-fragment.txt > raw-fragment.txt +edg-changes-example decode -o raw-fragment.txt utf8-fragment.txt +# Whole file: +edg-changes-example decode src/Changes > /tmp/Changes.raw +``` + +Decode replaces each `«HHHH…»` (or legacy `«HH»«HH»`) with the corresponding +bytes. No other escapes are interpreted. + +## Workflow for a new changelog entry + +1. Draft the entry as UTF-8 prose. +2. If an example needs non-UTF-8 bytes, author the example in a small raw + file (or produce the markers by hand only when the bytes are already + known), then run `edg-changes-example encode` and paste the UTF-8 result + into the entry. +3. Immediately after that example, add: + + `Note: The above example must be decoded using the edg-changes-example tool.` + +4. Insert the entry into the appropriate `Changes` file with a **UTF-8** + write (keep the rest of the file valid UTF-8). +5. Verify the file still decodes as UTF-8 (raises on failure): + + ```bash + python3 -c "open('path/to/Changes', encoding='utf-8').read()" + ``` + + Use the real path (for example `src/Changes`). A clean exit means the + whole file is valid UTF-8; a `UnicodeDecodeError` means the write + introduced non-UTF-8 bytes and must be fixed before continuing. + +## Do not + +- Commit raw Latin-1 / Shift-JIS / etc. bytes inside a `Changes` file. +- Invent alternate note wording; match the existing sentence above. +- Run `encode` on the entire historical `src/Changes` and rewrite it unless + that is an explicit, reviewed task — encode **new example fragments**, then + splice the UTF-8 text into the entry. + +## Related + +Always-on summary: [AGENTS.md](../../../AGENTS.md). Front-end context: +skill **edg-cpfe-frontend**. diff --git a/.agents/skills/edg-cpfe-bisect/SKILL.md b/.agents/skills/edg-cpfe-bisect/SKILL.md new file mode 100644 index 0000000000..daa9e16c2b --- /dev/null +++ b/.agents/skills/edg-cpfe-bisect/SKILL.md @@ -0,0 +1,65 @@ +--- +name: edg-cpfe-bisect +description: >- + Finds the commit that introduced a test regression or improved behavior + between known good and bad front-end revisions using + edg-docker-test-bisect (and edg-bisect for manual steps). Use when + troubleshooting a failing or newly-passing test across two commits, + searching for the first bad commit, or locating a fix commit. +--- + +# EDG cpfe test bisection + +Prefer Docker. Put `dev_tools/bin` on `PATH` (direnv / +[HACKING.md](../../../HACKING.md)). Project root = directory containing +`src/cfe.c` and `dev_tools/`. + +Full tutorial: [dev_annex/tutorials/TEST_BISECT.md](../../../dev_annex/tutorials/TEST_BISECT.md). +Build/test/record recipes: skill **edg-cpfe-build-test**. + +## When to use + +You need a **test that differs** between two revisions, plus: + +| Goal | Newer tip | Older tip | +| --- | --- | --- | +| Find the **regressing** commit | bad (fails) | good (passes) | +| Find the **fixing** commit | good (passes) | bad (fails) | + +The CLI detects which situation you mean after verifying that the test +behaves differently on the two commits. + +## Automatic bisect + +Canonical CLI: **`edg-docker-test-bisect`** (not `edg-docker-bisect`). + +```bash +edg-docker-test-bisect [--posture POSTURE] +``` + +- **Overwrites** front-end sources during the run; restores them to **HEAD** + afterward (HEAD itself is not rewritten as history). +- Tip: `--posture=quick-c-fe` for speed (C-generating `edg_x86_64` only). + +### Test path forms + +- `sandbox/foo.sft.cpp` (short form; preferred) +- `tests/sandbox/foo.sft.cpp` +- `tests/tests/sandbox/foo.sft.cpp` +- Or a cwd-relative / absolute path into a valid suite + +If the reproducer is throwaway, put it under `sandbox`. + +## Manual bisect + +Wrapper around git-bisect style steps: `edg-bisect` +(`start|good|bad|skip|reset|…`). Use when automatic mode is too heavy or you +need to mark skips yourself. + +## Checklist + +1. Confirm the test **fails on bad** and **passes on good** (or the inverse + for a fix hunt) with `edg-docker-test --non-interactive …`. +2. Run `edg-docker-test-bisect` with those commits and the test path. +3. After it finishes, sources should match **HEAD**; verify with `git status`. +4. Inspect the identified commit; do not commit or push unless asked. diff --git a/.agents/skills/edg-cpfe-build-test/SKILL.md b/.agents/skills/edg-cpfe-build-test/SKILL.md new file mode 100644 index 0000000000..4c5e95af74 --- /dev/null +++ b/.agents/skills/edg-cpfe-build-test/SKILL.md @@ -0,0 +1,88 @@ +--- +name: edg-cpfe-build-test +description: >- + Builds and tests the EDG C++ front end in Docker via edg-exec and + edg-docker-test, and records expectations with -W. Use when building + cpfe, running edgy filters, recording output, or choosing postures. + For good/bad commit bisection, use skill edg-cpfe-bisect. +--- + +# EDG cpfe build and test + +Prefer Docker. Put `dev_tools/bin` on `PATH` (direnv / [HACKING.md](../../../HACKING.md)). +Project root = directory containing `src/cfe.c` and `dev_tools/`. + +## Build + +Day posture (debug GCC): + +```bash +edg-exec -- cmake --preset linux-gcc-debug +edg-exec --wdir build/gcc -- ninja +edg-exec --wdir build/gcc -- ninja cpfe # C++ → lowered C (.int.c) +edg-exec --wdir build/gcc -- ninja cpfe-cp # C++ → C++ from IL +``` + +- Default dirs: `build/gcc`, `bases/docker/dev-env/gcc` (`EDG_BASE`). +- Clang: preset `linux-clang-debug` → `build/clang`. +- Shell in container (cwd → `/edg/workspace`): `edg-exec` or `edg-docker-shell`. +- If `src/defines.h` is swapped for reproduction, one binary may not build; + that can be expected. Prefer CMake presets from `CMakePresets.json` when + possible ([BUILD.md](../../../BUILD.md)). + +## Test + +```bash +edg-docker-test --non-interactive # day posture, no AcknowlEDG +edg-docker-test --non-interactive rma # ~2k tests, quick smoke +edg-docker-test --non-interactive --posture=quick-c-fe sandbox/foo +edg-docker-test --dry-run rma +``` + +- **`--non-interactive`**: do not launch AcknowlEDG (required for unattended + agent runs). +- Filters: one edgy-style argument (comma-separated OK). +- Postures: `day`, `night`, `quick-c-fe`, `extensive`, or `--posture=@file.json`. +- Results: `tests/runs///` (e.g. `changes.elog`, + `executed.elist`). Empty / no unexpected diffs means a clean match. +- Optional override: `EDG_TEST_HOME` (must contain `.edgy/config.json`). + +First-test walkthrough: [dev_annex/tutorials/FIRST_TEST.md](../../../dev_annex/tutorials/FIRST_TEST.md). + +## Recording expected output + +1. Add or fix the test under `tests/tests//`. +2. Record: + + ```bash + edg-docker-test --non-interactive -W path/within/suite + ``` + +3. Recordings live beside the test in a `..rto/` directory. Prefer + committing **`default.*`** expectation files, not config-specific names + that are gitignored. See edgy helpers in `dev_tools/pylibs/edgtest`. +4. Re-run without `-W` to confirm a clean match before asking to commit. + +## Finding a regressing or fixing commit + +Use skill **edg-cpfe-bisect** (`edg-docker-test-bisect` / +[TEST_BISECT.md](../../../dev_annex/tutorials/TEST_BISECT.md)). + +## Broad / risky changes + +For fixes that may change diagnostics or IL widely (e.g. `overload.c`): + +1. Rebuild in Docker and compare with + `edg-docker-test --non-interactive …`. Do **not** invent a local `-W` + baseline just for the change. Expectations on **`origin/main`** are kept + stable by CI and the Docker workflow, so a clean tree at that tip should + match recorded output; diffs that appear in this run’s elog files are + (with high confidence) caused by the change under test. +2. Inspect `tests/runs///changes.elog` (and + `improvements.elog` / `regressions.elist`). Expect a few intentional + diffs; no true regressions. +3. Only use `-W` when deliberately updating expectations for those + intentional diffs — never as a substitute for the `origin/main` + baseline. + +Full-suite runs are slow (~tens of minutes); prefer targeted filters when safe. diff --git a/.agents/skills/edg-cpfe-frontend/SKILL.md b/.agents/skills/edg-cpfe-frontend/SKILL.md new file mode 100644 index 0000000000..efd7da0233 --- /dev/null +++ b/.agents/skills/edg-cpfe-frontend/SKILL.md @@ -0,0 +1,60 @@ +--- +name: edg-cpfe-frontend +description: >- + Navigates and edits the EDG C++ front end under src/: IL, lexer, symbols, + lookup, templates, overload resolution, and Changes changelogs. Use when + changing front-end C sources or debugging semantics. For coding style, + use skill edg-cpfe-style (canonical doc/source/code_style.rst). For Changes + encoding, defer to edg-changes-example. +--- + +# EDG cpfe front-end sources + +All paths are relative to the monorepo root. + +## Module map + +| Area | Primary files | +| --- | --- | +| Entry / init | `src/cfe.c`, `fe_init.c`, `fe_common.h`, `fe_wrapup.c` | +| IL | `il.h`, `il.c`, `il_*`, `lower_*` | +| Decls / expr / stmts | `decls.c`, `expr.c`, `exprutil.c`, `statements.c` | +| Types | `types.c`, related headers | +| Lexer / preprocessor | `lexical.c`, `preproc.c`, `macro.c` | +| Symbols | `symbol_tbl.c` / `a_symbol` | +| Lookup | `lookup.c` (`normal_id_lookup`, `class_qualified_id_lookup`, `IDL_*`) | +| Templates | `templates.c` (scan vs rescan; `copy_type_with_substitution`, …) | +| Overload | `overload.c` (`a_conv_descr`, candidates, conversions) | + +Lexer token sequence numbers feed caching, correspondence, and diagnostics. +Templates often separate a first **scan** from **rescans** when earlier +parameters must be known. Overload work is historically regression-sensitive; +prefer the **edg-cpfe-build-test** skill (targeted tests, then broader runs / +bisect) when changing it. + +## Style + +Do not invent style rules here. Follow skill **edg-cpfe-style** and +[doc/source/code_style.rst](../../../doc/source/code_style.rst). + +## Interesting options + +Language: `--c++11` … `--c++26`. Emulation: `--gnu=XXYYZZ`, +`--clang_v=XXYYZZ`, `--microsoft_v=XXYYZZ`. Debug: `-d-xyz` / +`db_flag_is_set("xyz")`, `-dN` (levels 1–5). Requires a **DEBUG** build. + +## Editing `Changes` files + +Write every `Changes` file as **UTF-8** (`src/Changes`, `util/Changes`, and +any other `Changes` in the tree). For non-UTF-8 example bytes, `«HHHH…»` +markers, the required note line, encode/decode commands, and verification, +follow skill **edg-changes-example** — do not invent a parallel procedure here. + +## Related skills + +- **edg-cpfe-style** — coding style; [code_style.rst](../../../doc/source/code_style.rst). +- **edg-changes-example** — UTF-8 Changes writes; `edg-changes-example` CLI. +- **edg-cpfe-build-test** — rebuild, `edg-docker-test`, recording. +- **edg-cpfe-bisect** — `edg-docker-test-bisect` between good/bad commits. +- Do not invent a separate defect-tracker workflow; use the project's current + GitHub Discussions / issues process ([CONTRIBUTING.md](../../../CONTRIBUTING.md)). diff --git a/.agents/skills/edg-cpfe-style/SKILL.md b/.agents/skills/edg-cpfe-style/SKILL.md new file mode 100644 index 0000000000..81013a34f5 --- /dev/null +++ b/.agents/skills/edg-cpfe-style/SKILL.md @@ -0,0 +1,70 @@ +--- +name: edg-cpfe-style +description: >- + Applies EDG C++ front-end coding style under src/ (whitespace, wrapping, + naming, functions, classes, statements, preprocessor). Use when writing or + reviewing front-end C/C++ sources, formatting new helpers, or checking + comment and layout conventions. Canonical rules live in the RST style guide. +--- + +# EDG cpfe coding style + +Canonical guide (read and follow; do not invent parallel rules): + +- [doc/source/code_style.rst](../../../doc/source/code_style.rst) + +Supporting philosophy / host–target placement notes: + +- [doc/source/int_overview.rst](../../../doc/source/int_overview.rst) + (section **Coding Philosophy**) + +Always-on project notes also list a short subset in +[AGENTS.md](../../../AGENTS.md); when the guide and local neighbors disagree +on formatting trivia, **match surrounding code** and prefer the RST for new +prose. + +## How to use this skill + +1. Open `doc/source/code_style.rst` and apply the section that matches what + you are editing (whitespace, wrapping, naming, variables, functions, + lambdas, classes, statements, preprocessor, …). +2. Keep edits within the touch radius of the change; do not reflow unrelated + code to “fix style.” +3. For changelog encoding (`Changes` files), use skill **edg-changes-example** + — that is not covered by the C style guide. + +## Enumerator constant casts + +The code base has many casts of an enumerator constant to its own enum type, +e.g. `(a_type_kind)tk_integer`. This is a historical leftover; do not +preserve it. + +- **Do not add** new casts of an enumerator constant to its own enum type. + Write the bare enumerator (`tk_integer`). +- **Remove** such casts in code adjacent to your change (within the touch + radius; see "How to use this skill"). Do not sweep the rest of the file or + tree just to delete them. +- Casts that convert to a *different* type (e.g., to `int` or `char`, or to + an enum other than the enumerator's own) are not covered by this rule. + +## Quick orientation (not a substitute for the RST) + +| Topic | Where in `code_style.rst` | +| --- | --- | +| 79 columns, 2-space indent, tab width 8 | General Whitespace Rules | +| Condition / statement wrapping | General Line Wrapping | +| Blank lines around decls / functions | General Line Breaks | +| Snake case; `a_` / `an_` type prefixes | Naming Convention | +| Global/namespace variables + comments | Writing a Variable | +| Parameter columns, docs after `)`, return | Writing a Function | +| Lambdas | Writing a Lambda | +| Classes, members, initializers | Writing a Class | +| `for` / `if` / `switch` / do-while | Statements | +| `#if` / includes layout | Preprocessor Directives | + +## Related skills + +- **edg-cpfe-frontend** — `src/` module map, options, Changes pointers +- **edg-changes-example** — UTF-8 / non-UTF-8 `Changes` encoding +- **edg-cpfe-build-test** — rebuild and test after style-sensitive edits +- **edg-python-style** — Python under `dev_tools/` (separate conventions) diff --git a/.agents/skills/edg-python-style/SKILL.md b/.agents/skills/edg-python-style/SKILL.md new file mode 100644 index 0000000000..e8892db6be --- /dev/null +++ b/.agents/skills/edg-python-style/SKILL.md @@ -0,0 +1,152 @@ +--- +name: edg-python-style +description: >- + Applies EDG Python conventions under dev_tools/ (pylibs and bin CLIs). + Use when writing or reviewing Python for edgtest, edgutil, edgacknowledg, + edgdocker, edgy, or other EDG tools. There is no separate formal Python + style RST — match existing code and these conventions. +--- + +# EDG Python style + +There is **no** dedicated Python style RST. Treat mature trees as normative, +especially: + +- [`dev_tools/bin/edgy`](../../../dev_tools/bin/edgy) +- [`dev_tools/pylibs/edgutil/`](../../../dev_tools/pylibs/edgutil/) +- [`dev_tools/pylibs/edgtest/`](../../../dev_tools/pylibs/edgtest/) + +Prefer matching the file you are editing over inventing a new dialect. + +## Layout and headers + +- New scripts under `dev_tools/bin/` start with `#!/usr/bin/env python3` when + executable. +- Keep the Apache-2.0 WITH LLVM-exception license / SPDX header used by + neighbors. +- Package code lives under `dev_tools/pylibs//` (importable as + `edgtest`, `edgutil`, `edgacknowledg`, …). Put `dev_tools/bin` on `PATH` + and `dev_tools/pylibs` on `PYTHONPATH` (direnv / [HACKING.md](../../../HACKING.md)). + +## Indentation and wrapping + +- **2-space** indentation (not PEP 8’s 4 spaces). +- Prefer wrapping near **79** columns when practical (same habit as the + front end); do not mass-reflow unrelated lines. +- Wrap long signatures, calls, and `typing` imports with parentheses; align + continuations with neighboring EDG Python, not Black/ruff defaults. + +### Function parameter lists + +If a `def` / `async def` signature does not fit on one line, wrap the +parameter list in the same spirit as EDG C++ (`code_style.rst` — Wrapping +Function Declarations): + +1. Prefer keeping the `def` name and opening `(` on the first line when the + first parameter(s) still fit; continue parameters on following lines. +2. When that is not enough (long name, many parameters, long annotations), + break immediately after `(` and put parameters on the next lines. +3. **Right-indent** wrapped parameter lines so they end at **column 79** + (right-aligned to the margin), packing as many parameters per line as + fit. Do not use a fixed left hanging indent (PEP 8 / Black’s 4-space + continuation). + +```python +def get_output_filename(env: Dict[str, str], + case_number: int, + command_number: int) -> str: + +async def invoke_recorded_process( + base_command: List[str], command_args: List[str], + working_dir: Path, env: Dict[str, str], + build_constants: BuildConstants, + output_file_handle, *, + filter_command: Optional[str] = None, debug: bool, + driver_debug: bool = False, + injected_command_args: List[str] = None) -> int: + +def parse_test_status_diff( + status_line: str) -> Tuple[str, ParsedTestStatusDiff]: +``` + +Return annotations (`-> …`) stay on the last parameter line when they fit +within 79 columns; otherwise wrap similarly to neighbors. + +## Spacing around `=` + +Put spaces around `=` in **keyword arguments**, defaults, and similar +bindings (distinct from PEP 8): + +```python +proc = await asyncio.create_subprocess_exec( + *cmd, + stdout = asyncio.subprocess.PIPE, + stderr = asyncio.subprocess.STDOUT +) +parser.add_argument('--jobs', dest = 'job_count', type = int) +``` + +Same for dict/kw literals in project style: `maxsize = None`, +`debug = True`. + +## Strings + +- Formatted: **`f"..."`** (double quotes). +- Plain / non-interpolated: **`'...'`** (single quotes). +- Prefer f-strings over `%` or `.format()` for new code. + +## Docstrings and comments + +- Prefer **`'''...'''`** (triple single quotes) for docstrings, matching + `edgutil` / `edgtest`. +- Comments are concise `#` lines; explain non-obvious intent, not the prior + state of the code. + +## Typing and APIs + +- Annotate public functions and `__init__` (`-> None` on constructors). +- Use `typing` names common in-tree (`Dict`, `List`, `Optional`, `Tuple`, …) + unless the file already uses newer builtins (`list`, `dict`) — match the + file. +- Keyword-only parameters after `*` are common for options: + `async def run(..., *, debug: bool)`. +- Prefer `pathlib.Path` over raw path strings for filesystem work. + +## Imports + +Typical order: + +1. Future / preamble hooks if required (e.g. ALSR disable in `edgy`) +2. Stdlib +3. Blank line +4. In-tree packages (`edgtest`, `edgutil`, …) +5. Blank line +6. `from … import …` (stdlib then local), with parenthesized multi-line + `typing` imports when long + +Avoid unused imports; do not introduce heavy formatters that rewrite the +whole tree. + +## Structure + +- Modules are flat and explicit; classes are fine for stateful helpers. +- Private helpers / module constants: leading `_`. +- Prefer `async def` + `asyncio` patterns already used in edgy / edgtest over + ad-hoc threads, unless matching an existing sync API. +- Fail loudly with clear errors; do not add speculative try/except “just in + case.” + +## What not to do + +- Do not run Black/ruff/autopep8 in a way that converts the tree to 4-space + PEP 8, strips spaces around `=`, or replaces right-aligned-to-79 + parameter wraps with fixed hanging indents. +- Do not copy front-end **C** style (`edg-cpfe-style`) into Python. +- Do not invent a second string-quoting scheme; follow `f"..."` / `'...'`. + +## Related skills + +- **acknowledg-client** / **acknowledg-bench** — domain code that should + follow this style +- **edg-cpfe-build-test** — running tools that live under `dev_tools/` +- **edg-cpfe-style** — C/C++ front-end style only (`doc/source/code_style.rst`) diff --git a/.git-hooks/pre-push.py b/.git-hooks/pre-push.py index e5c7717c8b..b723adac60 100755 --- a/.git-hooks/pre-push.py +++ b/.git-hooks/pre-push.py @@ -85,7 +85,10 @@ # Don't check GitHub workflow yml files; long lines are all but required. '.github/workflows/', # Don't check the error_msg.txt file. - 'src/error_msg.txt' + 'src/error_msg.txt', + # Don't check AGENT files. + 'AGENTS.md', + '.agents/' ] def _is_excluded_file_path(path_str: str) -> bool: diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..617311268d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,133 @@ +# EDG C++ Front End (cpfe) — agent notes + +This monorepo is the **Edison Design Group C and C++ front end**: a large +C/C++ codebase under `src/` (mostly `.c` compiled as C++), an in-tree test +kit under `tests/`, Docker/dev tooling under `dev_tools/`, and benchmarks +under `benchmarks/`. Detect the project root via `src/cfe.c` and `dev_tools/`. + +Human docs: [HACKING.md](HACKING.md), [BUILD.md](BUILD.md), +[CONTRIBUTING.md](CONTRIBUTING.md), [dev_annex/tutorials/](dev_annex/tutorials/), +https://edgcpp.org/doc/. + +## Version control + +**Never commit** in this repository unless the user explicitly asks for a +commit *in this conversation*. Finish the work, leave it staged or in the +working tree, and say so. This holds even when the change is complete, +tested, and obviously correct, and even when a commit was just requested for +a different repository or topic. + +**Never push** to a shared remote unless the user explicitly asks to push. + +Tests live in-tree at `tests/` (same clone). A request to commit or push +test changes still requires an explicit ask; it does not authorize unrelated +`src/` commits. + +## Monorepo layout + +| Path | Role | +| --- | --- | +| `src/` | Front end sources (`cfe.c` is `main`) | +| `tests/` | Test home: `tests/tests/`, `tests/expectations/`, `tests/runs/` | +| `dev_tools/bin/` | CLIs (`edg-exec`, `edg-docker-test`, …); put on `PATH` via direnv | +| `dev_tools/pylibs/` | EDG Python libraries | +| `benchmarks/` | Cachegrind benches, runs, baselines | +| `bases/docker/dev-env/gcc` | Default `EDG_BASE` for docker day posture | +| `build/gcc` | Default day-posture build dir (`linux-gcc-debug`) | +| `util/` | Front-end utilities | + +Optional env: `EDG_BASE`, `EDG_CONFIG`, `EDG_TEST_HOME` (override test home; +must contain `.edgy/config.json`). Default test home is `/tests`. + +## Build and test + +Prefer the **Docker** workflow. Do not build or run the project test suite on +the host toolchain for normal development; use `edg-exec` / +`edg-docker-test`. Setup: direnv (or add `dev_tools/bin` to `PATH` and +`dev_tools/pylibs` to `PYTHONPATH`), then `dev-init.py docker`. See +[HACKING.md](HACKING.md). + +For full recipes (presets, filters, recording), read the project skill +**edg-cpfe-build-test**. For good/bad commit bisection, read +**edg-cpfe-bisect**. + +Agent-friendly defaults: + +- `edg-docker-test --non-interactive …` — does not launch AcknowlEDG. +- `edg-docker-test-bisect` — automatic regression/fix bisect (skill + **edg-cpfe-bisect**; + [TEST_BISECT.md](dev_annex/tutorials/TEST_BISECT.md)). + +## Source overview (`src/`) + +`cfe.c` is the `main` path; `fe_init.c` / `fe_common.h` wire initialization. +Shared **IL** lives in `il.h` / `il.c` and `il_*` / `lower_*` modules. +Large TUs: `decls.c`, `expr.c`, `statements.c`, `types.c`. Lexer: +`lexical.c` / `preproc.c` / `macro.c`. Symbols: `symbol_tbl.c`; lookup: +`lookup.c`. Templates: `templates.c` (scan/rescan, substitution). Overload +resolution: `overload.c` (sensitive; broad changes may need full-suite +checks). + +Details: skills **edg-cpfe-frontend** (module map) and **edg-cpfe-style** +(coding style). Changelog encoding: skill **edg-changes-example**. + +## Comments and coding style (`src/`) + +Canonical rules: [doc/source/code_style.rst](doc/source/code_style.rst). +Agent workflow and section map: skill **edg-cpfe-style**. Philosophy notes: +[doc/source/int_overview.rst](doc/source/int_overview.rst) (Coding Philosophy). + +Hard always-on reminders (details in the RST / skill): + +- `/* … */` comments; function prose **after** the declarator; **79** columns. +- Snake case; type names use `a_` / `an_` prefixes. Casts like `(char*)p`. +- At most one `return`, at the end of the function. Match neighbors; do not + reflow unrelated code. + +## Editing `Changes` files + +Any `Changes` changelog in the tree (not only `src/Changes`) must be written +and kept as **valid UTF-8**. Prefer UTF-8 writes when adding or editing +entries. + +Examples that need non-UTF-8 bytes must use `edg-changes-example` (`«HHHH…»` +markers) and the standard note line — see skill **edg-changes-example**. After +edits, verify with Python that the file still decodes as UTF-8 (see skill +**edg-changes-example**). + +## Interesting options + +`--c++11` … `--c++26`; `--gnu=XXYYZZ`; `--clang_v=XXYYZZ`; +`--microsoft_v=XXYYZZ`; `-d-xyz` / `-dN` (needs a **DEBUG** build). + +## AcknowlEDG (`dev_tools/`) + +Interactive review UI + provider CLIs for tests and benchmarks. Canonical +Python package: `dev_tools/pylibs/edgacknowledg/`. The path +`dev_tools/services/acknowledg/server/edgacknowledg` is a **symlink** to that +package — edit pylibs only. Wire protocol: +[dev_tools/services/acknowledg/PROTOCOL.md](dev_tools/services/acknowledg/PROTOCOL.md). + +Skills: **acknowledg-client**, **acknowledg-bench**. Python conventions for +`dev_tools/` (no formal RST): skill **edg-python-style**. + +## Agent skills (portable) + +Project skills follow the open [Agent Skills](https://agentskills.io/) +format (`SKILL.md` + `name` / `description` frontmatter) and live only under +[`.agents/skills/`](.agents/skills/). Clients that implement the standard +(Codex, Zed, VS Code, Cursor, and others) discover them there. + +If an agent does not auto-load skills, open the matching +`.agents/skills//SKILL.md` when the task matches its description. + +| Skill | Path | Load when | +| --- | --- | --- | +| `edg-cpfe-build-test` | [`.agents/skills/edg-cpfe-build-test/SKILL.md`](.agents/skills/edg-cpfe-build-test/SKILL.md) | Building, `edg-docker-test`, recording | +| `edg-cpfe-bisect` | [`.agents/skills/edg-cpfe-bisect/SKILL.md`](.agents/skills/edg-cpfe-bisect/SKILL.md) | Good/bad commit bisect for a test regression or fix | +| `edg-cpfe-frontend` | [`.agents/skills/edg-cpfe-frontend/SKILL.md`](.agents/skills/edg-cpfe-frontend/SKILL.md) | Editing `src/`, IL/templates/overload, `Changes` | +| `edg-cpfe-style` | [`.agents/skills/edg-cpfe-style/SKILL.md`](.agents/skills/edg-cpfe-style/SKILL.md) | Front-end coding style (`doc/source/code_style.rst`) | +| `edg-python-style` | [`.agents/skills/edg-python-style/SKILL.md`](.agents/skills/edg-python-style/SKILL.md) | Python under `dev_tools/` (no formal RST; match in-tree) | +| `edg-changes-example` | [`.agents/skills/edg-changes-example/SKILL.md`](.agents/skills/edg-changes-example/SKILL.md) | Encoding non-UTF-8 examples in any `Changes` file | +| `acknowledg-client` | [`.agents/skills/acknowledg-client/SKILL.md`](.agents/skills/acknowledg-client/SKILL.md) | Batch/Direct client, reconnect, provider CLIs | +| `acknowledg-bench` | [`.agents/skills/acknowledg-bench/SKILL.md`](.agents/skills/acknowledg-bench/SKILL.md) | `.cgout` / Ir / baselines / bench review |