Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/compile/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Presets: `gcc-` / `clang-` + `debug` (coverage), `release`, `asan` (ASan + UBSan

## What CI builds on every PR

1. `clang-format` **19** over the tree (gates everything). Check `clang-format --version`: if it is not 19, ask the user to install it; a format diff seen only in CI points to the version gap.
1. `clang-format` at the version pinned in `build.yml` over the tree (gates everything). Check `clang-format --version`: if it differs, ask the user to install it; a format diff seen only in CI points to the version gap.
2. gcc and clang x amd64 and arm64, in `joinframework/join-ci`: `release` then `debug`; `asan` on amd64.
3. CodeQL on `gcc-debug`.
4. `gcovr` coverage on amd64 (gcc and clang, kept as artifacts); only gcc's is uploaded to Coveralls/Codecov/Codacy by `deploy-coverage`, which can fail the run.
Expand Down
8 changes: 4 additions & 4 deletions .agents/skills/conformance/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ description: Check dtoa/atod correctness versus main with the dconv-benchmark co

Required at closing when conversion code changes (`dconv/include/dconv/`). No timing: any machine works. For speed see skill `performance`.

Clone `https://github.com/joinframework/dconv-benchmark.git` into `<bench>` under `<scratchpad>`, never into the dconv tree. It pins dconv to a release tag, so point both builds at local checkouts, `origin/main` in a worktree:
`<bench>` is `dconv-benchmark` next to dconv (`../dconv-benchmark`); if missing, clone `https://github.com/joinframework/dconv-benchmark.git` there, never into the dconv tree. It pins dconv to a release tag, so point both builds at local checkouts, `origin/main` in a worktree:

```bash
git -C <dconv> fetch origin main && git -C <dconv> worktree add --detach <scratchpad>/base origin/main
cmake -S <bench> -B <bench>/build/main -DCMAKE_BUILD_TYPE=Release -DFETCHCONTENT_SOURCE_DIR_DCONV=<scratchpad>/base
cmake -S <bench> -B <bench>/build/branch -DCMAKE_BUILD_TYPE=Release -DFETCHCONTENT_SOURCE_DIR_DCONV=<dconv>
cmake -S <bench> -B <scratchpad>/build/main -DCMAKE_BUILD_TYPE=Release -DFETCHCONTENT_SOURCE_DIR_DCONV=<scratchpad>/base
cmake -S <bench> -B <scratchpad>/build/branch -DCMAKE_BUILD_TYPE=Release -DFETCHCONTENT_SOURCE_DIR_DCONV=<dconv>
cmake --build <build> --target conformance
<build>/benchmarks/conformance/conformance --output <scratchpad>/conformance-<main|branch>.json
```

Remove the worktree at the end (`git worktree remove --force`), even on failure.
Check each tree measures the intended dconv: `grep FETCHCONTENT_SOURCE_DIR_DCONV <build>/CMakeCache.txt` must show its path, otherwise the numbers are the pinned tag. Remove the worktree at the end (`git worktree remove --force`), even on failure.

## Pass criteria

Expand Down
8 changes: 4 additions & 4 deletions .agents/skills/coverage/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ cmake --preset $C-debug && cmake --build --preset $C-debug
find build/$C/debug -name '*.gcda' -delete
ctest --preset $C-debug
gcovr --root . --gcov-executable "$GCOV" --exclude-pattern-prefix LCOV --exclude-branches-by-pattern '.*' \
--exclude '.*/tests/.*' --exclude 'tools/.*' --json-summary <scratchpad>/$C-after.json --print-summary build/$C/debug
--exclude '.*/tests/.*' --exclude 'tools/.*' --json-summary <scratchpad>/$C-${SIDE:-after}.json --print-summary build/$C/debug
```

Baseline: same steps on `origin/main` in a worktree (`git fetch origin main && git worktree add --detach <scratchpad>/base origin/main`) into `$C-before.json`; remove it at the end (`git worktree remove --force`), even on failure.
Baseline: same steps on `origin/main` in a worktree (`git fetch origin main && git worktree add --detach <scratchpad>/base origin/main`) with `SIDE=before`, into `$C-before.json`; remove it at the end (`git worktree remove --force`), even on failure.

Compare the total and every changed header. Any drop fails: add tests (or `LCOV_EXCL_*` for truly unreachable code, sparingly) and re-measure.

Needs `gcovr` and `llvm-cov` matching clang. Without them locally, save the steps above to `<scratchpad>/coverage.sh` and run it in CI's image (`docker pull joinframework/join-ci:latest` first if it lacks `gcovr`), on copies so the host `build/` is untouched:
Needs `gcovr` and `llvm-cov` matching clang. Without them locally, save the steps above, after `set -e`, to `<scratchpad>/coverage.sh` and run it in CI's image (`docker pull joinframework/join-ci:latest` first if it lacks `gcovr`), on copies so the host `build/` is untouched:

```bash
rsync -a --exclude build ./ <scratchpad>/head/
docker run --rm --user "$(id -u):$(id -g)" -v <scratchpad>:<scratchpad> -w <scratchpad>/head \
joinframework/join-ci:latest bash <scratchpad>/coverage.sh
```

Same for the baseline with `-w <scratchpad>/base`. If neither works, report coverage as **not verified**, never "not degraded".
Same for the baseline with `-e SIDE=before -w <scratchpad>/base`. If neither works, report coverage as **not verified**, never "not degraded".
2 changes: 1 addition & 1 deletion .agents/skills/create-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ description: Branch, commit, push and open a PR with the dconv conventions. Use
- `hotfix/`: small quick fix, minimal diff.

Ask once if bugfix vs hotfix is unclear.
4. **Version**: bump `VERSION` in every PR: minor for a feature or an API change, patch otherwise (bugfix, hotfix, tools, CI, docs, agent config), major only on request. Minor or major also sets `PROJECT_NUMBER` = `<major>.<minor>` in `doxyfile`.
4. **Version**: bump `VERSION` in every PR, unless it is already ahead of the latest tag at an equal or higher level: minor for a feature or an API change, patch otherwise (bugfix, hotfix, tools, CI, docs, agent config), major only on request. Minor or major also sets `PROJECT_NUMBER` = `<major>.<minor>` in `doxyfile`.
5. **Commit**: explicit paths (no `git add -A`), one commit. Message: one capitalized imperative line, about 50 chars, no period, no body, no PR number, no trailer: `Stop installing tool headers`, or `Schubfach algorithm` for a new algorithm.
6. **Push**: `git push -u origin <branch>`. Never rewrite pushed history; report a rejected push.
7. **PR**: title = commit line, no body. Ask first; with `gh`: `gh pr create --base main --title "<Title>" --body ""`. Otherwise give `https://github.com/joinframework/dconv/compare/main...<branch>?expand=1`.
Expand Down
6 changes: 3 additions & 3 deletions .agents/skills/performance/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Required at closing when conversion code changes (`dconv/include/dconv/`). Done

## 1. Ask first

Ask the user: **is a real machine available (ideally x86_64 and aarch64)?** Clone `dconv-benchmark` there as in skill `conformance`, never into the dconv tree.
Ask the user: **is a real machine available (ideally x86_64 and aarch64)?** Use the `dconv-benchmark` checkout of skill `conformance`.

## 2. Performance mode

Expand All @@ -25,10 +25,10 @@ Use `sudo` only when justified; otherwise give the commands with the `! ` prefix

## 3. Compare

Same `build/main` / `build/branch` setup as skill `conformance` (local checkouts via `FETCHCONTENT_SOURCE_DIR_DCONV`, `main` in a worktree). Build `bench_dtoa_dconv` and `bench_atod_dconv` (+ `bench_dtoa_dconvgrisu{,2,3}` if Grisu changed).
Same `<scratchpad>/build/main` / `<scratchpad>/build/branch` setup as skill `conformance` (local checkouts via `FETCHCONTENT_SOURCE_DIR_DCONV`, `main` in a worktree). Build `bench_dtoa_dconv` and `bench_atod_dconv` (+ `bench_dtoa_dconvgrisu{,2,3}` if Grisu changed).

Binaries: `<build>/benchmarks/performance/bench_*`. Export the same `GLIBC_TUNABLES` as `scripts/run_bench.sh`. Run them alternately main/branch, several times, `taskset -c 2`, with `--benchmark_min_warmup_time=0.1 --benchmark_min_time=0.2s`; keep the minimum. Datasets: `Canada`, `Random`, `Unit`, `Integer`, `Digits01..17`. Remove the worktree at the end, even on failure.

## 4. Report

Machine (CPU, arch, governor/boost, native or WSL2), repetitions, and a before/after table per dataset with the relative change. A change is real only beyond the spread; never claim an unmeasured gain.
Machine (CPU, arch, governor/boost, native or WSL2), repetitions, and a before/after table per dataset with the relative change. A change is real only beyond the spread; never claim an unmeasured gain. When a delta is close to the spread, rerun with 5 repetitions per side and decide with google benchmark's `compare.py` (`<build>/_deps/googlebenchmark-src/tools/compare.py benchmarks <main>.json <branch>.json`, needs `scipy`; merge each side's runs into one JSON first): with 3 per side its Mann-Whitney test cannot reach p < 0.05. A mechanism read from the diff is a hypothesis until `perf stat -e cycles,instructions,cache-misses,branch-misses` shows it (needs `kernel.perf_event_paranoid` <= 1).
1 change: 1 addition & 0 deletions .agents/skills/release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ description: Tag and publish a dconv release from main. Use when asked to releas
5. **Downstream**: list the updates, each done in its own repo with its own PR, only if the user asks:
- join: `find_package(dconv <version> REQUIRED)` in `data/CMakeLists.txt`, `find_dependency(dconv <version>)` in `cmake/joinConfig.cmake.in`, `libdconv-dev (>= <version>)` in `cmake/packaging.cmake`, the `.deb` URL and file name in `README.md`.
- dconv-benchmark: `GIT_TAG v<version>` in `cmake/deps.cmake`, the dconv rows of the README libraries table (results and plots only after a new benchmark run).
- json-benchmark, pack-benchmark: `GIT_TAG v<version>` in `cmake/deps.cmake`.
30 changes: 15 additions & 15 deletions .claude/hooks/format.sh
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
#!/usr/bin/env bash
missing () {
echo "format hook: $1 is missing, nothing was formatted. Ask the user to install it (AGENTS.md, Definition of done)." >&2
exit 2
}
input=$(cat)
case "$input" in *'.hpp"'*|*'.cpp"'*) ;; *) exit 0 ;; esac
command -v jq >/dev/null || missing jq
file=$(jq -r '.tool_input.file_path // empty' <<<"$input")
case "$file" in
*.hpp|*.cpp)
command -v clang-format >/dev/null || missing clang-format
[ -f "$file" ] || exit 0
clang-format -i "$file" || { echo "format hook: clang-format failed on $file" >&2; exit 2; }
;;
esac
re='"file_path": *"([^"]*\.(hpp|cpp))"'
sre='"session_id": *"([^"]*)"'
in=$(cat)
[[ $in =~ $sre ]] && mark=${TMPDIR:-/tmp}/format-hook-${BASH_REMATCH[1]}
[[ $in =~ $re ]] || exit 0
file=${BASH_REMATCH[1]}
[[ $file == "$CLAUDE_PROJECT_DIR"/* ]] || exit 0
v=$(sed -n "s/.*clang-format-version: *'\([0-9]*\)'.*/\1/p" "$CLAUDE_PROJECT_DIR"/.github/workflows/build.yml | head -1)
cf=clang-format-$v
command -v "$cf" >/dev/null || cf=clang-format
command -v "$cf" >/dev/null || { echo "format hook: clang-format is missing, nothing was formatted. Ask the user to install it (AGENTS.md, Definition of done)." >&2; exit 2; }
[ -f "$file" ] || exit 0
"$cf" -i "$file" || { echo "format hook: $cf failed on $file" >&2; exit 2; }
have=$("$cf" --version | sed -n 's/.*version \([0-9]*\)\..*/\1/p')
[ -z "$v" ] || [ "$have" = "$v" ] || [ -e "$mark" ] || { touch "$mark"; echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"format hook: formatted with clang-format $have, CI uses $v; ask the user to install clang-format $v.\"}}"; }
exit 0
2 changes: 1 addition & 1 deletion .claude/rules/cpp-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ paths:

# C++ style

`.clang-format` is enforced (hook, or `clang-format` 19); never hand-format.
`.clang-format` is enforced (hook, or `clang-format` at the CI version); never hand-format.

## Layout
- C++20, header-only, no language extensions; GNU builtins and attributes already in use (`__builtin_expect`, `__attribute__ ((cold, noinline))`) are accepted. Space before every `(` (`foo (a)`, `TEST (s, n)`); Allman braces on every control statement.
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ cmake --preset gcc-debug && cmake --build --preset gcc-debug && ctest --preset g

## Agent files

Skills in `.agents/skills/<name>/SKILL.md`: compile, test, coverage, conformance, performance, add-gtest, gen-tables, create-pr, release; `<scratchpad>` in them is a temporary directory outside the repository. Run `clang-format` (CI uses 19) on changed C++ files.
Skills in `.agents/skills/<name>/SKILL.md`: compile, test, coverage, conformance, performance, add-gtest, gen-tables, create-pr, release; `<scratchpad>` in them is a temporary directory outside the repository. Run `clang-format` on changed C++ files at the version pinned in `.github/workflows/build.yml`.

Changes to `AGENTS.md` and `.agents/` keep them tool-agnostic and short; `.claude/` is Claude Code only.

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.2.2
1.2.3
Loading