Skip to content

Commit 1a73433

Browse files
docs: state write-up for humans and machines; make the golden path real (#148)
Closes out the my-lang session by recording current state in-repo (not only in dev-notes) and fixing the documentation defects found while doing it. ## The Justfile was parse-dead `README.md` documented `just build` / `just test` as the quickstart. Neither worked: a `//` C-style comment on line 2 made `just` reject the **entire file** (`error: unknown start of token '.'`), so no recipe was reachable — and every recipe was an `@echo` stub regardless. `just verify` checked `.machine_read/` (a path that has never existed) and swallowed every failure with `||`, so it always reported success. Now: real cargo recipes, `-all` variants for the LLVM-dependent crate, and a `verify` that exits non-zero. **Verified by running them** — `just build` exits 0; `just test` reports **221 passing, 0 failing**. *(Estate note: 143 of 751 Justfiles across the estate are parse-dead by the same class of defect — 76× unindented shell blocks, 35× `//` comments, 32× `Justfile`/`justfile` case collisions. Out of scope here; recorded in dev-notes.)* ## For machines — `.machine_readable/6a2/STATE.a2ml` - version `0.1.0` → `0.2.0` (was drifting from `Cargo.toml`) - new `[security-posture]`: distinguishes **expiring baseline debt** (7 entries → 2026-10-27, #145) from **permanent scoped exemptions** (3, each a rule-precision problem), plus upstream rule-fix candidates - new `[toolchain]`; `known-local-limitation` records that `my-llvm` needs system LLVM 21, so a bare workspace build failure is not misread as a regression - dialect model corrected: **solo ⊂ duet ⊂ ensemble**, with `me` an outside projector — not four co-equal surfaces - `superseded-branch` records the evidence that `feat/stdlib-fs-env-format` is safe to delete ## For humans — README + CHANGELOG Quickstart matches reality (incl. the my-llvm caveat); dialect containment; measured test count (137+ → 221); governance/security posture; `contractiles/` path corrected (it is under `.machine_readable/`, not the repo root). CHANGELOG gains Security/Fixed/Changed entries for #143, #146, #147. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 8636e15 commit 1a73433

4 files changed

Lines changed: 202 additions & 43 deletions

File tree

.machine_readable/6a2/STATE.a2ml

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,34 @@
44
# STATE.a2ml — My Lang project state
55
[metadata]
66
project = "my-lang"
7-
version = "0.1.0"
8-
last-updated = "2026-06-14"
7+
version = "0.2.0" # matches Cargo.toml [workspace.package]
8+
last-updated = "2026-07-27"
99
status = "active"
10-
session = "Coq solo-core: R2–R5b (SEMIRING functor / affine_pres / Tropical / usage-walk checker / aff_type_dec) + M1 me→solo elaboration + structure climb S1.0–S3c.3-msg (session-π: subject reduction, fidelity, progress, duet-by-projection, n-party projection totality, static n-party config, label-union merge, union-projection, n-ary located operational semantics, head-coupled message subject reduction = first earned n-party safety half) + E4 residue-measure SEAM (Coq mirror) — all axiom-free, CI-gated — 2026-06-14"
10+
session = "CI/CD + supply-chain close-out (PRs #143/#146/#147): codeql-action repointed off a NONEXISTENT SHA; all 15 Hypatia findings triaged (8 fixed at source incl. a self-referential critical, 7 baselined to 2026-10-27 under issue #145); unsafe_block ruled unsatisfiable and scoped-exempted; GitHub Pages un-stuck (workflow was disabled_manually) and LIVE; ARCHITECTURE.md + Justfile de-boilerplated. main FULLY GREEN — 2026-07-27"
11+
prior-session = "Coq solo-core: R2–R5b (SEMIRING functor / affine_pres / Tropical / usage-walk checker / aff_type_dec) + M1 me→solo elaboration + structure climb S1.0–S3c.3-msg (session-π: subject reduction, fidelity, progress, duet-by-projection, n-party projection totality, static n-party config, label-union merge, union-projection, n-ary located operational semantics, head-coupled message subject reduction = first earned n-party safety half) + E4 residue-measure SEAM (Coq mirror) — all axiom-free, CI-gated — 2026-06-14"
1112

1213
[project-context]
1314
name = "My Lang"
1415
purpose = """
1516
Multi-dialect programming language with QTT (Quantitative Type Theory) semantic
16-
core. Four dialects: solo (affine, single-agent), duet (session-typed, two-party),
17-
ensemble (multi-agent), me (block/visual/pedagogic). Formal verification —
18-
mechanised Coq + Idris2 solo-core — is a first-class deliverable alongside the
19-
Rust compiler implementation.
17+
core. Formal verification — mechanised Coq + Idris2 solo-core — is a first-class
18+
deliverable alongside the Rust compiler implementation.
2019
"""
20+
# CORRECTION (2026-07-27): the dialects are NOT four co-equal surfaces. They are
21+
# three NESTED SUBSETS plus a projector that sits outside the hierarchy:
22+
# solo ⊂ duet ⊂ ensemble
23+
# solo = innermost kernel (affine, single-agent)
24+
# duet = solo + session-typed two-party (mechanised as ensemble|2-party by projection)
25+
# ensemble= duet + multi-agent (session-typed π-calculus core) = the full language
26+
# me = NOT a fourth dialect; an on-the-fly projector that cuts the hierarchy
27+
# at a chosen point. Its Rust scaffolding is SIDELINED in
28+
# _exploratory/me-scaffolding/ and is not in the Cargo workspace.
29+
# Consequence for audits: grade the layered stack, not three standalone compilers.
30+
# A broken solo breaks duet and ensemble by transitivity.
31+
dialect-model = "nested-subsets"
32+
dialects-ordered = ["solo", "duet", "ensemble"]
33+
dialect-projector = "me"
34+
architecture-doc = "ARCHITECTURE.md"
2135
completion-percentage = 50
2236

2337
[position]
@@ -88,8 +102,10 @@ issues = [
88102
{ id = "product-elim", description = "RESOLVED (F1.4): additive & (projections) + multiplicative ⊗ (let-pair elim) both coherent; preservation proved sound over the open-term substitution lemma ht_subst.", severity = "resolved" },
89103
{ id = "typeck", description = "Solo dialect affine type-checker is TODO(#typeck) — mechanised solo-core (check / check_correct, R5) leads the implementation and is its spec; a hand-written Rust checker is tested against, not Coq-proved equal to, check", severity = "high" },
90104
{ id = "proof-ci", description = "RESOLVED (F5): .github/workflows/proofs.yml compiles the Coq solo-core + idris2 --build and asserts Print Assumptions closed per rung", severity = "resolved" },
91-
{ id = "hypatia-reusable-timeout", description = "UPSTREAM/STANDARDS: Hypatia workflow_audit/missing_timeout_minutes fires on reusable-workflow CALLER jobs (governance/hypatia-scan/mirror/scorecard/secret-scanner/spark-theatre-gate all `uses:` hyperpolymath/standards). timeout-minutes is invalid on `uses:` jobs — real fix belongs in the standards reusable workflows + Hypatia should exempt caller jobs. Not fixable in my-lang.", severity = "medium" },
92-
{ id = "expect-hot-path", description = "Hypatia code_safety/expect_in_hot_path: parser.rs (157), lib/common/string.rs (1), examples/measure_depth.rs (2). CWE-754 — replace expect() with recoverable errors in shipped hot paths.", severity = "medium" },
105+
{ id = "hypatia-reusable-timeout", description = "UPSTREAM/STANDARDS (issue #95): Hypatia workflow_audit/missing_timeout_minutes fired on reusable-workflow CALLER jobs. MEASURED 2026-07-27: no longer reproduces — the current governance scan returns zero workflow_audit findings (PR #134 dropped the invalid timeout-minutes keys that were making those files parse-dead; wrappers are SHA-pinned). Kept open as the upstream tracker only.", severity = "low" },
106+
{ id = "expect-hot-path", description = "SUPERSEDED by panic-surface (below). The parser.rs x157 count was a SCANNER FALSE POSITIVE — a name collision with the parser's own `self.expect(TokenKind) -> Result` method, not Option/Result::expect panics. Issue #94 closed as consolidated into #145.", severity = "resolved" },
107+
{ id = "panic-surface", description = "Hypatia code_safety unwrap/expect debt, BASELINED not fixed (7 entries in .hypatia-baseline.json, expire 2026-10-27, tracked in issue #145): my-fmt x26 unwrap (infallible fmt::Write-to-String), my-llvm x3 unwrap (internal MIR invariants), my-mir x5 + string.rs x1 + measure_depth.rs x2 expect (deliberate invariant style), parser.rs x157 (scanner FP), modules.md SD022 (illustrative user-project layout). Revisit before the expiry or the gate re-reds.", severity = "medium", tracking = "hyperpolymath/my-lang#145" },
108+
{ id = "justfile-parse-dead", description = "RESOLVED 2026-07-27: the Justfile carried a `// Owner:` C-style comment on line 2, so `just` refused the WHOLE file (`error: unknown start of token '.'`) and every recipe was unavailable — while README documented `just build` / `just test` as the golden path. Recipes were also `@echo` stubs. Now real cargo commands, parse-verified. ESTATE-WIDE: 143 of 751 Justfiles measured parse-dead (76x unindented shell `-`, 35x `//` comment, 32x Justfile/justfile case collision).", severity = "resolved" },
93109
]
94110

95111
[critical-next-actions]
@@ -100,11 +116,47 @@ actions = [
100116
{ priority = 4, description = "Echo parser syntax stage 2: Echo<A => B> surface syntax in solo dialect", estimate = "4-6 hours" },
101117
]
102118

119+
[security-posture]
120+
# Added 2026-07-27. How this repo's scanner findings are dispositioned, so an
121+
# agent can tell "clean" from "suppressed" without re-deriving it.
122+
hypatia-baseline = ".hypatia-baseline.json"
123+
hypatia-baseline-entries = 7
124+
hypatia-baseline-expires = "2026-10-27"
125+
hypatia-baseline-tracking = "hyperpolymath/my-lang#145"
126+
hypatia-baseline-schema = "array of {severity, rule_module, type, file|file_pattern, note?, expires_at?, tracking_issue?}; validated by hyperpolymath/standards scripts/apply-baseline.sh"
127+
hypatia-ignore = ".hypatia-ignore"
128+
# Scoped exemptions live in .hypatia-ignore (PERMANENT facts) rather than the
129+
# baseline (EXPIRING debt). Three are active, each a rule-precision problem:
130+
exemptions = [
131+
{ rule = "code_safety/coq_axiom", file = "proofs/verification/coq/solo-core/ResourceAlgebra.v", reason = "keyword-matches `Axiom` without distinguishing a Module Type field from a top-level Axiom; the development is axiom-free in the kernel sense (Print Assumptions closed)" },
132+
{ rule = "code_safety/transmute", file = "crates/my-llvm/src/lib.rs", reason = "keyword-matches the WORD transmute, which appears only inside a SAFETY comment; no mem::transmute call exists" },
133+
{ rule = "code_safety/unsafe_block", file = "crates/my-llvm/src/lib.rs", reason = "UNSATISFIABLE RULE: a bare `unsafe\\s*\\{` regex (hypatia lib/rules/code_safety.ex) — the 'requires SAFETY comment' in its description is never checked, so no documentation can clear it. The single unsafe block is mandated by inkwell's unsafe build_gep and carries a 17-line invariant argument." },
134+
]
135+
# Upstream rule-fix candidates surfaced by the 2026-07-27 triage:
136+
upstream-rule-fixes = [
137+
"code_safety/expect_in_hot_path: disambiguate a user-defined `self.expect(..) -> Result` method from the panicking Option/Result::expect (157 false hits here)",
138+
"code_safety/unsafe_block: actually check for an adjacent SAFETY comment, per the rule's own description",
139+
"security_errors/secret_detected: an exemption file's own explanatory comment should not re-trigger the assignment-shape matcher (this produced the only critical)",
140+
]
141+
secret-scanning = "gitleaks (Secret Scanner workflow) + trufflehog (just secret-scan-trufflehog); no live credentials known in tree or history"
142+
143+
[toolchain]
144+
rust = "1.97.0" # mise.toml + .tool-versions
145+
llvm = "21" # my-llvm only, via LLVM_SYS_211_PREFIX; excluded from default build/test and from coverage CI
146+
coq = "distro (apt) — see proofs.yml"
147+
idris2 = "built from source in CI — see proofs.yml"
148+
task-runner = "just (Justfile); parse-verified 2026-07-27"
149+
103150
[maintenance-status]
104-
last-run-utc = "2026-06-05T14:01:00Z"
151+
last-run-utc = "2026-07-27T18:15:00Z"
105152
last-result = "pass"
106-
ci-governance = "pass"
107-
ci-security-scan = "pass"
108-
ci-rust-tests = "pass"
109-
ci-pr = "PR #92 — all 18 checks green (governance, scans, SPARK, Hypatia success, CodeQL neutral)"
153+
main-commit = "8636e15"
154+
main-board = "FULLY GREEN — every workflow on main succeeds or is deliberately skipped"
155+
ci-governance = "pass" # incl. Validate Hypatia Baseline, green for the first time since >=2026-07-22
156+
ci-security-scan = "pass" # Hypatia Security Scan + Secret Scanner + CodeQL + Scorecard
157+
ci-rust-tests = "pass" # Coverage (llvm-cov, floor 40%)
158+
ci-pages = "pass" # https://hyperpolymath.github.io/my-lang/ returns HTTP 200
159+
ci-pr = "PRs #143/#146/#147 merged 2026-07-27; no open PRs"
110160
proof-build = "pass — coqc 10/10 solo-core modules (Quantity, EchoMode, ResourceAlgebra, SoloCore, Tropical, Context, ContextProps, EchoResidue, EchoMeasure, SessionPi; local full clean rebuild 2026-06-14); idris2 --build OK; all per-rung Print Assumptions closed (R-ladder, M1, S1.0–S3c.3-msg, E4)"
161+
known-local-limitation = "my-llvm cannot be built in the maintainer's WSL environment (no system LLVM 21). `cargo build --workspace` FAILS there; `--exclude my-llvm` succeeds. Do not read a bare workspace build failure as a regression without checking for the llvm-sys compile_error."
162+
superseded-branch = "origin/feat/stdlib-fs-env-format — SUPERSEDED, safe to delete: main carries every builtin it adds PLUS fs_list_dir, its stdlib.rs is 355 lines behind, it conflicts on stdlib.rs + CONTRIBUTING.md, and it is 188 commits behind main"

CHANGELOG.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,46 @@ this project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0
1717

1818
## [Unreleased]
1919

20+
### Security
21+
22+
- fix(hypatia): triage all 15 findings — 8 fixed at source, 7 baselined (#146).
23+
The **critical** was self-referential: `.hypatia-ignore`'s own comment
24+
explaining the API-key false positive itself matched the secret detector's
25+
`KEY="..."` assignment shape. Also fixed two genuine empty-name panics in
26+
`my-lint`, a guarded `unwrap` in `my-hir`, and five stale directory
27+
references in docs/config. Remaining debt is baselined to 2026-10-27 under
28+
#145, validated against standards' own `apply-baseline.sh` before merge.
29+
- fix(hypatia): scoped exemption for the unsatisfiable `unsafe_block` rule
30+
(#147). The rule is a bare `unsafe\s*\{` regex — its "requires SAFETY
31+
comment" is never checked — so it belongs in `.hypatia-ignore` as a
32+
permanent fact, not in the baseline as expiring debt.
33+
- fix(ci): repoint `github/codeql-action` off a **nonexistent SHA** (#143).
34+
The pin `29b1f65c` does not exist upstream; an unresolvable `uses:` pin
35+
produces a startup failure with *no check run at all*, so it is invisible
36+
to `gh pr checks`. Now pinned to `4187e74d` (v3.37.3), API-verified.
37+
38+
### Fixed
39+
40+
- fix(just): the `Justfile` was **parse-dead**. A `//` C-style comment on line
41+
2 made `just` reject the entire file (`error: unknown start of token '.'`),
42+
so every recipe was unavailable — while `README.md` documented `just build`
43+
/ `just test` as the golden path. Recipes were also `@echo` stubs. They are
44+
now real `cargo` commands, and `just verify` checks paths that actually
45+
exist instead of swallowing every failure with `||`.
46+
47+
### Changed
48+
49+
- docs: `ARCHITECTURE.md` replaced mint-time boilerplate (which described
50+
`src/`, `config/` and `scripts/` directories that do not exist) with the
51+
real 15-crate workspace layout, the `solo ⊂ duet ⊂ ensemble` containment
52+
hierarchy (with `me` as a projector outside it), and the CI gate map.
53+
- docs: `README.md` and `.machine_readable/6a2/STATE.a2ml` now record the
54+
dialects as nested subsets rather than four co-equal surfaces, and carry a
55+
security-posture section distinguishing expiring baseline debt from
56+
permanent scoped exemptions.
57+
- chore: `mise.toml` replaced ~30 tools pinned to `latest` (Node, Go, Zig,
58+
Java… none used here) with a single pinned `rust = "1.97.0"`.
59+
2060
### Added
2161

2262
- test(conformance): run conformance/valid|invalid + examples/*.my fixtures under `cargo test`, with a fail-closed KNOWN_PARSE_GAPS allowlist (#84)

Justfile

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,75 @@
11
# SPDX-License-Identifier: MPL-2.0
2-
// Owner: Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
3-
# Justfile - hyperpolymath standard task runner
2+
# Owner: Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
3+
# Justfile — hyperpolymath standard task runner
4+
#
5+
# NOTE: every comment here must start with `#`. A `//` C-style comment makes
6+
# the whole file parse-dead (`error: unknown start of token '.'`) and every
7+
# recipe unavailable — which is exactly what happened to this file until
8+
# 2026-07-27, silently breaking the README's documented golden path.
9+
#
10+
# my-llvm needs a system LLVM 21 (LLVM_SYS_211_PREFIX). The default build/test
11+
# recipes exclude it so the golden path works on a clean checkout; use the
12+
# `-all` variants when you have the toolchain. CI does the same (coverage.yml).
413

514
default:
615
@just --list
716

8-
# Build the project
17+
# Build the workspace (excludes my-llvm — needs system LLVM 21)
918
build:
10-
@echo "Building..."
19+
cargo build --workspace --exclude my-llvm
1120

12-
# Run tests
21+
# Build everything including the LLVM back end (requires LLVM 21)
22+
build-all:
23+
cargo build --workspace
24+
25+
# Run unit + conformance tests (excludes my-llvm)
1326
test:
14-
@echo "Testing..."
27+
cargo test --workspace --exclude my-llvm
28+
29+
# Run the full test suite including the LLVM back end (requires LLVM 21)
30+
test-all:
31+
cargo test --workspace
1532

1633
# Run lints
1734
lint:
18-
@echo "Linting..."
19-
20-
# Clean build artifacts
21-
clean:
22-
@echo "Cleaning..."
35+
cargo clippy --workspace --exclude my-llvm --all-targets
2336

2437
# Format code
2538
fmt:
26-
@echo "Formatting..."
39+
cargo fmt --all
40+
41+
# Check formatting without writing (CI-style)
42+
fmt-check:
43+
cargo fmt --all -- --check
44+
45+
# Clean build artifacts
46+
clean:
47+
cargo clean
2748

2849
# Run all checks
29-
check: lint test
50+
check: fmt-check lint test
51+
52+
# Machine-check the Coq solo-core (the authoritative proof track)
53+
proofs-coq:
54+
cd proofs/verification/coq/solo-core && \
55+
coq_makefile -f _CoqProject -o CoqMakefile && \
56+
make -f CoqMakefile
57+
58+
# Machine-check the Idris2 solo-core
59+
proofs-idris:
60+
cd proofs/verification/idris/solo-core && idris2 --build solo-core.ipkg
61+
62+
# Both proof tracks
63+
proofs: proofs-coq proofs-idris
64+
65+
# End-to-end pipeline smoke test (build -> parse -> interpret an example)
66+
pipeline:
67+
./test_pipeline.sh
3068

3169
# Prepare a release
3270
release VERSION:
3371
@echo "Releasing {{VERSION}}..."
72+
@echo "Set version = \"{{VERSION}}\" in Cargo.toml [workspace.package] and .machine_readable/6a2/STATE.a2ml, then tag."
3473

3574
# Run dialect demos (per golden-path contract)
3675
# Usage: just demo [dialect]
@@ -67,10 +106,6 @@ demo dialect="all":
67106
echo ""
68107
echo "Note: Hives are coming soon. Add submodules to ./hives/"
69108
echo ""
70-
# When hives are available, iterate:
71-
# for d in me solo duet ensemble; do
72-
# run_demo "$d" || true
73-
# done
74109
echo "Run 'just demo <dialect>' once hives are installed."
75110
echo "Dialects: me, solo, duet, ensemble"
76111
else
@@ -81,14 +116,20 @@ demo dialect="all":
81116
init:
82117
git submodule update --init --recursive
83118

84-
# Verify playground health
119+
# (The previous version checked a `.machine_read/` path that has never existed
120+
# and swallowed every failure with `||`, so it always reported success.)
121+
# Verify repository structure — exits non-zero when something is missing
85122
verify:
86-
@echo "Verifying playground structure..."
87-
@test -d .machine_read && echo "[OK] .machine_read/ exists" || echo "[FAIL] .machine_read/ missing"
88-
@test -f .machine_read/ANCHOR.scm && echo "[OK] ANCHOR.scm exists" || echo "[FAIL] ANCHOR.scm missing"
89-
@test -f .machine_read/SPEC.playground.scm && echo "[OK] SPEC.playground.scm exists" || echo "[FAIL] SPEC.playground.scm missing"
90-
@test -d hives && echo "[OK] hives/ exists" || echo "[FAIL] hives/ missing"
91-
@echo "Verification complete."
123+
#!/usr/bin/env bash
124+
set -uo pipefail
125+
fail=0
126+
for p in .machine_readable .machine_readable/6a2/STATE.a2ml \
127+
.hypatia-baseline.json .hypatia-ignore \
128+
proofs/STATUS.md Cargo.toml; do
129+
if [[ -e "$p" ]]; then echo "[OK] $p"
130+
else echo "[FAIL] $p missing"; fail=1; fi
131+
done
132+
exit "$fail"
92133
93134
# Run panic-attacker pre-commit scan
94135
assail:

0 commit comments

Comments
 (0)