Skip to content

Commit 1656097

Browse files
port(ts->affine): unit/evidence_graph_test → AffineScript (STEP 4-C SEED, Refs standards#239 / #243) (#91)
Estate-wide TypeScript→AffineScript migration campaign (`hyperpolymath/standards#239`) — **first STEP 4-C per-repo port** (the seed for tail batch 2, `standards#243`). Mirrors the STEP-2 pattern shipped in session-sentinel#25 / tropical-resource-typing#15 / nafa-app#23 / phronesis#19. ## What lands - `tests/unit/evidence_graph_test.ts` (313L, 5 `Deno.test` blocks with nested `t.step` subtests) → `tests/unit/evidence_graph_test.affine` (333L AS source) + `tests/unit/evidence_graph_test.deno.js` (compiled Deno-ESM artefact). - `deno.json` `test:unit` task now drives the compiled `.deno.js`; legacy `tests/unit/` runner kept as `test:unit-legacy` for any iteration still in progress. ## Adaptations from the .ts source ### Test driver shape Each `await t.step("…", () => { … })` flattens into a `pub fn test_<name>() -> Bool` returning true/false with a `FAIL: <reason>` line via `consoleError`. `main()` drives the 25-test list with a `run_one` reporter and exits non-zero on any failure. Replaces `Deno.test` discovery (gap 1 in standards#242 / deferred — panic-on-fail driver works for STEP-2/4-C scope). ### TS interfaces → AS record types ```as pub type Node = { id: String, label: String, node_type: String, prompt_score: Float } pub type Link = { source: String, target: String, relationship: String } pub type GraphData = { nodes: [Node], links: [Link] } ``` The TS source's `"claim" | "evidence" | string` union for `nodeType` collapses to plain `String` (the union already allowed any string). ### TS throws → AS `Option<T>` The Deno-ESM backend has no user-throwable exception surface, so `create_node` / `create_link` `throw new Error(...)` on bad inputs becomes `Option<Node>` / `Option<Link>` with `None` for rejected inputs. Negative tests check for `None` via `check_none_node` / `check_none_link`; the contract — "the factory rejects bad input" — is preserved. ### Null-handling tests adaptation The TS source's `null as unknown as string` casts are vacuous in AffineScript (statically typed; no nullable strings). The representative shape (empty-string inputs as a stand-in for "ill-formed input") is retained as the AS equivalent, since that's what those tests ultimately verified. ## Verification ``` $ node tests/unit/evidence_graph_test.deno.js ok node: create claim node ok node: create evidence node ok node: custom type ok node: reject empty id ok node: reject empty label ok node: reject negative prompt_score ok node: reject prompt_score > 100 ok node: boundary prompt_score 0 ok node: boundary prompt_score 100 ok link: supports ok link: contradicts ok link: contextualizes ok link: custom relationship ok link: reject empty source ok link: reject empty target ok link: reject empty relationship ok graph: empty ok graph: with nodes ok graph: with links ok graph: complex ok null: empty id rejected ok null: empty label rejected ok null: empty arrays graph ok access: all node fields ok access: all link fields OK: all tests passed (25/25) ``` ## Out of scope for this PR Other bofig TS test files (the remaining 4 of the 5 files under `tests/`): - **`tests/property/graph_properties_test.ts`** (377L) — needs `Math.random` binding, filed as `standards#327` / `affinescript#509` (auto-merge armed). - **`tests/bench/graph_bench.ts`** (252L) — same `Math.random` gating + `Deno.bench` differs from `Deno.test`. - **`tests/e2e/graph_lifecycle_test.ts`** (445L) and **`tests/aspect/security_test.ts`** (459L) — independent ports; each will be a follow-up STEP 4-C tail PR in this repo. ## Refs - `hyperpolymath/standards#239` (umbrella) / `#243` (STEP 4 tail batch 2 — this issue) - `hyperpolymath/standards#242` (STEP 3 predecessor) - Sibling stdlib PRs (auto-merge armed): `affinescript#504` (STEP 3), `affinescript#507` (STEP 4-A binary I/O), `affinescript#509` (STEP 4-B random + perf.now) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 51c38dd commit 1656097

44 files changed

Lines changed: 2631 additions & 2108 deletions

Some content is hidden

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

.github/workflows/boj-build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77
jobs:
88
trigger-boj:
99
runs-on: ubuntu-latest
10+
timeout-minutes: 5
1011
steps:
1112
- name: Checkout
1213
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

.github/workflows/casket-pages.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ concurrency:
1818
jobs:
1919
build:
2020
runs-on: ubuntu-latest
21+
timeout-minutes: 45
2122
steps:
2223
- name: Checkout
2324
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
@@ -109,6 +110,7 @@ jobs:
109110
name: github-pages
110111
url: ${{ steps.deployment.outputs.page_url }}
111112
runs-on: ubuntu-latest
113+
timeout-minutes: 10
112114
needs: build
113115
steps:
114116
- name: Deploy to GitHub Pages

.github/workflows/codeql.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ permissions:
2323
jobs:
2424
analyze:
2525
runs-on: ubuntu-latest
26+
timeout-minutes: 30
2627
permissions:
2728
contents: read
2829
security-events: write
@@ -32,6 +33,8 @@ jobs:
3233
include:
3334
- language: javascript-typescript
3435
build-mode: none
36+
- language: actions
37+
build-mode: none
3538

3639
steps:
3740
- name: Checkout

.github/workflows/dogfood-gate.yml

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ jobs:
2222
a2ml-validate:
2323
name: Validate A2ML manifests
2424
runs-on: ubuntu-latest
25+
timeout-minutes: 15
2526

2627
steps:
2728
- name: Checkout repository
@@ -66,6 +67,7 @@ jobs:
6667
k9-validate:
6768
name: Validate K9 contracts
6869
runs-on: ubuntu-latest
70+
timeout-minutes: 15
6971

7072
steps:
7173
- name: Checkout repository
@@ -115,6 +117,7 @@ jobs:
115117
empty-lint:
116118
name: Empty-linter (invisible characters)
117119
runs-on: ubuntu-latest
120+
timeout-minutes: 10
118121

119122
steps:
120123
- name: Checkout repository
@@ -179,6 +182,7 @@ jobs:
179182
groove-check:
180183
name: Groove manifest check
181184
runs-on: ubuntu-latest
185+
timeout-minutes: 10
182186

183187
steps:
184188
- name: Checkout repository
@@ -237,6 +241,7 @@ jobs:
237241
eclexiaiser-validate:
238242
name: Validate eclexiaiser manifest
239243
runs-on: ubuntu-latest
244+
timeout-minutes: 10
240245

241246
steps:
242247
- name: Checkout repository
@@ -258,26 +263,26 @@ jobs:
258263
259264
# Validate TOML structure using Python 3.11+ tomllib
260265
python3 -c "
261-
import tomllib, sys
262-
with open('eclexiaiser.toml', 'rb') as f:
263-
data = tomllib.load(f)
264-
project = data.get('project', {})
265-
if not project.get('name', '').strip():
266-
print('ERROR: project.name is required', file=sys.stderr)
267-
sys.exit(1)
268-
functions = data.get('functions', [])
269-
if not functions:
270-
print('ERROR: at least one [[functions]] entry is required', file=sys.stderr)
271-
sys.exit(1)
272-
for fn in functions:
273-
if not fn.get('name', '').strip():
274-
print('ERROR: function name cannot be empty', file=sys.stderr)
275-
sys.exit(1)
276-
if not fn.get('source', '').strip():
277-
print(f'ERROR: function {fn[\"name\"]} has no source path', file=sys.stderr)
278-
sys.exit(1)
279-
print(f'Valid: {project[\"name\"]} ({len(functions)} function(s))')
280-
" || {
266+
import tomllib, sys
267+
with open('eclexiaiser.toml', 'rb') as f:
268+
data = tomllib.load(f)
269+
project = data.get('project', {})
270+
if not project.get('name', '').strip():
271+
print('ERROR: project.name is required', file=sys.stderr)
272+
sys.exit(1)
273+
functions = data.get('functions', [])
274+
if not functions:
275+
print('ERROR: at least one [[functions]] entry is required', file=sys.stderr)
276+
sys.exit(1)
277+
for fn in functions:
278+
if not fn.get('name', '').strip():
279+
print('ERROR: function name cannot be empty', file=sys.stderr)
280+
sys.exit(1)
281+
if not fn.get('source', '').strip():
282+
print(f'ERROR: function {fn[\"name\"]} has no source path', file=sys.stderr)
283+
sys.exit(1)
284+
print(f'Valid: {project[\"name\"]} ({len(functions)} function(s))')
285+
" || {
281286
echo "::error file=eclexiaiser.toml::Invalid eclexiaiser.toml — see step output for details"
282287
exit 1
283288
}
@@ -300,6 +305,7 @@ print(f'Valid: {project[\"name\"]} ({len(functions)} function(s))')
300305
dogfood-summary:
301306
name: Dogfooding compliance summary
302307
runs-on: ubuntu-latest
308+
timeout-minutes: 10
303309
needs: [a2ml-validate, k9-validate, empty-lint, groove-check, eclexiaiser-validate]
304310
if: always()
305311

@@ -378,4 +384,3 @@ print(f'Valid: {project[\"name\"]} ({len(functions)} function(s))')
378384
*Generated by the [Dogfood Gate](https://github.com/hyperpolymath/rsr-template-repo) workflow.*
379385
*Dogfooding is guinea pig fooding — we test our tools on ourselves.*
380386
EOF
381-

.github/workflows/elixir-ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ jobs:
1212
elixir-ci:
1313
uses: hyperpolymath/standards/.github/workflows/elixir-ci-reusable.yml@4fdf4314b4ab54269adbaff10e30e483b5e86845
1414
with:
15-
otp-version: "26"
16-
elixir-version: "1.15"
15+
otp-version: "27.2.1"
16+
elixir-version: "1.18.2"

.github/workflows/elixir.yml

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,40 @@ jobs:
2020

2121
name: Build and test
2222
runs-on: ubuntu-latest
23+
timeout-minutes: 30
24+
env:
25+
MIX_ENV: test
26+
POSTGRES_PASSWORD: ${{ github.run_id }}
27+
ARANGO_USERNAME: root
28+
ARANGO_PASSWORD: ${{ github.run_id }}
29+
30+
services:
31+
postgres:
32+
image: postgres:16-alpine@sha256:79950da386bda7fcc9d57aa9aa9be6c6d7407596a9b8f68014b09a778a9ab316
33+
env:
34+
POSTGRES_PASSWORD: ${{ github.run_id }}
35+
ports:
36+
- 5432:5432
37+
options: >-
38+
--health-cmd "pg_isready -U postgres -d postgres"
39+
--health-interval 10s
40+
--health-timeout 5s
41+
--health-retries 5
42+
43+
arangodb:
44+
image: arangodb:3.12@sha256:95567b13d74dbec3869468f88c31a53ae598fa5f90e0e2e582df89b7b6893ae7
45+
env:
46+
ARANGO_ROOT_PASSWORD: ${{ github.run_id }}
47+
ports:
48+
- 8529:8529
2349

2450
steps:
2551
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
2652
- name: Set up Elixir
2753
uses: erlef/setup-beam@e6d7c94229049569db56a7ad5a540c051a010af9 # v1.20.4
2854
with:
29-
elixir-version: '1.15.2' # [Required] Define the Elixir version
30-
otp-version: '26.0' # [Required] Define the Erlang/OTP version
55+
elixir-version: '1.18.2'
56+
otp-version: '27.2.1'
3157
- name: Restore dependencies cache
3258
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
3359
with:
@@ -36,5 +62,15 @@ jobs:
3662
restore-keys: ${{ runner.os }}-mix-
3763
- name: Install dependencies
3864
run: mix deps.get
65+
- name: Wait for ArangoDB
66+
run: |
67+
for attempt in $(seq 1 30); do
68+
if curl -fsS -u "root:${ARANGO_PASSWORD}" http://localhost:8529/_api/version >/dev/null; then
69+
exit 0
70+
fi
71+
sleep 2
72+
done
73+
echo "::error::ArangoDB did not become ready"
74+
exit 1
3975
- name: Run tests
4076
run: mix test

.github/workflows/generator-generic-ossf-slsa3-publish.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ on:
2323
jobs:
2424
build:
2525
runs-on: ubuntu-latest
26+
timeout-minutes: 10
2627
outputs:
2728
digests: ${{ steps.hash.outputs.digests }}
2829

.github/workflows/governance.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ permissions:
3131

3232
jobs:
3333
governance:
34-
uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@main
34+
uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@861b5e911d9e5dcfb3c0ab3dd2a9a3c8fd0a1613

.github/workflows/hypatia-scan.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ jobs:
4343
scan:
4444
name: Hypatia Neurosymbolic Analysis
4545
runs-on: ubuntu-latest
46+
timeout-minutes: 30
4647

4748
steps:
4849
- name: Checkout repository
@@ -53,8 +54,8 @@ jobs:
5354
- name: Setup Elixir for Hypatia scanner
5455
uses: erlef/setup-beam@fc68ffb90438ef2936bbb3251622353b3dcb2f93 # v1.18.2
5556
with:
56-
elixir-version: '1.18'
57-
otp-version: '27'
57+
elixir-version: '1.18.2'
58+
otp-version: '27.2.1'
5859

5960
- name: Clone Hypatia
6061
run: |
@@ -411,4 +412,4 @@ jobs:
411412
repo: context.repo.repo,
412413
issue_number: context.issue.number,
413414
body: comment
414-
});
415+
});

.github/workflows/instant-sync.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,22 @@ permissions:
1414
jobs:
1515
dispatch:
1616
runs-on: ubuntu-latest
17+
timeout-minutes: 5
1718
steps:
19+
- name: Check dispatch token
20+
id: dispatch-token
21+
env:
22+
FARM_DISPATCH_TOKEN: ${{ secrets.FARM_DISPATCH_TOKEN }}
23+
run: |
24+
if [ -n "$FARM_DISPATCH_TOKEN" ]; then
25+
echo "present=true" >> "$GITHUB_OUTPUT"
26+
else
27+
echo "present=false" >> "$GITHUB_OUTPUT"
28+
echo "::notice::FARM_DISPATCH_TOKEN is not configured; skipping propagation dispatch."
29+
fi
30+
1831
- name: Trigger Propagation
32+
if: steps.dispatch-token.outputs.present == 'true'
1933
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v3
2034
with:
2135
token: ${{ secrets.FARM_DISPATCH_TOKEN }}
@@ -30,4 +44,5 @@ jobs:
3044
}
3145
3246
- name: Confirm
47+
if: steps.dispatch-token.outputs.present == 'true'
3348
run: echo "::notice::Propagation triggered for ${{ github.event.repository.name }}"

0 commit comments

Comments
 (0)