Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8040325
Automerge master back to develop
github-actions[bot] May 17, 2026
12913cb
Bump @opentelemetry/semantic-conventions from 1.40.0 to 1.41.1
dependabot[bot] May 18, 2026
1401278
Bump eslint from 10.3.0 to 10.4.0
dependabot[bot] May 18, 2026
a64369c
Bump qs from 6.14.2 to 6.15.2
dependabot[bot] May 23, 2026
1c2d4d1
Bump ts-jest from 29.4.9 to 29.4.11
dependabot[bot] May 25, 2026
b532626
Bump @types/node from 25.6.2 to 25.9.1
dependabot[bot] May 25, 2026
a2a78b1
Bump typescript-eslint from 8.59.2 to 8.59.4
dependabot[bot] May 25, 2026
ed27822
Bump protobufjs from 7.5.6 to 7.6.1
dependabot[bot] May 28, 2026
01e9a90
Bump @grpc/grpc-js from 1.14.3 to 1.14.4
dependabot[bot] Jun 11, 2026
6cb89a6
Bump @opentelemetry/core, @opentelemetry/exporter-metrics-otlp-grpc, …
dependabot[bot] Jun 17, 2026
51c4bcb
Bump hono from 4.12.18 to 4.12.26
dependabot[bot] Jun 20, 2026
31fed00
Bump actions/checkout from 6 to 7
dependabot[bot] Jun 22, 2026
f5ecdd3
memfile v3: pure-C graph store, ops in C, transparent auto-migration
Levalicious Jun 27, 2026
17ba411
fix(lint): drop dead find_path budget consts; remove redundant smoke …
Levalicious Jun 27, 2026
014b7b8
fix(ci): import jest globals in migrate.test.ts
Levalicious Jun 27, 2026
b122b0d
fix(migrate): read old v1/v2 format in pure TS; drop old-addon depend…
Levalicious Jun 27, 2026
1061518
Merge pull request #200 from Levalicious/feature/memfile-v3
Levalicious Jun 27, 2026
f891ff2
Merge pull request #199 from Levalicious/dependabot/github_actions/de…
Levalicious Jun 27, 2026
71fd084
Merge pull request #188 from Levalicious/dependabot/npm_and_yarn/deve…
Levalicious Jun 27, 2026
682c690
Merge pull request #189 from Levalicious/dependabot/npm_and_yarn/deve…
Levalicious Jun 27, 2026
1ffded8
Merge pull request #191 from Levalicious/dependabot/npm_and_yarn/deve…
Levalicious Jun 27, 2026
2974b64
Merge pull request #192 from Levalicious/dependabot/npm_and_yarn/deve…
Levalicious Jun 27, 2026
a31dbb0
Merge pull request #193 from Levalicious/dependabot/npm_and_yarn/deve…
Levalicious Jun 27, 2026
3ac2c1e
bench: per-op adaptive microbench + base-vs-head CI gate + creep history
Levalicious Jun 27, 2026
eb672e6
proofs: ACSL/WP for the adjacency edge encoding + Frama-C CI gate
Levalicious Jun 27, 2026
042368f
Merge pull request #201 from Levalicious/feature/benchmarks
Levalicious Jun 28, 2026
e21bd86
Merge branch 'dependabot/npm_and_yarn/qs-6.15.2' into release/0.0.27
Levalicious Jun 28, 2026
2b9519e
Merge branch 'dependabot/npm_and_yarn/protobufjs-7.6.1' into release/…
Levalicious Jun 28, 2026
85ced7a
Merge branch 'dependabot/npm_and_yarn/grpc/grpc-js-1.14.4' into relea…
Levalicious Jun 28, 2026
19af121
Merge branch 'dependabot/npm_and_yarn/multi-56c3bea1c1' into release/…
Levalicious Jun 28, 2026
27fefa2
Merge branch 'dependabot/npm_and_yarn/hono-4.12.26' into release/0.0.27
Levalicious Jun 28, 2026
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 .github/workflows/automerge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Configure Git
run: |
git config user.name 'github-actions[bot]'
Expand Down
88 changes: 88 additions & 0 deletions .github/workflows/benchmark-history.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: benchmark-history

# Accumulate a reference-normalized benchmark time-series so slow creep (which the
# per-PR head-vs-base gate is blind to) becomes visible. On every push to master
# we run the bench, append one sample to an append-only NDJSON on the ORPHAN
# `bench-history` branch (isolated from master/PR history — no churn), and check
# the new sample against a rolling baseline. Creep is a SOFT-fail + commit comment.
on:
push:
branches: [master]

permissions:
contents: write

concurrency:
group: bench-history
cancel-in-progress: false

jobs:
accumulate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20

- name: Build and run bench (interleaved)
run: |
make -C native bench >/dev/null 2>&1
cp /tmp/mf_test_bench /tmp/bench
for i in $(seq 1 7); do /tmp/bench > "/tmp/run_$i.json"; done

- name: Aggregate one normalized sample
run: |
R=$(ls /tmp/run_*.json | paste -sd,)
node scripts/bench-aggregate.mjs --runs "$R" \
--sha "$GITHUB_SHA" --date "$(date -u +%FT%TZ)" > /tmp/sample.ndjson
cat /tmp/sample.ndjson

- name: Append to bench-history + creep check
id: creep
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git fetch origin bench-history || true
if git rev-parse --verify origin/bench-history >/dev/null 2>&1; then
git worktree add /tmp/hist origin/bench-history
else
git worktree add --detach /tmp/hist
git -C /tmp/hist checkout --orphan bench-history
git -C /tmp/hist rm -rf . >/dev/null 2>&1 || true
fi
touch /tmp/hist/history.ndjson
# creep check: new sample vs the EXISTING history (before appending)
set +e
node scripts/bench-creep.mjs --history /tmp/hist/history.ndjson --sample /tmp/sample.ndjson > /tmp/creep.md
echo "rc=$?" >> "$GITHUB_OUTPUT"
set -e
cat /tmp/creep.md
# append + push (one retry if the branch advanced under us)
cat /tmp/sample.ndjson >> /tmp/hist/history.ndjson
git -C /tmp/hist add history.ndjson
git -C /tmp/hist commit -m "bench: sample for ${GITHUB_SHA}"
git -C /tmp/hist push origin HEAD:bench-history || {
git -C /tmp/hist fetch origin bench-history
git -C /tmp/hist reset --soft origin/bench-history
cat /tmp/sample.ndjson >> /tmp/hist/history.ndjson
git -C /tmp/hist add history.ndjson
git -C /tmp/hist commit -m "bench: sample for ${GITHUB_SHA} (retry)"
git -C /tmp/hist push origin HEAD:bench-history
}

- name: Comment creep on commit
if: steps.creep.outputs.rc != '0'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const body = '<!-- bench-creep -->\n' + fs.readFileSync('/tmp/creep.md', 'utf8');
await github.rest.repos.createCommitComment({ ...context.repo, commit_sha: context.sha, body });

- name: Soft-fail on creep
if: steps.creep.outputs.rc != '0'
run: |
echo "::warning::Benchmark slow-creep detected vs rolling baseline — see the commit comment. (soft-fail)"
exit 1
88 changes: 88 additions & 0 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: benchmark

# Per-op graph-store microbenchmark, base vs head, on ONE runner — no committed
# baseline values (nothing for git to churn). Builds the SAME harness against the
# base and head store sources, runs them interleaved, frequency-corrects head to
# base via a fixed-work reference op, and diffs the medians. SOFT-fail: a
# regression turns the check red and posts a PR comment, but this check is not
# meant to be a required status, so it never hard-blocks a merge.
on:
pull_request:

permissions:
contents: read
pull-requests: write

jobs:
op-bench:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-node@v4
with:
node-version: 20

- name: Build head benchmark
run: |
make -C native bench >/dev/null 2>&1
cp /tmp/mf_test_bench /tmp/bench_head

- name: Build base benchmark (head harness, base store sources)
id: base
run: |
BASE_SHA="${{ github.event.pull_request.base.sha }}"
git worktree add --detach /tmp/base "$BASE_SHA"
mkdir -p /tmp/base/native
# Overlay the head harness so ONLY the measured store code differs.
cp native/op_bench.c /tmp/base/native/op_bench.c
cp native/Makefile /tmp/base/native/Makefile
if make -C /tmp/base/native bench >/tmp/base_build.log 2>&1; then
cp /tmp/mf_test_bench /tmp/bench_base
echo "ok=1" >> "$GITHUB_OUTPUT"
else
echo "ok=0" >> "$GITHUB_OUTPUT"
echo "Base build failed — base likely has no comparable benchmark/store. Skipping comparison."
tail -25 /tmp/base_build.log || true
fi

- name: Run interleaved and compare
if: steps.base.outputs.ok == '1'
id: cmp
run: |
for i in $(seq 1 5); do
/tmp/bench_head > "/tmp/head_$i.json"
/tmp/bench_base > "/tmp/base_$i.json"
done
H=$(ls /tmp/head_*.json | paste -sd,)
B=$(ls /tmp/base_*.json | paste -sd,)
set +e
node scripts/bench-compare.mjs --base "$B" --head "$H" > /tmp/report.md
echo "rc=$?" >> "$GITHUB_OUTPUT"
set -e
cat /tmp/report.md

- name: Comment results on PR
if: steps.base.outputs.ok == '1'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const body = '<!-- op-bench -->\n' + fs.readFileSync('/tmp/report.md', 'utf8');
const { owner, repo } = context.repo;
const issue_number = context.payload.pull_request.number;
const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number });
const existing = comments.find((c) => c.body && c.body.includes('<!-- op-bench -->'));
if (existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number, body });
}

- name: Soft-fail on regression
if: steps.base.outputs.ok == '1' && steps.cmp.outputs.rc != '0'
run: |
echo "::warning::Per-op benchmark regression detected — see the op-bench PR comment. (soft-fail: not a required check)"
exit 1
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
build-mode: none
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
Expand Down
49 changes: 49 additions & 0 deletions .github/workflows/proofs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: proofs

# Frama-C/WP proofs of the v3 store's tractable algorithms: allocator size
# quantization + open-addressing probe (fc_proofs), name-index traversal bounds
# (fc_index), free-list descent memory-safety (fc_tree), and the adjacency edge
# encoding round-trip/injectivity + edge-iteration bounds (fc_graph). These prove
# memory-model-clean abstractions of the real algorithms — no mmap region for WP
# to reason about. Only runs when the proofs (or their build) change.
on:
pull_request:
paths:
- 'native/fc_*.c'
- 'native/fc_stubs.h'
- 'native/Makefile'
- '.github/workflows/proofs.yml'
push:
branches: [master]
paths:
- 'native/fc_*.c'
- 'native/fc_stubs.h'
- 'native/Makefile'
- '.github/workflows/proofs.yml'

permissions:
contents: read

jobs:
wp:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: "5.2"

- name: Install Frama-C + Alt-Ergo
run: opam install -y frama-c alt-ergo

- name: Detect provers
run: |
eval $(opam env)
why3 config detect

- name: Run WP proofs
run: |
eval $(opam env)
frama-c -version
make -C native proofs
2 changes: 1 addition & 1 deletion .github/workflows/sync-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/typescript.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: 22
Expand All @@ -35,7 +35,7 @@ jobs:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: 22
Expand Down Expand Up @@ -63,7 +63,7 @@ jobs:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: 22
Expand All @@ -83,7 +83,7 @@ jobs:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: 22
Expand Down
13 changes: 8 additions & 5 deletions binding.gyp
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
{
"targets": [
{
"target_name": "memoryfile",
"target_name": "graphstore",
"sources": [
"native/memoryfile.c",
"native/binding.c"
"native/stringtable.c",
"native/graph.c",
"native/graphbind.c"
],
"include_dirs": [
"native"
],
"cflags": ["-std=c11", "-Wall", "-Wextra", "-O2"],
"cflags": ["-std=c11", "-Wall", "-Wextra", "-O2", "-fno-strict-aliasing", "-fno-math-errno"],
"conditions": [
["OS=='linux'", {
"defines": ["_GNU_SOURCE"]
"defines": ["_GNU_SOURCE"],
"libraries!": ["-lnode"]
}],
["OS=='mac'", {
"xcode_settings": {
"OTHER_CFLAGS": ["-std=c11", "-Wall", "-Wextra", "-O2"]
"OTHER_CFLAGS": ["-std=c11", "-Wall", "-Wextra", "-O2", "-fno-strict-aliasing", "-fno-math-errno"]
}
}]
]
Expand Down
61 changes: 61 additions & 0 deletions native/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Dev-time test build for the v3 C store.
#
# Compiles the fuzz/unit harnesses with AddressSanitizer + UBSan AND the
# test-only double-free detector (-DMEMFILE_DOUBLE_FREE_CHECK). The PUBLISHED
# addon is built by binding.gyp / node-gyp, which NEVER defines that macro, so
# the detector is physically absent from the shipped binary (the production
# allocator stays clean — Principle_FixCauseNotChecks).
CC ?= cc
CFLAGS = -std=c11 -g -O1 -Wall -Wextra -fsanitize=address,undefined \
-D_GNU_SOURCE -DMEMFILE_DOUBLE_FREE_CHECK -I.
LIBS = -lm
OUT = /tmp/mf_test

.PHONY: test verify-detector test_memfile test_stringtable test_graph test_entity bench proofs proofs-eva clean

# `make test` = prove the detector fires, then run every harness with it active.
test: verify-detector test_memfile test_stringtable test_graph test_entity

verify-detector: test_doublefree.c memoryfile.c
@$(CC) $(CFLAGS) test_doublefree.c memoryfile.c $(LIBS) -o $(OUT)_df
@if $(OUT)_df 2>&1 | grep -q "DOUBLE-FREE detected"; then \
echo "ok: double-free detector fires (abort) on a deliberate double-free"; \
else echo "FAIL: double-free detector did NOT fire"; exit 1; fi

test_memfile: test_memfile.c memoryfile.c
$(CC) $(CFLAGS) $^ $(LIBS) -o $(OUT)_memfile && $(OUT)_memfile

test_stringtable: test_stringtable.c stringtable.c memoryfile.c
$(CC) $(CFLAGS) $^ $(LIBS) -o $(OUT)_st && $(OUT)_st

test_graph: test_graph.c graph.c stringtable.c memoryfile.c
$(CC) $(CFLAGS) $^ $(LIBS) -o $(OUT)_graph && $(OUT)_graph

test_entity: test_entity.c
$(CC) $(CFLAGS) $^ $(LIBS) -o $(OUT)_entity && $(OUT)_entity

# Per-op graph benchmark: optimized build (NO ASan / NO double-free-check — those
# skew timing). Emits per-op rdtsc cycle stats as JSON; CI compares base vs head.
BENCH_CFLAGS = -std=c11 -O2 -march=native -Wall -D_GNU_SOURCE -I.
bench: op_bench.c graph.c stringtable.c memoryfile.c
$(CC) $(BENCH_CFLAGS) $^ -lm -o $(OUT)_bench && $(OUT)_bench

# ---- Frama-C/WP + EVA proofs ----------------------------------------------
# Memory-model-clean abstractions in fc_*.c (NEVER compiled into the build):
# allocator size-quantization + open-addressing probe (fc_proofs), name-index
# traversal bounds (fc_index), free-list descent safety (fc_tree), adjacency
# edge encoding + edge-iteration bounds (fc_graph), and an EVA pass over the
# real allocator (fc_harness). frama-c + alt-ergo + z3 (CI installs via opam).
FRAMAC ?= frama-c

# frama-c exits 0 even with unproved goals, so the gate (scripts/run-proofs.sh)
# parses the "Proved goals: P / T" summary: clean files must be 100%, fc_tree is
# safety-only (termination = documented acyclicity frontier).
proofs:
@FRAMAC="$(FRAMAC)" bash ../scripts/run-proofs.sh

proofs-eva: fc_harness.c memoryfile.c
$(FRAMAC) -eva -main fc_main -cpp-extra-args="-include fc_stubs.h -I." $^

clean:
rm -f $(OUT)_*
6 changes: 6 additions & 0 deletions native/bench/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Compiled allocator bench/test binaries (build via this dir's Makefile).
bench_cart
bench_radix
bench_radix_asan
test_cart
test_coalesce
Loading
Loading