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
4796a7f
docs(1013): research prebuilt MEX binary shipping phase
HanSur94 Apr 22, 2026
066a82a
feat(1013-02): narrow .gitignore MEX exclusions with allow-list for s…
HanSur94 Apr 22, 2026
70479cb
test(1013-01): add mex_stamp helper + TestMexPrebuilt scaffolding (RED)
HanSur94 Apr 22, 2026
9b58b08
docs(1013-02): complete gitignore MEX allow-list plan
HanSur94 Apr 22, 2026
aa4761d
feat(1013-01): rewire needs_build with stamp check + expose test shim
HanSur94 Apr 22, 2026
7cbd3c2
docs(1013-01): complete stamp-check plan summary and state updates
HanSur94 Apr 22, 2026
e7fc529
test(1013-03): add failing Octave subdir probe test (RED)
HanSur94 Apr 22, 2026
a683559
feat(1013-03): Octave platform addpath + subdir probe in needs_build …
HanSur94 Apr 22, 2026
531766c
feat(1013-03): redirect Octave build outputs into platform-tagged subdir
HanSur94 Apr 22, 2026
1fc56ad
docs(1013-03): complete Octave subdir layout plan summary and state u…
HanSur94 Apr 22, 2026
7d8f1ba
feat(1013-04): ship macOS ARM64 prebuilt MEX binaries + stamp
HanSur94 Apr 23, 2026
4365f19
docs(1013-04): complete macOS ARM64 prebuilt MEX ship plan
HanSur94 Apr 23, 2026
2348911
chore(1013-06): update _build-mex-octave.yml for subdir layout + stam…
HanSur94 Apr 23, 2026
0e3495d
chore(1013-06): tests.yml cache key + smoke-test job comments
HanSur94 Apr 23, 2026
ac47f47
feat(1013-06): ship committed MEX binaries in release tarball
HanSur94 Apr 23, 2026
999ef5d
feat(1013-05): add refresh-mex-binaries workflow with 7-platform matr…
HanSur94 Apr 23, 2026
39ffdcd
docs(1013-06): complete rewire existing CI workflows plan
HanSur94 Apr 23, 2026
7eb6982
docs(1013-05): complete refresh-mex-binaries plan summary and roadmap…
HanSur94 Apr 23, 2026
18f9522
docs(1013): add gap closure plan 07 (mex_stamp public scope + test de…
HanSur94 Apr 23, 2026
87b4956
test(1013-07): require committed sentinel + subdir-aware resolver (RED)
HanSur94 Apr 23, 2026
ef0e08c
fix(1013-07): move mex_stamp.m to public scope so install.m can reach…
HanSur94 Apr 23, 2026
d9eea5a
docs(1013-07): document build_mex mtime guard as backstop to stamp gate
HanSur94 Apr 23, 2026
b34877a
docs(1013-07): complete gap-closure plan summary and state updates
HanSur94 Apr 23, 2026
d1a1c67
test(1013): persist human verification items as UAT
HanSur94 Apr 23, 2026
d88e9fe
docs(phase-1013): complete phase + evolve PROJECT.md
HanSur94 Apr 23, 2026
0007b9e
chore: complete v2.0 Tag-Based Domain Model milestone
HanSur94 Apr 23, 2026
1d65e48
ci(mex): trigger refresh workflow when mex_stamp.m formula changes
HanSur94 Apr 23, 2026
b335dde
docs(state): backfill commit hash for quick-260423-s4s
HanSur94 Apr 23, 2026
573d560
Merge origin/main into claude/heuristic-greider-5b1776
HanSur94 Apr 23, 2026
aca052b
fix(1013-07): SKIP stamp fast-path tests on platforms without committ…
HanSur94 Apr 23, 2026
b0f4a8b
style(1013-07): wrap SKIP messages to satisfy 160-char line limit
HanSur94 Apr 23, 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
8 changes: 7 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@
"Skill(gsd:quick)",
"Skill(gsd:debug)",
"Skill(gsd:research-phase)",
"WebSearch"
"WebSearch",
"Skill(gsd-add-phase)",
"Skill(gsd-add-phase:*)",
"Skill(gsd-discuss-phase)",
"Skill(gsd-discuss-phase:*)",
"Skill(gsd-quick)",
"Skill(gsd-quick:*)"
]
}
}
8 changes: 7 additions & 1 deletion .github/workflows/_build-mex-octave.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ jobs:
with:
path: |
libs/FastSense/private/*.mex
libs/FastSense/private/octave-linux-x86_64/*.mex
libs/FastSense/octave-linux-x86_64/*.mex
libs/SensorThreshold/private/*.mex
libs/SensorThreshold/private/octave-linux-x86_64/*.mex
libs/FastSense/mksqlite.mex
key: mex-linux-${{ hashFiles('libs/FastSense/private/mex_src/**', 'libs/FastSense/build_mex.m') }}
key: mex-linux-${{ hashFiles('libs/FastSense/private/mex_src/**', 'libs/FastSense/build_mex.m', 'libs/FastSense/private/.mex-version') }}

- name: Compile MEX files
if: steps.cache-mex.outputs.cache-hit != 'true'
Expand All @@ -38,6 +41,9 @@ jobs:
name: ${{ inputs.artifact-name }}
path: |
libs/FastSense/private/*.mex
libs/FastSense/private/octave-linux-x86_64/*.mex
libs/FastSense/octave-linux-x86_64/*.mex
libs/SensorThreshold/private/*.mex
libs/SensorThreshold/private/octave-linux-x86_64/*.mex
libs/FastSense/mksqlite.mex
retention-days: 1
320 changes: 320 additions & 0 deletions .github/workflows/refresh-mex-binaries.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
name: Refresh MEX Binaries

# Builds the full 7-way platform x runtime matrix (4 MATLAB + 3 Octave) on
# every push to main that touches MEX sources, and on manual dispatch. The
# aggregator job collects all 7 artifacts, regenerates the .mex-version
# stamp, and opens/updates a PR on `chore/refresh-mex-binaries` refreshing
# the committed binaries.
#
# Non-retrigger invariant:
# The auto-PR only touches binaries + .mex-version. Neither of those are
# in `paths`, so merging the PR does not loop. Belt-and-braces: each job
# ignores github-actions[bot] as actor.
#
# Source of truth for the stamp formula is libs/FastSense/private/mex_stamp.m.
# The aggregator mirrors its concatenation ordering: sorted *.c files in
# mex_src/, then sorted *.h files in mex_src/, then build_mex.m, then
# mksqlite.c.

on:
push:
branches: [main]
paths:
- 'libs/FastSense/private/mex_src/**'
- 'libs/FastSense/build_mex.m'
- 'libs/FastSense/mksqlite.c'
# Defensive: refresh if the stamp formula itself changes. The formula
# is reimplemented in bash inside the aggregator job; if mex_stamp.m
# and the aggregator drift, stamps mismatch and binaries rebuild
# without the committed stamp updating -- infinite rebuild loop.
- 'libs/FastSense/mex_stamp.m'
workflow_dispatch:

concurrency:
group: refresh-mex-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: write
pull-requests: write

jobs:
build-matlab:
name: MATLAB MEX ${{ matrix.label }}
if: github.actor != 'github-actions[bot]'
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- label: macos-arm64
os: macos-14
release: R2023b
mexext: mexmaca64
- label: macos-x86_64
os: macos-15-intel
release: R2023b
mexext: mexmaci64
- label: linux-x86_64
os: ubuntu-22.04
release: R2020b
mexext: mexa64
- label: windows-x86_64
os: windows-latest
release: R2020b
mexext: mexw64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6

- uses: matlab-actions/setup-matlab@v3
with:
release: ${{ matrix.release }}
cache: true

- name: Delete stale binaries for this platform
shell: bash
run: |
find libs -type f -name "*.${{ matrix.mexext }}" -delete || true

- name: Verify mexext on runner
uses: matlab-actions/run-command@v3
with:
command: "assert(strcmp(mexext(), '${{ matrix.mexext }}'), sprintf('Expected %s, got %s', '${{ matrix.mexext }}', mexext()));"

- name: Compile via install()
uses: matlab-actions/run-command@v3
with:
command: "install();"

- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: mex-matlab-${{ matrix.label }}
path: |
libs/FastSense/private/*.${{ matrix.mexext }}
libs/FastSense/mksqlite.${{ matrix.mexext }}
libs/SensorThreshold/private/*.${{ matrix.mexext }}
if-no-files-found: error
retention-days: 3

build-octave-linux:
name: Octave MEX linux-x86_64
if: github.actor != 'github-actions[bot]'
timeout-minutes: 30
runs-on: ubuntu-22.04
container: gnuoctave/octave:11.1.0
steps:
- uses: actions/checkout@v6

- name: Delete stale Octave binaries
run: find libs -type f -name '*.mex' -delete || true

- name: Compile via install()
run: octave --eval "install();"

- name: Verify subdir output
run: |
test -f libs/FastSense/private/octave-linux-x86_64/binary_search_mex.mex
test -f libs/FastSense/octave-linux-x86_64/mksqlite.mex

- uses: actions/upload-artifact@v7
with:
name: mex-octave-linux-x86_64
path: |
libs/FastSense/private/octave-linux-x86_64/*.mex
libs/FastSense/octave-linux-x86_64/*.mex
libs/SensorThreshold/private/octave-linux-x86_64/*.mex
if-no-files-found: error
retention-days: 3

build-octave-macos:
name: Octave MEX macos-arm64
if: github.actor != 'github-actions[bot]'
timeout-minutes: 30
runs-on: macos-14
steps:
- uses: actions/checkout@v6

- name: Install Octave
run: brew install octave

- name: Delete stale Octave binaries
run: find libs -type f -name '*.mex' -delete || true

- name: Compile via install()
run: octave --eval "install();"

- name: Verify subdir output
run: |
test -f libs/FastSense/private/octave-macos-arm64/binary_search_mex.mex
test -f libs/FastSense/octave-macos-arm64/mksqlite.mex

- uses: actions/upload-artifact@v7
with:
name: mex-octave-macos-arm64
path: |
libs/FastSense/private/octave-macos-arm64/*.mex
libs/FastSense/octave-macos-arm64/*.mex
libs/SensorThreshold/private/octave-macos-arm64/*.mex
if-no-files-found: error
retention-days: 3

build-octave-windows:
name: Octave MEX windows-x86_64
if: github.actor != 'github-actions[bot]'
timeout-minutes: 45
runs-on: windows-latest
steps:
- name: Allow paths with colons (wiki files)
run: git config --global core.protectNTFS false

- uses: actions/checkout@v6
with:
sparse-checkout: |
libs
install.m
sparse-checkout-cone-mode: false

- name: Install Octave
shell: pwsh
run: |
# Try Chocolatey first
choco install octave.portable --yes --version=9.2.0 --no-progress 2>$null
$octExe = Get-ChildItem -Path "C:\ProgramData\chocolatey\lib\octave.portable\tools" -Recurse -Filter "octave-cli.exe" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($octExe) {
Write-Host "Octave installed via Chocolatey at: $($octExe.FullName)"
echo "OCTAVE_EXE=$($octExe.FullName)" >> $env:GITHUB_ENV
exit 0
}
# Fallback: direct download from mirror
$LASTEXITCODE = 0
$global:LASTEXITCODE = 0
Write-Host "::warning::Chocolatey install failed (ftp.gnu.org may be down). Downloading from mirror..."
$url = "https://mirrors.kernel.org/gnu/octave/windows/octave-9.2.0-w64.zip"
$zip = "$env:TEMP\octave.zip"
$dest = "C:\octave"
Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing
Expand-Archive -Path $zip -DestinationPath $dest -Force
$octExe = Get-ChildItem -Path $dest -Recurse -Filter "octave-cli.exe" | Select-Object -First 1
if (-not $octExe) {
Write-Error "Failed to find octave-cli.exe after direct download"
exit 1
}
Write-Host "Octave installed via direct download at: $($octExe.FullName)"
echo "OCTAVE_EXE=$($octExe.FullName)" >> $env:GITHUB_ENV
timeout-minutes: 15

- name: Delete stale Octave binaries
shell: pwsh
run: Get-ChildItem -Recurse libs -Filter '*.mex' -ErrorAction SilentlyContinue | Remove-Item -Force

- name: Compile via install()
shell: pwsh
run: |
$octExe = $env:OCTAVE_EXE
if (-not $octExe -or -not (Test-Path $octExe)) {
Write-Error "OCTAVE_EXE not set or not found: $octExe"
exit 1
}
& $octExe --no-window-system --eval "install();"

- name: Verify subdir output
shell: pwsh
run: |
if (-not (Test-Path 'libs/FastSense/private/octave-windows-x86_64/binary_search_mex.mex')) { throw "kernel missing under libs/FastSense/private/octave-windows-x86_64/" }
if (-not (Test-Path 'libs/FastSense/octave-windows-x86_64/mksqlite.mex')) { throw "mksqlite missing under libs/FastSense/octave-windows-x86_64/" }

- uses: actions/upload-artifact@v7
with:
name: mex-octave-windows-x86_64
path: |
libs/FastSense/private/octave-windows-x86_64/*.mex
libs/FastSense/octave-windows-x86_64/*.mex
libs/SensorThreshold/private/octave-windows-x86_64/*.mex
if-no-files-found: error
retention-days: 3

open-refresh-pr:
name: Aggregate + Open PR
needs:
- build-matlab
- build-octave-linux
- build-octave-macos
- build-octave-windows
if: always() && !cancelled() && github.actor != 'github-actions[bot]'
runs-on: ubuntu-22.04
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v6

# Download ALL artifacts into the workspace; merge-multiple preserves
# the repo-relative paths each artifact was uploaded with, dropping
# them back into libs/.../{,octave-<tag>/}.
- uses: actions/download-artifact@v8
with:
pattern: mex-*
merge-multiple: true

- name: Regenerate .mex-version stamp
shell: bash
run: |
# Mirror libs/FastSense/private/mex_stamp.m: sorted *.c then sorted
# *.h from mex_src (non-recursive), then build_mex.m, then mksqlite.c.
set -euo pipefail
src_dir="libs/FastSense/private/mex_src"
mapfile -t c_files < <(find "$src_dir" -maxdepth 1 -name '*.c' | LC_ALL=C sort)
mapfile -t h_files < <(find "$src_dir" -maxdepth 1 -name '*.h' | LC_ALL=C sort)
tmp="$(mktemp)"
for f in "${c_files[@]}" "${h_files[@]}" libs/FastSense/build_mex.m libs/FastSense/mksqlite.c; do
cat "$f" >> "$tmp"
done
hex="$(sha256sum "$tmp" | awk '{print $1}')"
rm -f "$tmp"
printf 'sha256:%s\n' "$hex" > libs/FastSense/private/.mex-version
echo "Stamp:"
cat libs/FastSense/private/.mex-version

- name: Show refreshed tree
run: |
find libs -type f \( \
-name '*.mexmaca64' -o -name '*.mexmaci64' -o \
-name '*.mexa64' -o -name '*.mexw64' -o \
-name '*.mex' \
\) | sort
echo "--- stamp ---"
cat libs/FastSense/private/.mex-version

- uses: peter-evans/create-pull-request@v7
with:
branch: chore/refresh-mex-binaries
delete-branch: true
commit-message: "chore(mex): refresh prebuilt MEX binaries"
title: "chore: refresh prebuilt MEX binaries"
body: |
Auto-generated by `.github/workflows/refresh-mex-binaries.yml`.

Regenerates committed MEX binaries for all 7 platform x runtime
combinations and updates `libs/FastSense/private/.mex-version`.

Merging this PR does NOT retrigger the workflow (binaries and
the stamp are excluded from the workflow's `paths` filter).
add-paths: |
libs/FastSense/private/*.mexmaca64
libs/FastSense/private/*.mexmaci64
libs/FastSense/private/*.mexa64
libs/FastSense/private/*.mexw64
libs/FastSense/mksqlite.mexmaca64
libs/FastSense/mksqlite.mexmaci64
libs/FastSense/mksqlite.mexa64
libs/FastSense/mksqlite.mexw64
libs/SensorThreshold/private/*.mexmaca64
libs/SensorThreshold/private/*.mexmaci64
libs/SensorThreshold/private/*.mexa64
libs/SensorThreshold/private/*.mexw64
libs/FastSense/private/octave-*/**
libs/FastSense/octave-*/**
libs/SensorThreshold/private/octave-*/**
libs/FastSense/private/.mex-version
10 changes: 4 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,10 @@ jobs:
cp install.m LICENSE README.md CITATION.cff "${DIRNAME}/"
cp -r examples "${DIRNAME}/"

# Copy libs, excluding compiled MEX binaries
# Copy libs including committed prebuilt MEX binaries.
# Binaries are refreshed by .github/workflows/refresh-mex-binaries.yml
# and shipped in the release archive so end users skip compilation.
cp -r libs "${DIRNAME}/"
find "${DIRNAME}/libs" -type f \( \
-name "*.mexmaca64" -o -name "*.mexmaci64" \
-o -name "*.mexa64" -o -name "*.mexw64" \
-o -name "*.mex" \) -delete

# Create archives
tar czf "${DIRNAME}.tar.gz" "${DIRNAME}"
Expand All @@ -81,7 +79,7 @@ jobs:

## Installation

Download the archive, extract it, and run `install` in MATLAB/Octave to add libraries to path and compile MEX accelerators.
Download the archive, extract it, and run `install` in MATLAB/Octave. Prebuilt MEX binaries are bundled — compilation only happens on unsupported platforms.
files: |
FastSense-${{ steps.version.outputs.VERSION }}.tar.gz
FastSense-${{ steps.version.outputs.VERSION }}.zip
Loading
Loading