Skip to content

Commit a12ef81

Browse files
Merge branch 'main' into claude/lucid-pascal-0xL5X - resolve conflicts
Conflicts resolved by accepting PR branch versions (WASI S6b feature additions): - .machine_readable/6a2/META.a2ml: Updated with S6b completion - docs/ECOSYSTEM.adoc: Added S6b (sockets on-ramp) documentation - docs/TECH-DEBT.adoc: Updated with latest progress - docs/specs/SETTLED-DECISIONS.adoc: Updated decisions - lib/codegen.ml: Updated WASI codegen - lib/typecheck.ml: Updated type checking Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2 parents 1f96bca + e76b5f9 commit a12ef81

55 files changed

Lines changed: 4708 additions & 603 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.build/dune-project

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
(odoc (and (>= 2.4) :with-doc))
4040
(js_of_ocaml (>= 5.0))
4141
(js_of_ocaml-ppx (>= 5.0))
42-
(ocamlformat (and (>= 0.26) :with-test)))
42+
(ocamlformat (and (>= 0.26) :with-test))
43+
(bisect_ppx (and (>= 2.8) :with-test)))
4344
(tags
4445
(programming-language compiler webassembly affine-types dependent-types row-polymorphism effects ocaml)))

.claude/CLAUDE.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,117 @@ Rule: An issue is closed only by explicit merge of a PR with "Closes #N" OR by e
140140
Action (Hypatia): When asked to close an issue, confirm via a reply on the issue thread before doing so.
141141
Action (gitbot): Never use GitHub's "close issue" API directly; only close via PR merge with "Closes #N" keywords.
142142

143+
## Agent operations notes
144+
145+
Practical guidance for agents (Claude / other) operating in this repo,
146+
captured from parallel-bot session experience. Read once; saves turns.
147+
148+
### CI signal reliability
149+
150+
**"PR merged" does NOT mean "build green".** Auto-merge on this repo
151+
currently fires even when `build` or `lint` is failing — multiple
152+
recently-merged PRs (#334, #335, #336, #344) landed with `build` red,
153+
and the red persisted until PR #346's `FnExtern` interp fix. If you
154+
inherit a session reasoning about a recently-merged PR, do not assume
155+
its CI was green; check `mcp__github__pull_request_read` with method
156+
`get_check_runs` for the actual statuses, and check whether `main`
157+
itself is currently red before treating a build failure on your own
158+
PR as something *you* introduced.
159+
160+
### Reading CI logs
161+
162+
`WebFetch` against the GitHub Actions UI returns the React skeleton,
163+
not the log content. The fast paths for an agent are:
164+
165+
* `mcp__github__pull_request_read` with method `get_check_runs`
166+
per-job status (queued / in_progress / success / failure) with
167+
`details_url`. Sufficient for "did the build pass".
168+
* `mcp__github__pull_request_read` with method `get_status`
169+
combined commit status.
170+
* For actual log lines on a failed run, hand back to the user with
171+
`gh run view --log-failed <run-id>`; do not loop trying to scrape
172+
the UI.
173+
174+
### Known-failing baseline checks
175+
176+
These checks currently fail on *every* PR for repo-wide reasons, not
177+
because of any individual PR's changes. Do not waste turns
178+
investigating them on a per-PR basis:
179+
180+
* `vscode-smoke` — npm 404 on `@hyperpolymath/affine-vscode` (the
181+
in-editor harness depends on a not-yet-published npm package).
182+
* `migration-assistant` — was fixed by #342, but any branch created
183+
from a base older than #342 will still see it red until rebased.
184+
* `governance / Language / package anti-pattern policy` — flags the
185+
approved TypeScript exemptions (`affinescript-deno-test/*.ts`,
186+
`editors/vscode/test/*.js`, etc., all documented in this file's
187+
exemptions tables); the check has no allowlist for them.
188+
* The Hypatia security-scan bot comment — 143 findings; the bulk are
189+
the same TypeScript exemption hits + pre-existing root files. A
190+
real new finding will show as a *delta* in the count; otherwise
191+
ignore.
192+
193+
If a check from this list *changes status* on a PR (e.g.
194+
`vscode-smoke` suddenly passes, or Hypatia surfaces a new class of
195+
finding), that's signal worth investigating.
196+
197+
### Branching discipline with concurrent merges
198+
199+
When multiple agents are spawned in parallel, branch-creation time
200+
can lag `main` by hours and a stale base will silently revert other
201+
agents' work at merge time. **Before pushing any branch, run:**
202+
203+
```
204+
git fetch origin main
205+
git rebase origin/main
206+
```
207+
208+
Not just at branch-creation; immediately before push, after any
209+
in-session work. This guards against parallel-merge drift. Claude 1's
210+
STDLIB-04c branch (#337) accidentally reverted #334 and #335 because
211+
its base was stale; force-rebased to fix. Cheap to prevent, expensive
212+
to clean up.
213+
214+
### Post-squash-merge branch divergence
215+
216+
When a PR is squash-merged, the squashed commit on `main` gets a
217+
*new* SHA, distinct from any of the source-branch commits. If you
218+
then reset your local branch to `main` (or simply re-resolve it),
219+
`git status` reports N "ahead of origin/branch" — but those N
220+
commits are just the main-side commits the obsolete remote
221+
branch-tip never saw, not unpushed work.
222+
223+
Recognising the situation:
224+
225+
* The branch was already merged (PR closed, `merged: true`).
226+
* The local working tree matches `main`.
227+
* The remote branch still points at the *pre-merge* tip
228+
(`origin/<branch>` is an old SHA, not the squashed one).
229+
* `git log origin/<branch>..HEAD` lists commits that look like
230+
other people's work.
231+
232+
Safe fix — pick the one matching intent:
233+
234+
```
235+
git push origin --delete <branch> # done with the branch
236+
git push --force-with-lease origin <branch> # align the remote to main
237+
```
238+
239+
`--force-with-lease` is safe here because nothing on the remote
240+
branch is unmerged work; force-push without `--lease` only matters
241+
if someone else pushed concurrently, which is irrelevant for an
242+
already-merged branch you're cleaning up.
243+
244+
### Test-fixture hygiene for latent bug surfaces
245+
246+
When you add a stdlib `extern fn` (or any other new declaration
247+
shape), add a test that feeds it to *every* downstream consumer
248+
(parse, resolve, typecheck, interp, every codegen target that
249+
shouldn't reject it). The PR #346 `FnExtern` interp bug had survived
250+
since the interpreter was written because no test had ever fed an
251+
inline `extern fn` to `Interp.eval_program` — STDLIB-04a's tests
252+
were the first, and only then did the missing match arm fire.
253+
254+
Treat "first user of an existing-but-untested declaration shape" as a
255+
class-level surface, not a single test case.
256+

.github/workflows/ci.yml

Lines changed: 115 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,110 @@ jobs:
9898
run: opam exec -- dune build @doc
9999
continue-on-error: true
100100

101+
bench-visibility:
102+
# Visibility-only microbenchmarks per docs/standards/TESTING.adoc
103+
# §"Bench standards". Does NOT block merge. Promotion to a
104+
# ratcheted gate requires a calibrated baseline first.
105+
runs-on: ubuntu-latest
106+
107+
steps:
108+
- name: Checkout code
109+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
110+
111+
- name: Set up OCaml
112+
uses: ocaml/setup-ocaml@e32b06a3e831ff2fbc6f08cf35be2085e3918014 # v3
113+
with:
114+
ocaml-compiler: "5.1"
115+
116+
- name: Install dependencies
117+
run: opam install . --deps-only --with-test --with-doc
118+
119+
- name: Build bench targets
120+
run: opam exec -- dune build @bench --force
121+
continue-on-error: true
122+
123+
- name: Run benches
124+
id: bench
125+
continue-on-error: true
126+
run: |
127+
set -o pipefail
128+
opam exec -- dune runtest @bench --force 2>&1 | tee bench-output.log
129+
130+
- name: Render bench summary
131+
if: always()
132+
run: |
133+
{
134+
echo "# AffineScript microbench output (visibility-only)"
135+
echo ""
136+
echo "Policy: see [docs/standards/TESTING.adoc](docs/standards/TESTING.adoc)."
137+
echo ""
138+
if [ -f bench-output.log ]; then
139+
echo '```'
140+
cat bench-output.log
141+
echo '```'
142+
fi
143+
} >> "$GITHUB_STEP_SUMMARY"
144+
145+
- name: Upload bench log
146+
if: always()
147+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
148+
with:
149+
name: bench-output
150+
path: bench-output.log
151+
retention-days: 30
152+
153+
coverage-visibility:
154+
# Visibility-only coverage report per
155+
# docs/standards/TESTING.adoc §"Coverage (visibility-only)".
156+
# No merge-blocking floor today.
157+
runs-on: ubuntu-latest
158+
159+
steps:
160+
- name: Checkout code
161+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
162+
163+
- name: Set up OCaml
164+
uses: ocaml/setup-ocaml@e32b06a3e831ff2fbc6f08cf35be2085e3918014 # v3
165+
with:
166+
ocaml-compiler: "5.1"
167+
168+
- name: Install dependencies
169+
run: opam install . --deps-only --with-test --with-doc
170+
171+
- name: Run tests with bisect_ppx instrumentation
172+
continue-on-error: true
173+
run: |
174+
opam exec -- dune runtest --force --instrument-with bisect_ppx
175+
176+
- name: Generate HTML coverage report
177+
continue-on-error: true
178+
run: |
179+
opam exec -- bisect-ppx-report html -o _coverage --title="AffineScript coverage"
180+
opam exec -- bisect-ppx-report summary | tee coverage-summary.txt
181+
182+
- name: Render coverage summary
183+
if: always()
184+
run: |
185+
{
186+
echo "# AffineScript coverage (visibility-only)"
187+
echo ""
188+
echo "Policy: see [docs/standards/TESTING.adoc](docs/standards/TESTING.adoc)."
189+
echo ""
190+
if [ -f coverage-summary.txt ]; then
191+
echo '```'
192+
cat coverage-summary.txt
193+
echo '```'
194+
fi
195+
} >> "$GITHUB_STEP_SUMMARY"
196+
197+
- name: Upload coverage HTML
198+
if: always()
199+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
200+
with:
201+
name: coverage-html
202+
path: _coverage
203+
retention-days: 30
204+
101205
vscode-smoke:
102206
# In-editor end-to-end smoke test for the .affine VS Code extension
103207
# (issue #139). Loads the compiled out/extension.cjs in a real VS Code
@@ -183,16 +287,22 @@ jobs:
183287
# ReScript source. Picks the existing res-to-affine test fixture
184288
# so any drift in the pinned commit's syntactic surface area
185289
# surfaces here rather than at walker-rule writing time.
290+
#
291+
# NOTE: tree-sitter-cli >= 0.25 repurposed `--paths` to mean "a
292+
# file listing input source paths", not "a directory containing
293+
# a grammar". Grammar lookup is now driven by the current
294+
# working directory (the CLI walks up looking for grammar.js /
295+
# src/parser.c), so we cd into the vendored grammar tree and
296+
# pass an absolute path to the fixture. Without this, the step
297+
# fails with `Failed to read paths file ... Is a directory`.
186298
run: |
187299
shopt -s nullglob
188300
fixtures=(tools/res-to-affine/test/fixtures/*.res)
189301
if [ ${#fixtures[@]} -eq 0 ]; then
190302
echo "no .res fixtures to smoke-parse; skipping"
191303
exit 0
192304
fi
193-
tree-sitter parse \
194-
--quiet \
195-
"${fixtures[0]}" \
196-
--paths tools/vendor/tree-sitter-rescript \
197-
> /dev/null
305+
fixture_abs="$(realpath "${fixtures[0]}")"
306+
( cd tools/vendor/tree-sitter-rescript \
307+
&& tree-sitter parse --quiet "${fixture_abs}" > /dev/null )
198308
echo "smoke-parsed: ${fixtures[0]}"

.github/workflows/panic-attack.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# panic-attack.yml — estate compliance scanner, weekly schedule.
3+
#
4+
# Per docs/standards/PANIC-ATTACK.adoc:
5+
# - This runs on a weekly cron; per-PR invocation is intentionally
6+
# out of scope (CodeQL/Semgrep/secret-scanner cover per-PR).
7+
# - Findings are surfaced as a workflow summary. Critical/high
8+
# findings that are not in the false-positive registry will
9+
# open a tracking issue.
10+
# - Release cuts call `just panic` manually per RELEASE.md.
11+
#
12+
# Tool: `panic-attacker` Rust crate from
13+
# road-skate/features/panic-attacker. Installed via cargo.
14+
15+
name: Panic-Attack (compliance scan)
16+
17+
on:
18+
schedule:
19+
# Sundays 03:00 UTC — quiet window for the estate's CI fleet.
20+
- cron: '0 3 * * 0'
21+
workflow_dispatch:
22+
23+
# Read-only audit workflow — safe to cancel superseded runs.
24+
concurrency:
25+
group: ${{ github.workflow }}-${{ github.ref }}
26+
cancel-in-progress: true
27+
28+
permissions:
29+
contents: read
30+
issues: write # opens a tracking issue on unresolved critical/high
31+
32+
jobs:
33+
scan:
34+
runs-on: ubuntu-latest
35+
steps:
36+
- name: Checkout
37+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
38+
with:
39+
persist-credentials: false
40+
41+
- name: Install Rust toolchain (stable)
42+
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # stable
43+
with:
44+
toolchain: stable
45+
46+
- name: Install panic-attacker
47+
# Source-install from the estate crate. Once published to
48+
# crates.io this can switch to `cargo install panic-attacker`.
49+
run: |
50+
if [ -d road-skate/features/panic-attacker ]; then
51+
cargo install --path road-skate/features/panic-attacker --locked || \
52+
cargo install --path road-skate/features/panic-attacker
53+
else
54+
echo "::warning::road-skate/features/panic-attacker not found; trying crates.io"
55+
cargo install panic-attacker || {
56+
echo "::error::panic-attacker unavailable from both estate path and crates.io"
57+
exit 1
58+
}
59+
fi
60+
61+
- name: Run panic-attack
62+
id: scan
63+
continue-on-error: true
64+
run: |
65+
set -o pipefail
66+
panic-attack assail 2>&1 | tee panic-attack.log
67+
68+
- name: Render summary
69+
if: always()
70+
run: |
71+
{
72+
echo "# panic-attack weekly scan"
73+
echo ""
74+
echo "Repository: \`${{ github.repository }}\`"
75+
echo "Ref: \`${{ github.sha }}\`"
76+
echo ""
77+
if [ -f panic-attack.log ]; then
78+
echo "## Output"
79+
echo '```'
80+
tail -200 panic-attack.log
81+
echo '```'
82+
else
83+
echo "(no output captured)"
84+
fi
85+
echo ""
86+
echo "Policy: see [docs/standards/PANIC-ATTACK.adoc](docs/standards/PANIC-ATTACK.adoc)."
87+
} >> "$GITHUB_STEP_SUMMARY"
88+
89+
- name: Upload log artifact
90+
if: always()
91+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
92+
with:
93+
name: panic-attack-log
94+
path: panic-attack.log
95+
retention-days: 30

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ secrets/
8080
# Test/Coverage
8181
/coverage/
8282
htmlcov/
83+
/_coverage/
84+
bisect*.coverage
85+
86+
# Benchmark archives (visibility-only output, see docs/standards/TESTING.adoc)
87+
/bench-runs/
8388

8489
# Logs
8590
*.log

0 commit comments

Comments
 (0)