diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08c7d6d..45f702a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,7 @@ jobs: test: name: Test on ${{ matrix.os }} runs-on: ${{ matrix.os }} + timeout-minutes: 45 if: vars.RUN_EXTENSION_CI == 'true' strategy: @@ -69,6 +70,7 @@ jobs: security: name: Security Audit runs-on: ubuntu-latest + timeout-minutes: 15 if: vars.RUN_EXTENSION_CI == 'true' steps: @@ -100,41 +102,88 @@ jobs: rsr-compliance: name: RSR Compliance Check runs-on: ubuntu-latest - + timeout-minutes: 10 + + # NON-NORMATIVE MIRROR of the RSR v2.0 criteria SSOT + # (hyperpolymath/standards 0-canon/rsr/rsr-criteria-v2.a2ml; the ONE + # normative checker is hypatia:rsr-conformance). Regenerate this step + # from the SSOT detector table instead of hand-drifting it. + # + # Owner ruling 2026-10-10 (#78): community-health files stay AsciiDoc + # (SECURITY.adoc, CODE_OF_CONDUCT.adoc, CONTRIBUTING.adoc). The SSOT's + # 2.1.3/2.1.4/2.1.5 detectors list only .md variants; that detector lag + # is reported upstream (standards#994) — GitHub community-profile + # recognition of .adoc is requested in github/orgs/community discussions + # #181843 and #189645. The .md variants are still ACCEPTED here so a + # future conversion cannot fail this check. + # + # .well-known: canonical location is www/.well-known/ (rsr-template-repo#53, + # stage-5 sweep); a legacy root .well-known/ is tolerated with a warning + # during the migration window. steps: - name: Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Check required files run: | - echo "Checking RSR Bronze compliance..." - - # Documentation - test -f README.md || test -f README.adoc || (echo "Missing README.md or README.adoc" && exit 1) - test -f CONTRIBUTING.adoc || (echo "Missing CONTRIBUTING.adoc" && exit 1) - test -f CODE_OF_CONDUCT.md || (echo "Missing CODE_OF_CONDUCT.md" && exit 1) - test -f SECURITY.md || (echo "Missing SECURITY.md" && exit 1) - test -f MAINTAINERS.adoc || (echo "Missing MAINTAINERS.adoc" && exit 1) - test -f CHANGELOG.adoc || (echo "Missing CHANGELOG.adoc" && exit 1) - test -f CLAUDE.md || (echo "Missing CLAUDE.md" && exit 1) - - # Licenses - test -f LICENSES/MPL-2.0.txt || (echo "Missing LICENSES/MPL-2.0.txt" && exit 1) - test -f LICENSES/CC-BY-SA-4.0.txt || (echo "Missing LICENSES/CC-BY-SA-4.0.txt" && exit 1) - - # .well-known/ - test -f .well-known/security.txt || (echo "Missing security.txt" && exit 1) - test -f .well-known/ai.txt || (echo "Missing ai.txt" && exit 1) - test -f .well-known/humans.txt || (echo "Missing humans.txt" && exit 1) - - # Build system - test -f justfile || test -f Justfile || (echo "Missing justfile or Justfile" && exit 1) - - echo "✅ RSR Bronze compliance verified!" + set -u + echo "Checking RSR compliance (mirror of rsr-criteria-v2 detectors)..." + FAIL=0 + req() { # req + crit="$1"; desc="$2"; shift 2 + for f in "$@"; do [ -f "$f" ] && { echo " ✅ $crit $desc ($f)"; return 0; } + [ -d "$f" ] && { echo " ✅ $crit $desc ($f/)"; return 0; }; done + echo " ❌ $crit missing $desc (looked for: $*)"; FAIL=1 + } + # 2.1.x documentation + req 2.1.1 README README.adoc README.md + req 2.1.5 CONTRIBUTING CONTRIBUTING.adoc .github/CONTRIBUTING.md CONTRIBUTING.md + req 2.1.4 CODE_OF_CONDUCT CODE_OF_CONDUCT.adoc .github/CODE_OF_CONDUCT.md CODE_OF_CONDUCT.md + req 2.1.3 SECURITY SECURITY.adoc .github/SECURITY.md SECURITY.md + req 2.1.7 MAINTAINERS MAINTAINERS.adoc docs/MAINTAINERS.adoc + req 2.1.6 CHANGELOG CHANGELOG.adoc CHANGELOG.md + req 2.1.8 GOVERNANCE GOVERNANCE.adoc docs/GOVERNANCE.adoc .github/GOVERNANCE.md + req — CLAUDE.md CLAUDE.md + # 2.1.2 / 7.1.2 licensing + req 2.1.2 LICENSE LICENSE + req 7.1.2 LICENSES/MPL-2.0.txt LICENSES/MPL-2.0.txt + req 7.1.2 LICENSES/CC-BY-SA-4.0.txt LICENSES/CC-BY-SA-4.0.txt + # 2.3.1 AI manifest front door + req 2.3.1 AI-manifest 0-AI-MANIFEST.a2ml 0-AI-MANIFEST.deed + # 1.1.2 / 1.1.3 build system: Justfile present, Makefile banned + req 1.1.2 Justfile Justfile justfile + if [ -f Makefile ] || [ -f makefile ]; then + echo " ❌ 1.1.3 Makefile present — estate policy is Justfile"; FAIL=1 + else + echo " ✅ 1.1.3 no Makefile" + fi + # 3.1.1 descriptiles canonical, 6a2 retired + req 3.1.1 descriptiles .machine_readable/descriptiles + if [ -d .machine_readable/6a2 ]; then + echo " ❌ 3.1.1 retired .machine_readable/6a2/ still present"; FAIL=1 + else + echo " ✅ 3.1.1 no retired 6a2/" + fi + # 2.2.1 well-known — canonical www/, legacy root tolerated with warning + for f in security.txt ai.txt humans.txt; do + if [ -f "www/.well-known/$f" ]; then + echo " ✅ 2.2.1 www/.well-known/$f" + elif [ -f ".well-known/$f" ]; then + echo " ⚠️ 2.2.1 legacy root .well-known/$f — canonical is www/.well-known/ (rsr-template-repo#53)" + else + echo " ❌ 2.2.1 missing $f (want www/.well-known/$f)"; FAIL=1 + fi + done + if [ "$FAIL" -ne 0 ]; then + echo "::error::RSR compliance check failed — see ❌ rows above" + exit 1 + fi + echo "✅ RSR compliance verified (non-normative mirror; oracle: hypatia:rsr-conformance)" build: name: Build Extension runs-on: ubuntu-latest + timeout-minutes: 30 if: vars.RUN_EXTENSION_CI == 'true' needs: [test, security, rsr-compliance] @@ -173,6 +222,7 @@ jobs: performance: name: Performance Benchmarks runs-on: ubuntu-latest + timeout-minutes: 30 if: github.event_name == 'push' && github.ref == 'refs/heads/main' && vars.RUN_EXTENSION_CI == 'true' steps: @@ -200,6 +250,7 @@ jobs: docs: name: Build Documentation runs-on: ubuntu-latest + timeout-minutes: 15 if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: @@ -219,6 +270,7 @@ jobs: release: name: Create Release runs-on: ubuntu-latest + timeout-minutes: 45 if: startsWith(github.ref, 'refs/tags/v') && vars.RUN_EXTENSION_CI == 'true' needs: [build] permissions: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 06fb4f6..358471f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -31,6 +31,7 @@ jobs: # - https://gh.io/using-larger-runners (GitHub.com only) # Consider using larger runners or machines with greater resources for possible analysis time improvements. runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: 30 permissions: # required for all workflows security-events: write diff --git a/.github/workflows/guix-nix-policy.yml b/.github/workflows/guix-nix-policy.yml index 2fd41f1..e891568 100644 --- a/.github/workflows/guix-nix-policy.yml +++ b/.github/workflows/guix-nix-policy.yml @@ -15,6 +15,7 @@ permissions: jobs: check: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Enforce Guix primary / Nix fallback diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml new file mode 100644 index 0000000..15fcd62 --- /dev/null +++ b/.github/workflows/hypatia-scan.yml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: MPL-2.0 +# +# Standalone Hypatia security scan (push / PR / weekly). This is NOT a duplicate +# of the `hypatia-scan` job in `static-analysis-gate.yml`: that job exists to +# feed the gitbot-fleet LEARNING pipeline (via `deposit-findings`), whereas this +# workflow is the repo's independent security scan. Same tool, two consumers. +# Required by the estate baseline (Hypatia workflow_audit/missing_workflow; +# RSR v2.0 SSOT criterion 6.1.2) and by rsr-template-repo's canonical set. +# +# The reusable workflow pins the scanner to a vetted Hypatia commit, validates +# findings, uploads SARIF to the Security tab, and runs ADVISORY (fix-forward) +# until this repo commits a `.hypatia-baseline.json` + `scripts/apply-baseline.sh`, +# which arms the blocking gate automatically (standards#399/#437 mechanism). +name: Hypatia Security Scan + +on: + push: + branches: [main, master, develop] + pull_request: + branches: [main, master] + schedule: + - cron: '0 0 * * 0' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +permissions: + actions: read # required by the reusable workflow (staleness check reads workflow runs) + contents: read + security-events: write + +jobs: + hypatia: + uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@10e0b6ffb979f0a27fe58206512055133e67460a diff --git a/.github/workflows/instant-sync.yml b/.github/workflows/instant-sync.yml index e6afd3d..67e471f 100644 --- a/.github/workflows/instant-sync.yml +++ b/.github/workflows/instant-sync.yml @@ -1,5 +1,11 @@ # SPDX-License-Identifier: MPL-2.0 # Instant Forge Sync - Triggers propagation to all forges on push/release +# +# WF017 gate: the dispatch step consumes `secrets.FARM_DISPATCH_TOKEN`. On +# repos where that secret has not been propagated the ungated action fails +# EVERY run (the 2026-05-30 estate audit caught 65 repos this way), so the +# job binds a presence boolean and the step gates on it — the idiom the +# Hypatia rule recognises (env-boolean gate). name: Instant Sync on: @@ -14,9 +20,13 @@ permissions: jobs: dispatch: runs-on: ubuntu-latest + timeout-minutes: 10 + env: + HAS_FARM_TOKEN: ${{ secrets.FARM_DISPATCH_TOKEN != '' }} steps: - name: Trigger Propagation id: dispatch + if: env.HAS_FARM_TOKEN == 'true' continue-on-error: true uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 with: @@ -34,7 +44,9 @@ jobs: - name: Confirm if: always() run: | - if [ "${{ steps.dispatch.outcome }}" = "failure" ]; then + if [ "${{ env.HAS_FARM_TOKEN }}" != "true" ]; then + echo "::notice::FARM_DISPATCH_TOKEN not set on this repository — forge propagation skipped." + elif [ "${{ steps.dispatch.outcome }}" = "failure" ]; then echo "::warning::Propagation dispatch failed; rotate FARM_DISPATCH_TOKEN for hyperpolymath/.git-private-farm." else echo "::notice::Propagation triggered for ${{ github.event.repository.name }}" diff --git a/.github/workflows/rsr-antipattern.yml b/.github/workflows/rsr-antipattern.yml index c1ceca9..faa49c8 100644 --- a/.github/workflows/rsr-antipattern.yml +++ b/.github/workflows/rsr-antipattern.yml @@ -84,7 +84,12 @@ jobs: if claude_md.exists(): in_table = False for line in claude_md.read_text(encoding='utf-8').splitlines(): - if re.search(r'TypeScript [Ee]xemptions', line): + # Intended heading: "### TypeScript Exemptions" in .claude/CLAUDE.md. + # Anchored to the markdown heading shape so a PROSE mention of the + # phrase cannot silently walk the parser into the wrong section + # (Hypatia WF022 — the affinescript silent-gate failure class, + # standards#183). + if re.search(r'^#{1,4}\s+.*TypeScript [Ee]xemptions', line): in_table = True continue if in_table and line.startswith(('### ', '## ', '# ')): diff --git a/.github/workflows/wellknown-enforcement.yml b/.github/workflows/wellknown-enforcement.yml index 1e88c56..441c922 100644 --- a/.github/workflows/wellknown-enforcement.yml +++ b/.github/workflows/wellknown-enforcement.yml @@ -1,48 +1,74 @@ # SPDX-License-Identifier: MPL-2.0 +# Well-Known Standards (RFC 9116 + RSR) +# +# Canonical location for the .well-known bundle is www/.well-known/ +# (rsr-template-repo#53, stage-5 sweep #119; the template's +# dot-wellknown-enforcement.yml semantics). A repository-root .well-known/ +# is LEGACY — tolerated with a warning during the migration window, never +# silently validated as canonical. A security.txt absent from BOTH +# locations is a HARD ERROR (the pre-2026-10 version of this workflow +# exit-0'd there — a vacuous gate; see the defect class PR #67 eliminated). name: Well-Known Standards (RFC 9116 + RSR) on: push: branches: [main, master] paths: - - '.well-known/**' + - 'www/.well-known/**' + - '.well-known/**' # legacy location — migration window - 'security.txt' pull_request: paths: + - 'www/.well-known/**' - '.well-known/**' + - 'security.txt' schedule: - # Weekly expiry check + # Weekly expiry check (Mondays 09:00 UTC) - cron: '0 9 * * 1' workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + permissions: contents: read jobs: validate: runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - + - name: RFC 9116 security.txt validation run: | SECTXT="" - [ -f ".well-known/security.txt" ] && SECTXT=".well-known/security.txt" - [ -f "security.txt" ] && SECTXT="security.txt" - + if [ -f "www/.well-known/security.txt" ]; then + SECTXT="www/.well-known/security.txt" + elif [ -f ".well-known/security.txt" ]; then + SECTXT=".well-known/security.txt" + echo "::warning::security.txt at legacy root .well-known/ — canonical location is www/.well-known/ (rsr-template-repo#53)" + elif [ -f "security.txt" ]; then + SECTXT="security.txt" + echo "::warning::security.txt at repository root — canonical location is www/.well-known/" + fi + if [ -z "$SECTXT" ]; then - echo "::warning::No security.txt found. See https://github.com/hyperpolymath/well-known-ecosystem" - exit 0 + echo "::error::No security.txt found — required by RFC 9116 / RSR 2.2.1. See https://github.com/hyperpolymath/well-known-ecosystem" + exit 1 fi - + # Required: Contact grep -q "^Contact:" "$SECTXT" || { echo "::error::Missing Contact field"; exit 1; } - + # Required: Expires if ! grep -q "^Expires:" "$SECTXT"; then echo "::error::Missing Expires field" exit 1 fi - + # Check expiry EXPIRES=$(grep "^Expires:" "$SECTXT" | cut -d: -f2- | tr -d ' ' | head -1) if date -d "$EXPIRES" > /dev/null 2>&1; then @@ -60,10 +86,20 @@ jobs: - name: RSR well-known compliance run: | MISSING="" - [ ! -f ".well-known/security.txt" ] && [ ! -f "security.txt" ] && MISSING="$MISSING security.txt" - [ ! -f ".well-known/ai.txt" ] && MISSING="$MISSING ai.txt" - [ ! -f ".well-known/humans.txt" ] && MISSING="$MISSING humans.txt" - + LEGACY="" + for f in security.txt ai.txt humans.txt; do + if [ -f "www/.well-known/$f" ]; then + : + elif [ -f ".well-known/$f" ] || { [ "$f" = security.txt ] && [ -f security.txt ]; }; then + LEGACY="$LEGACY $f" + else + MISSING="$MISSING $f" + fi + done + if [ -n "$LEGACY" ]; then + echo "::warning::legacy .well-known location (canonical is www/.well-known/):$LEGACY — see rsr-template-repo scripts/migrate-wellknown-to-www.sh" + fi + if [ -n "$MISSING" ]; then echo "::warning::Missing RSR recommended files:$MISSING" echo "Reference: https://github.com/hyperpolymath/well-known-ecosystem/.well-known/" @@ -71,6 +107,9 @@ jobs: echo "✅ RSR well-known compliant" fi + - name: Canonical well-known bundle validation + run: bash www/tests/check-wellknown.sh + - name: Mixed content check run: | MIXED=$(grep -rE 'src="http://|href="http://' --include="*.html" --include="*.htm" . 2>/dev/null | grep -vE 'localhost|127\.0\.0\.1|example\.com' | head -5 || true) diff --git a/.hypatia-ignore b/.hypatia-ignore new file mode 100644 index 0000000..35c018b --- /dev/null +++ b/.hypatia-ignore @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: MPL-2.0 +# +# .hypatia-ignore — scoped, documented exemptions for the Hypatia scanner. +# +# Format (one entry per line): +# /: +# /*: (any type in module) +# (any rule, any module) +# +# Lines starting with `#` are comments. A path-fragment is a substring match +# against the repo-relative file path. Built-in defaults live in Hypatia's +# lib/hypatia/scanner_suppression.ex — do not duplicate them here. Each entry +# below carries a stated reason. Prefer fix-the-code or an inline directive +# over an entry here; this file is for whole-file/directory exemptions. +# Shaped after rsr-template-repo's own .hypatia-ignore. + +# ─── Research-extension workflow findings ─────────────────────────────────────── +# +# RE001 is an advisory recommendation to add harden-runner whenever a workflow +# references a secret. These workflows deliberately use repository-locked, +# SHA-pinned actions and least-privilege permissions while retaining the +# outbound access they need (GitHub API, SMTP, forge propagation). Mirrors the +# template's own exemptions for the same workflow classes. Keep the exemptions +# file-scoped so the advisory still applies to every new workflow. +research_extensions/RE001:.github/workflows/push-email-notify.yml +research_extensions/RE001:.github/workflows/label-triage.yml +research_extensions/RE001:.github/workflows/labels.yml +research_extensions/RE001:.github/workflows/instant-sync.yml +research_extensions/RE001:.github/workflows/ci.yml diff --git a/.machine_readable/6a2/ECOSYSTEM.a2ml b/.machine_readable/6a2/ECOSYSTEM.a2ml deleted file mode 100644 index 846d1a2..0000000 --- a/.machine_readable/6a2/ECOSYSTEM.a2ml +++ /dev/null @@ -1,26 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -# -# ECOSYSTEM.a2ml — Nextgen Languages Evangeliser ecosystem position -[metadata] -version = "1.2" -last-updated = "2026-06-18" - -[project] -name = "Nextgen Languages Evangeliser" -purpose = "Duolingo/Rosetta-Stone for programming languages — a comprehension+transfer engine that classifies cross-language correspondences; emits into PanLL; extended by community language cartridges." -role = "project" - -[position-in-ecosystem] -category = "application" -description = "Cross-language correspondence engine; classify-not-translate; feeds PanLL" - -[related-projects] -projects = [ - "hyperpolymath/panll — downstream IDE / contact layer this engine feeds (+ eNSAID)", - "hyperpolymath/proven-tests-and-benches — Dyadic relation carrier", - "hyperpolymath/echo-types — loss-with-residue (Agda)", - "hyperpolymath/standards — cartridges, overlay-protocol, ensaid-config, accessibility", - "hyperpolymath/affinescript — first-class target + future host", - "hyperpolymath/typed-wasm — AffineScript codegen layer", -] diff --git a/.machine_readable/6a2/STATE.a2ml b/.machine_readable/6a2/STATE.a2ml deleted file mode 100644 index e0ab148..0000000 --- a/.machine_readable/6a2/STATE.a2ml +++ /dev/null @@ -1,46 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -# -# STATE.a2ml — Nextgen Languages Evangeliser project state -[metadata] -project = "nextgen-languages-evangeliser" -version = "1.2.0" -last-updated = "2026-06-18" -status = "active" - -[project-context] -name = "Nextgen Languages Evangeliser" -purpose = "Duolingo/Rosetta-Stone for programming languages — a comprehension+transfer engine that classifies cross-language correspondences so learning transfers from a known language to a new one." -completion-percentage = 58 - -[position] -phase = "Re-point: correspondence engine + abstraction-model pivot" # design | implementation | testing | maintenance | archived -maturity = "alpha" # experimental | alpha | beta | production | lts - -[route-to-mvp] -milestones = [ - "M0 correspondence-model spec — complete", - "M1 standards/repo hygiene — complete", - "M2 identity re-point + abstraction-model pivot — in progress", - "M3 browser multi-pane GUI + overlay view-layers — pending", - "M4 AffineScript host port (Zig FFI + Idris2 ABI) — gated", - "M5 cartridge contract + 2nd language pack — pending", - "M6 proofs/benches + PanLL octad emission — pending", -] - -[blockers-and-issues] -issues = [ - "AffineScript/Idris2/Zig/Deno toolchain absent in some environments — author now, verify in CI", - "JTV grammar-v2 worked example stubbed pending julia-the-viper repo access", - "Licence-label drift to flag to owner vs MPL-2.0 SPDX — FLAG-ONLY, manual", -] - -[critical-next-actions] -actions = [ - "Abstraction-model pivot: Concept/Form/Transition + CorrespondenceKind in src", - "Browser multi-pane GUI surface", -] - -[maintenance-status] -last-run-utc = "2026-06-18T00:00:00Z" -last-result = "pass" # unknown | pass | warn | fail diff --git a/.machine_readable/ECOSYSTEM.a2ml b/.machine_readable/ECOSYSTEM.a2ml deleted file mode 100644 index badfe41..0000000 --- a/.machine_readable/ECOSYSTEM.a2ml +++ /dev/null @@ -1,34 +0,0 @@ -;; SPDX-License-Identifier: MPL-2.0 -;; ECOSYSTEM.a2ml — nextgen-languages-evangeliser ecosystem position -;; Migrated from root ECOSYSTEM.scm on 2026-04-12 -;; Renamed from rescript-evangeliser on 2026-04-16 -;; Updated for the correspondence re-point on 2026-06-18 - -(ecosystem - (version "1.2") - (name "nextgen-languages-evangeliser") - (predecessor "rescript-evangeliser") - (type "project") - (purpose "Duolingo/Rosetta-Stone for programming languages — a comprehension+transfer engine that classifies cross-language correspondences so learning transfers from a known language to a new one") - - (position-in-ecosystem - (role "component") - (layer "application") - (description "Cross-language correspondence engine; classify-not-translate; emits into PanLL; extended by community language cartridges")) - - (related-projects - (downstream-ide "hyperpolymath/panll (+ eNSAID contact layer this engine feeds)") - (carrier-relation "hyperpolymath/proven-tests-and-benches (Dyadic.idr)") - (carrier-residue "hyperpolymath/echo-types (loss-with-residue, Agda)") - (cartridge-home "hyperpolymath/standards (cartridges, overlay-protocol, ensaid-config, accessibility)") - (flagship-target "hyperpolymath/affinescript") - (codegen-layer "hyperpolymath/typed-wasm") - (legacy-target "rescript-lang/rescript")) - - (what-this-is "A language-comprehension and transfer engine: it classifies cross-language correspondences (cognate/false-friend/antonym/alien-realization/novel/vanished) and teaches the transfer without shame") - (what-this-is-not - "An IDE (that is PanLL)" - "A linter" - "A universal translator (no Curry-Howard-fidelity goal)" - "A compiler or type-checker" - "A shame-the-JavaScript pattern matcher")) diff --git a/.machine_readable/META.a2ml b/.machine_readable/META.a2ml deleted file mode 100644 index 7da2f59..0000000 --- a/.machine_readable/META.a2ml +++ /dev/null @@ -1,25 +0,0 @@ -;; SPDX-License-Identifier: MPL-2.0 -;; META.a2ml — nextgen-languages-evangeliser meta-information -;; Migrated from root META.scm on 2026-04-12 -;; Updated for the correspondence re-point on 2026-06-18 - -(meta - (version "1.1.0") - (project "nextgen-languages-evangeliser") - (architecture-decisions - (adr-1 "Classify, don't translate — the engine names the kind of cross-language correspondence (CorrespondenceKind) and its residue; it is not a universal translator and claims no Curry-Howard fidelity") - (adr-2 "Formal carrier = Dyadic relation + Echo loss-with-residue (proven-tests-and-benches Dyadic.idr + hyperpolymath/echo-types); invariant-path is the governance front-end") - (adr-3 "Engine vs cartridge — we build the general engine, interface and vocabulary; per-language facts are authored as cartridges (standards/cartridges)") - (adr-4 "Host moves ReScript -> AffineScript with Zig FFI + Idris2 ABI seams; the ReScript host has been removed (Deno + cartridges is the interim surface), ReScript retained only as a legacy target language") - (adr-5 "The engine emits into PanLL (octads -> VeriSimDB -> panels); it is not itself the IDE") - (adr-6 "Knot-theory framing is an aspirational lens, not a literal computation — documented honestly; RESOLVED 2026-08-05, the third layer is the SORTAL layer, see invariant-path ADR-0001")) - (development-practices - (code-style "standard") - (security "openssf-scorecard + panic-attack static-analysis-gate") - (versioning "semver") - (documentation "asciidoc") - (branching "trunk-based")) - (design-rationale - (no-shame "celebrate / minimise / better / safety / example — transfer-first pedagogy") - (levels-of-objects "classification runs per stratum: surface -> structure -> intention -> trope -> invariant") - (accessibility "Hyperpolymath Accessibility Standard, Level A -> AA"))) diff --git a/.machine_readable/STATE.a2ml b/.machine_readable/STATE.a2ml deleted file mode 100644 index 547c72d..0000000 --- a/.machine_readable/STATE.a2ml +++ /dev/null @@ -1,55 +0,0 @@ -;; SPDX-License-Identifier: MPL-2.0 -;; STATE.a2ml — nextgen-languages-evangeliser project state -;; Migrated from root STATE.scm on 2026-04-12 -;; Renamed from rescript-evangeliser on 2026-04-16 -;; Re-pointed to the correspondence (classify-not-translate) model on 2026-06-18 - -(state - (metadata - (version "1.2.0") - (project "nextgen-languages-evangeliser") - (predecessor "rescript-evangeliser") - (last-updated "2026-06-18")) - - (identity - (is "Duolingo/Rosetta-Stone for programming languages — a comprehension+transfer engine that classifies cross-language correspondences so learning transfers from a known language to a new one") - (is-not "an IDE (that is PanLL), a linter, a universal translator, or a shame-the-JS pattern matcher") - (model "Concept/Form/Transition + six CorrespondenceKinds (cognate/false-friend/antonym/alien-realization/novel/vanished) as graded Echo fibres") - (division "we build the engine + interface + vocabulary; the community authors per-language cartridges")) - - (current-position - (phase "Re-point: cross-language correspondence engine + abstraction-model pivot") - (overall-completion 66) - (working-features - "Correspondence-model design spec (docs/theory/CORRESPONDENCE-MODEL.adoc) — merged" - "Standards/machine-readable hygiene (6a2 manifests, panic-attack gate, eNSAID config) — merged" - "Idris2 ABI spine (Carrier.idr + Correspondence.idr) — typechecks; Zig FFI mirror; AffineScript host binding" - "Cartridge contract (JSON-Schema) + reference pack (one correspondence per CorrespondenceKind)" - "Browser multi-pane workspace (index / forms / classification; five overlay view-layers) — merged" - "Offline CLI (Deno; reads cartridges): --kind / --find / --json" - "Cartridge invariant tests (Deno; mirrors Abi.Correspondence)" - "Legacy ReScript host removed — runtime surface is Deno + cartridges")) - - (route-to-mvp - (milestones - (m0 (status "complete") (items "Correspondence-model spec: Concept/Form/Transition + six kinds + Dyadic/Echo carrier")) - (m1 (status "complete") (items "Standards/repo hygiene: 6a2 manifests, panic-attack gate, eNSAID config")) - (m2 (status "in-progress") (items "Identity re-point" "Abstraction-model pivot: Concept/Form/Transition + CorrespondenceKind as cartridge facts")) - (m3 (status "complete") (items "Browser multi-pane GUI + overlay view-layers + HAS accessibility")) - (m4 (status "gated") (items "AffineScript host application (ReScript host removed; Zig FFI + Idris2 ABI seams authored)")) - (m5 (status "pending") (items "Cartridge contract + 2nd language pack")) - (m6 (status "pending") (items "Idris2/Echo witnesses + benches + PanLL octad emission")))) - - (blockers-and-issues - "AffineScript/Idris2/Zig/Deno toolchain absent in some environments — author now, verify in CI" - "JTV grammar-v2 worked example stubbed pending julia-the-viper repo access") - - (critical-next-actions - "Port the legacy pattern catalogue into cartridge facts (Concept/Form/Transition + CorrespondenceKind)" - "Author a second language pack to prove engine-agnosticism" - "Begin the AffineScript host application against the Idris2 ABI / Zig FFI seams") - - (session-history - (2026-04-16 "Phase 0 decisions locked; Phase 1 started") - (2026-06-18 "Re-point: correspondence-model spec merged (#19); standards hygiene merged (#20); identity re-point") - (2026-06-27 "ABI/FFI/host spine + multi-pane GUI merged; legacy ReScript host removed (Deno + cartridges)"))) diff --git a/.machine_readable/6a2/AGENTIC.a2ml b/.machine_readable/descriptiles/AGENTIC.a2ml similarity index 90% rename from .machine_readable/6a2/AGENTIC.a2ml rename to .machine_readable/descriptiles/AGENTIC.a2ml index 433bdc4..2bcbfa3 100644 --- a/.machine_readable/6a2/AGENTIC.a2ml +++ b/.machine_readable/descriptiles/AGENTIC.a2ml @@ -38,6 +38,6 @@ require-rerun-after-fix = true release-claim-requires-hard-pass = true [automation-hooks] -# on-enter: Read 0-AI-MANIFEST.a2ml, then .machine_readable/6a2/STATE.a2ml -# on-exit: Update 6a2/STATE.a2ml with session outcomes +# on-enter: Read 0-AI-MANIFEST.a2ml, then .machine_readable/descriptiles/STATE.a2ml +# on-exit: Update descriptiles/STATE.a2ml with session outcomes # on-commit: Run `just validate-rsr` diff --git a/.machine_readable/descriptiles/ECOSYSTEM.a2ml b/.machine_readable/descriptiles/ECOSYSTEM.a2ml new file mode 100644 index 0000000..5dc0699 --- /dev/null +++ b/.machine_readable/descriptiles/ECOSYSTEM.a2ml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# ECOSYSTEM.a2ml — Nextgen Languages Evangeliser ecosystem position +# Relocated 2026-10-10 from the retired .machine_readable/6a2/ path to the +# canonical .machine_readable/descriptiles/ (RSR v2.0 SSOT 3.1.1/3.1.4; +# Hypatia SD004). Content reconciled with the legacy s-expression-dialect +# .machine_readable/ECOSYSTEM.a2ml twin (deleted in the same change): the +# record dialect is the base; the twin's predecessor, what-this-is / +# what-this-is-not and legacy-target facts are ported in. +[metadata] +version = "1.3" +last-updated = "2026-10-10" + +[project] +name = "Nextgen Languages Evangeliser" +predecessor = "rescript-evangeliser" +type = "project" +purpose = "Duolingo/Rosetta-Stone for programming languages — a comprehension+transfer engine that classifies cross-language correspondences; emits into PanLL; extended by community language cartridges." +role = "component" + +[position-in-ecosystem] +category = "application" +description = "Cross-language correspondence engine; classify-not-translate; feeds PanLL" + +[related-projects] +projects = [ + "hyperpolymath/panll — downstream IDE / contact layer this engine feeds (+ eNSAID)", + "hyperpolymath/proven-tests-and-benches — Dyadic relation carrier (Dyadic.idr)", + "hyperpolymath/echo-types — loss-with-residue (Agda)", + "hyperpolymath/standards — cartridges, overlay-protocol, ensaid-config, accessibility", + "hyperpolymath/affinescript — first-class target + future host", + "hyperpolymath/typed-wasm — AffineScript codegen layer", + "rescript-lang/rescript — legacy target language in the correspondence catalogue", +] + +[what-this-is] +is = "A language-comprehension and transfer engine: it classifies cross-language correspondences (cognate/false-friend/antonym/alien-realization/novel/vanished) and teaches the transfer without shame" + +[what-this-is-not] +is-not = [ + "An IDE (that is PanLL)", + "A linter", + "A universal translator (no Curry-Howard-fidelity goal)", + "A compiler or type-checker", + "A shame-the-JavaScript pattern matcher", +] diff --git a/.machine_readable/6a2/META.a2ml b/.machine_readable/descriptiles/META.a2ml similarity index 60% rename from .machine_readable/6a2/META.a2ml rename to .machine_readable/descriptiles/META.a2ml index 823b1bd..479e9fc 100644 --- a/.machine_readable/6a2/META.a2ml +++ b/.machine_readable/descriptiles/META.a2ml @@ -2,9 +2,16 @@ # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # # META.a2ml — Nextgen Languages Evangeliser meta-level information +# Relocated 2026-10-10 from the retired .machine_readable/6a2/ path to the +# canonical .machine_readable/descriptiles/ (RSR v2.0 SSOT 3.1.1/3.1.3; +# Hypatia SD004). Content reconciled with the legacy s-expression-dialect +# .machine_readable/META.a2ml twin (deleted in the same change): the record +# dialect is the base; the twin's development-practices and design-rationale +# facts are ported in. [metadata] -version = "1.1.0" -last-updated = "2026-06-18" +version = "1.2.0" +project = "nextgen-languages-evangeliser" +last-updated = "2026-10-10" [project-info] license = "MPL-2.0" @@ -21,10 +28,18 @@ decisions = [ ] [development-practices] +code-style = "standard" +security = "openssf-scorecard + panic-attack static-analysis-gate + hypatia scan" versioning = "SemVer" documentation = "AsciiDoc" +branching = "trunk-based" build-tool = "just" +[design-rationale] +no-shame = "celebrate / minimise / better / safety / example — transfer-first pedagogy" +levels-of-objects = "classification runs per stratum: surface -> structure -> intention -> trope -> invariant" +accessibility = "Hyperpolymath Accessibility Standard, Level A -> AA" + [maintenance-axes] scoping-first = true axis-1 = "must > intend > like" diff --git a/.machine_readable/6a2/NEUROSYM.a2ml b/.machine_readable/descriptiles/NEUROSYM.a2ml similarity index 100% rename from .machine_readable/6a2/NEUROSYM.a2ml rename to .machine_readable/descriptiles/NEUROSYM.a2ml diff --git a/.machine_readable/6a2/PLAYBOOK.a2ml b/.machine_readable/descriptiles/PLAYBOOK.a2ml similarity index 87% rename from .machine_readable/6a2/PLAYBOOK.a2ml rename to .machine_readable/descriptiles/PLAYBOOK.a2ml index 0827bf7..986e642 100644 --- a/.machine_readable/6a2/PLAYBOOK.a2ml +++ b/.machine_readable/descriptiles/PLAYBOOK.a2ml @@ -15,13 +15,13 @@ project = "nextgen-languages-evangeliser" # target = "library + cli + browser-gui" [incident-response] -# 1. Read .machine_readable/6a2/STATE.a2ml for current status. +# 1. Read .machine_readable/descriptiles/STATE.a2ml for current status. # 2. Review recent commits and CI (Static Analysis Gate, RSR Compliance). # 3. Run `just validate-rsr` to check compliance. # 4. Run `just assail` for the panic-attack static-analysis scan. [release-process] -# 1. Update version in 6a2/STATE.a2ml and 6a2/META.a2ml. +# 1. Update version in descriptiles/STATE.a2ml and descriptiles/META.a2ml. # 2. Run `just validate-rsr` and `just crg-grade`. # 3. Tag and push. diff --git a/.machine_readable/descriptiles/STATE.a2ml b/.machine_readable/descriptiles/STATE.a2ml new file mode 100644 index 0000000..79feb0b --- /dev/null +++ b/.machine_readable/descriptiles/STATE.a2ml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# STATE.a2ml — Nextgen Languages Evangeliser project state +# Relocated 2026-10-10 from the retired .machine_readable/6a2/ path to the +# canonical .machine_readable/descriptiles/ (RSR v2.0 SSOT 3.1.1/3.1.2; +# Hypatia SD004). Content reconciled with the legacy s-expression-dialect +# .machine_readable/STATE.a2ml twin (deleted in the same change): the record +# dialect is the base, the semantically newer facts from the twin (M3 +# complete, completion 66, working features, session history to 2026-06-27) +# are ported in. +[metadata] +project = "nextgen-languages-evangeliser" +predecessor = "rescript-evangeliser" +version = "1.3.0" +last-updated = "2026-10-10" +status = "active" + +[project-context] +name = "Nextgen Languages Evangeliser" +purpose = "Duolingo/Rosetta-Stone for programming languages — a comprehension+transfer engine that classifies cross-language correspondences so learning transfers from a known language to a new one." +is-not = "an IDE (that is PanLL), a linter, a universal translator, or a shame-the-JS pattern matcher" +model = "Concept/Form/Transition + six CorrespondenceKinds (cognate/false-friend/antonym/alien-realization/novel/vanished) as graded Echo fibres" +division = "we build the engine + interface + vocabulary; the community authors per-language cartridges" +completion-percentage = 66 + +[position] +phase = "Re-point: correspondence engine + abstraction-model pivot" # design | implementation | testing | maintenance | archived +maturity = "alpha" # experimental | alpha | beta | production | lts + +[working-features] +features = [ + "Correspondence-model design spec (docs/theory/CORRESPONDENCE-MODEL.adoc) — merged", + "Standards/machine-readable hygiene (descriptile manifests, panic-attack gate, eNSAID config) — merged", + "Idris2 ABI spine (Carrier.idr + Correspondence.idr) — typechecks; Zig FFI mirror; AffineScript host binding", + "Cartridge contract (JSON-Schema) + reference pack (one correspondence per CorrespondenceKind)", + "Browser multi-pane workspace (index / forms / classification; five overlay view-layers) — merged", + "Offline CLI (Deno; reads cartridges): --kind / --find / --json", + "Cartridge invariant tests (Deno; mirrors Abi.Correspondence)", + "Legacy ReScript host removed — runtime surface is Deno + cartridges", +] + +[route-to-mvp] +milestones = [ + "M0 correspondence-model spec — complete", + "M1 standards/repo hygiene — complete", + "M2 identity re-point + abstraction-model pivot — in progress", + "M3 browser multi-pane GUI + overlay view-layers — complete", + "M4 AffineScript host port (Zig FFI + Idris2 ABI) — gated", + "M5 cartridge contract + 2nd language pack — pending", + "M6 proofs/benches + PanLL octad emission — pending", +] + +[blockers-and-issues] +issues = [ + "AffineScript/Idris2/Zig/Deno toolchain absent in some environments — author now, verify in CI", + "JTV grammar-v2 worked example stubbed pending julia-the-viper repo access", + "Licence-label drift to flag to owner vs MPL-2.0 SPDX — FLAG-ONLY, manual", +] + +[critical-next-actions] +actions = [ + "Port the legacy pattern catalogue into cartridge facts (Concept/Form/Transition + CorrespondenceKind)", + "Author a second language pack to prove engine-agnosticism", + "Begin the AffineScript host application against the Idris2 ABI / Zig FFI seams", +] + +[session-history] +history = [ + "2026-04-16 Phase 0 decisions locked; Phase 1 started", + "2026-06-18 Re-point: correspondence-model spec merged (#19); standards hygiene merged (#20); identity re-point", + "2026-06-27 ABI/FFI/host spine + multi-pane GUI merged; legacy ReScript host removed (Deno + cartridges)", + "2026-10-10 Descriptiles relocated to .machine_readable/descriptiles/; deferred red checks remediated (#78)", +] + +[maintenance-status] +last-run-utc = "2026-10-10T00:00:00Z" +last-result = "pass" # unknown | pass | warn | fail diff --git a/0-AI-MANIFEST.a2ml b/0-AI-MANIFEST.a2ml index 8d5e431..8a9bc12 100644 --- a/0-AI-MANIFEST.a2ml +++ b/0-AI-MANIFEST.a2ml @@ -11,9 +11,12 @@ This is the AI manifest for **nextgen-languages-evangeliser**. It declares: ## CANONICAL LOCATIONS (UNIVERSAL RULE) -### Machine-Readable Metadata: `.machine_readable/` ONLY +### Descriptiles: `.machine_readable/descriptiles/` ONLY -These 6 a2ml files MUST exist in `.machine_readable/` directory ONLY: +These 6 a2ml descriptiles MUST exist in `.machine_readable/descriptiles/` ONLY +(the former `.machine_readable/` root of the machine tree and the +`.machine_readable/6a2/` directory are BOTH retired — RSR v2.0 SSOT 3.1.1, +6a2 deprecated 2026-06-30, enforced by Hypatia SD004): 1. **STATE.a2ml** - Project state, progress, blockers 2. **META.a2ml** - Architecture decisions, governance 3. **ECOSYSTEM.a2ml** - Position in ecosystem, relationships @@ -21,15 +24,16 @@ These 6 a2ml files MUST exist in `.machine_readable/` directory ONLY: 5. **NEUROSYM.a2ml** - Neurosymbolic integration config 6. **PLAYBOOK.a2ml** - Operational runbook -**CRITICAL:** If ANY of these files exist in the root directory, this is an ERROR. +**CRITICAL:** If ANY of these files exist in the repository root, directly in +`.machine_readable/`, or in `.machine_readable/6a2/`, this is an ERROR. -### Anchor File: `.machine_readable/anchors/ANCHOR.a2ml` ONLY +### Anchor File: `.machine_readable/descriptiles/anchors/ANCHOR.a2ml` ONLY Canonical authority and semantic-boundary declaration MUST exist at: -` .machine_readable/anchors/ANCHOR.a2ml ` +` .machine_readable/descriptiles/anchors/ANCHOR.a2ml ` -Do not place `ANCHOR.a2ml` at repository root. +Do not place `ANCHOR.a2ml` at repository root or directly in `.machine_readable/`. ### Maintenance Policies: `.machine_readable/policies/` ONLY diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index d57878f..428af18 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -8,6 +8,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 == [Unreleased] +=== Estate red-checks remediation (2026-10) — issue #78 + +==== Fixed +- Cleared the two deferred red checks on `main` (issue #78): the Hypatia gate's 10 critical findings and the stale `RSR Compliance Check`. +- Descriptiles relocated to the canonical `.machine_readable/descriptiles/` (RSR v2.0 SSOT 3.1.x; Hypatia SD004): the six `6a2/` record-dialect manifests moved, the legacy s-expression twins (`.machine_readable/{META,STATE,ECOSYSTEM}.a2ml`) reconciled into them and retired, retired-location references repaired (`0-AI-MANIFEST.a2ml`, `AGENTIC.a2ml`, `PLAYBOOK.a2ml`, `HANDOFF.adoc`, `CLAUDE.md`). +- `setup.sh`: download-and-execute (`curl … | bash`) removed in favour of checksummed manual-install instructions (CWE-494). +- `gui/app.js`: removed the dead `innerHTML` escape hatch — SafeDOM by construction (CWE-79). +- Least-privilege Deno permissions everywhere; `-A` eliminated (`scripts/validate-cartridges.js`, `scripts/launch-gui.sh`, `Justfile`) (CWE-250). +- `rsr-antipattern.yml`: exemption-heading regex anchored to the markdown heading shape (Hypatia WF022). +- `instant-sync.yml`: `repository-dispatch` now gated on secret presence (WF017) with `timeout-minutes`. +- `ci.yml`, `codeql.yml`, `guix-nix-policy.yml`: `timeout-minutes` on every job (ERR-WF-013). +- `www/.well-known/security.txt`: `Canonical:`/`Policy:` URLs corrected (wrong repository name, retired paths). + +==== Added +- `.github/workflows/hypatia-scan.yml` — estate-required independent Hypatia security scan, pinned to the standards reusable workflow (RSR 6.1.2). +- `www/tests/check-wellknown.sh` — canonical well-known bundle validator (minted from rsr-template-repo), wired into `just validate-security` and the Well-Known Standards workflow. +- `docs/plans/PLAN-issue-78-red-checks.adoc` — the remediation ultraplan. + +==== Changed +- `ci.yml` `RSR Compliance Check` regenerated as a non-normative mirror of the rsr-criteria-v2 SSOT detectors (any_of shapes; `www/.well-known/` canonical; owner ruling 2026-10-10: community-health files stay AsciiDoc). +- `just validate-rsr` repaired: `deno task` recipes (which referenced a `deno.json` that never existed) replaced with direct least-privilege `deno` commands; validators updated to the same SSOT shapes. +- `wellknown-enforcement.yml` now fails closed when `security.txt` is absent everywhere (was a vacuous warn-only gate) and treats `www/.well-known/` as canonical. +- `SECURITY.adoc` modernized: retired VS Code-extension threat model replaced with the current Deno + cartridge-data surface; dead links fixed. +- `Justfile` header aligned to LANGUAGE-POLICY runtime tiers (Bun tier 1; this repo's Deno surface is tier-2 grandfathered). + === Re-point (2026-06) — correspondence engine; ReScript host removed ==== Added diff --git a/CLAUDE.md b/CLAUDE.md index a88a84d..6bccc33 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -36,7 +36,7 @@ Emits `octads` → VeriSimDB (`:8097`) + Groove signals → **PanLL** panels; vi ## Technology Stack -- **Deno** — runtime, tooling & current host surface (workspace server + CLI + cartridge data); not npm/bun +- **Deno** — runtime, tooling & current host surface (workspace server + CLI + cartridge data); tier 2 and *grandfathered* under the estate LANGUAGE-POLICY (Bun is tier 1 for new JS surface work; npm is last-resort) - **AffineScript** — future host + first-class target (Zig FFI + Idris2 ABI seams; OCaml 5.1+; emits typed-wasm IR → WebAssembly) - **Zig** — FFI layer; **Idris2** — ABI / proofs - **Nickel** — configuration @@ -61,8 +61,9 @@ nextgen-languages-evangeliser/ ├── bin/evangeliser.js # Offline CLI (Deno; reads cartridges) ├── test/run_all.js # Cartridge invariant tests (Deno) ├── docs/theory/CORRESPONDENCE-MODEL.adoc # Canonical design spec -├── Justfile # Task orchestration (NOT Makefile) -└── deno.json # Deno configuration +├── .machine_readable/descriptiles/ # STATE / META / ECOSYSTEM / AGENTIC / NEUROSYM / PLAYBOOK (record-dialect A2ML) +├── www/.well-known/ # RFC 9116 security.txt + ai.txt + humans.txt (canonical location) +└── Justfile # Task orchestration (NOT Makefile) ``` ## Common Commands @@ -85,7 +86,7 @@ We **never** shame developers. The voice stays **celebrate / minimise / better / ## Re-point Status (was the ReScript → AffineScript "migration") - ✅ Correspondence-model spec merged (Concept/Form/Transition + six kinds + Dyadic/Echo carrier) -- ✅ Standards / repo hygiene merged (6a2 manifests, panic-attack gate, eNSAID config) +- ✅ Standards / repo hygiene merged (descriptile manifests — relocated to `.machine_readable/descriptiles/` in 2026-10 — panic-attack gate, eNSAID config) - ✅ Idris2 ABI spine + Zig FFI mirror + AffineScript host binding authored (ABI typechecks) - ✅ Browser multi-pane GUI (overlay view-layers) merged - ✅ Legacy ReScript host **removed** — runtime surface is Deno + cartridges @@ -99,7 +100,7 @@ We **never** shame developers. The voice stays **celebrate / minimise / better / - Lead with the **correspondence model**; AffineScript is one first-class target/host, not the sole pitch. - **Classify, don't translate.** No universal-translator or Curry–Howard-fidelity claims. - **No shame.** Keep the celebrate / minimise / better / safety / example voice. -- **Use Deno, not npm/bun. Use Justfile, not Makefile. Use Zig for FFI, Idris2 for ABI/proofs.** +- **This repo's runtime is Deno (tier 2, grandfathered); estate LANGUAGE-POLICY ranks Bun tier 1 for new work and npm last. Use Justfile, not Makefile. Use Zig for FFI, Idris2 for ABI/proofs.** - **The ReScript host is removed.** ReScript is allowed only as a legacy *target* in correspondence content; never reintroduce `.res` host code — the host path is AffineScript (`.affine`). - Toolchain may be absent in this environment — **author now, verify in CI**; never claim a green build you did not run. - **Licence: MPL-2.0 (sole-owner repo). Add SPDX `MPL-2.0` to new files. NEVER relicense or bulk-sweep SPDX; licence-label drift is FLAG-ONLY to the owner.** diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc index 1cac254..d0a1cc6 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.adoc @@ -114,12 +114,12 @@ full policy and JavaScript-glue exemptions. == Security Never commit secrets. Report vulnerabilities privately — see -link:SECURITY.md[SECURITY.md]. +link:SECURITY.adoc[SECURITY.adoc]. == Code of Conduct This project follows the *Compassionate Code Contribution Pledge* — see -link:CODE_OF_CONDUCT.md[CODE_OF_CONDUCT.md]. In short: never shame developers, +link:CODE_OF_CONDUCT.adoc[CODE_OF_CONDUCT.adoc]. In short: never shame developers, celebrate existing knowledge, assume good intent, foster psychological safety. == Licence diff --git a/HANDOFF.adoc b/HANDOFF.adoc index 2eb89b8..768942a 100644 --- a/HANDOFF.adoc +++ b/HANDOFF.adoc @@ -6,7 +6,7 @@ Quick orientation for the next working session (e.g. resuming in the Claude Code desktop app). The long-term plan lives in link:ROADMAP.adoc[ROADMAP.adoc]; the canonical design in link:docs/theory/CORRESPONDENCE-MODEL.adoc[the spec]; the -live state in `.machine_readable/STATE.a2ml`. +live state in `.machine_readable/descriptiles/STATE.a2ml`. == Where things stand (2026-06) diff --git a/Justfile b/Justfile index f3b47e8..20dc6eb 100644 --- a/Justfile +++ b/Justfile @@ -2,10 +2,13 @@ # https://github.com/casey/just # SPDX-License-Identifier: MPL-2.0 # -# Per Hyperpolymath policy: -# - Use Deno, not npm/bun +# Per Hyperpolymath LANGUAGE-POLICY (standards §1): +# - Runtime tiers: Bun (1st) > Deno (2nd) > pnpm > npm (last resort). +# This repo's Deno surface is EXISTING and GRANDFATHERED (tier 2) — keep it +# Deno; do not add npm/bun lockfiles (mixed toolchains fail runtime-policy). # - Use justfile, not Makefile # - Use AffineScript, not TypeScript (ReScript host removed 2026-06; AffineScript host pending) +# - Deno permissions are least-privilege: explicit --allow-* flags, never -A (CWE-250) # List all available recipes import? "contractile.just" @@ -20,7 +23,7 @@ default: # runtime surface is Deno + cartridge data (the ReScript host was removed). build: @echo "🧩 Validating cartridges (no compile step — Deno-only host)..." - deno task build + @deno run --allow-read scripts/validate-cartridges.js # Serve the workspace with live reload on change watch: @@ -56,12 +59,12 @@ setup: install # Format code fmt: @echo "✨ Formatting..." - deno task fmt + @deno fmt bin gui scripts src test # Lint code lint: @echo "🔍 Linting..." - deno task lint + @deno lint bin gui scripts src test # Serve the browser correspondence workspace (multi-pane, cartridge-driven) gui: @@ -73,24 +76,25 @@ gui: # Run all tests (cartridge invariants + GUI smoke test) test: @echo "🧪 Running tests..." - deno task test + @deno run --allow-read test/run_all.js + @deno run --allow-read test/gui_smoke_test.js # Run just the GUI smoke test (server handler: shell, bundle, cartridge API) test-gui: @echo "🖥️ GUI smoke test..." - @deno task test:gui + @deno run --allow-read test/gui_smoke_test.js # === VALIDATION === # Run full validation validate: @echo "🔍 Validating project..." - deno task validate + @just validate-cartridges # Validate cartridges against the correspondence schema (Deno + ajv) validate-cartridges: @echo "🧩 Validating cartridges..." - @deno run -A --no-lock scripts/validate-cartridges.js + @deno run --allow-read scripts/validate-cartridges.js # Validate RSR compliance validate-rsr: @@ -107,25 +111,35 @@ validate-structure: @echo "📁 Checking project structure..." @test -d src || (echo "❌ Missing src/" && exit 1) @test -d cartridges || (echo "❌ Missing cartridges/" && exit 1) - @test -f deno.json || (echo "❌ Missing deno.json" && exit 1) + @test -d .machine_readable/descriptiles || (echo "❌ Missing .machine_readable/descriptiles/ (RSR 3.1.1)" && exit 1) + @test ! -d .machine_readable/6a2 || (echo "❌ Retired .machine_readable/6a2/ still present (RSR 3.1.1)" && exit 1) + @test -d www/.well-known || (echo "❌ Missing www/.well-known/ (RSR 2.2.1 canonical location)" && exit 1) @echo "✅ Project structure valid" # Validate documentation +# Community-health files: AsciiDoc is canonical here (owner ruling 2026-10-10, +# #78); .md variants are also accepted so a future conversion cannot fail this. +# Mirrors the RSR v2.0 SSOT any_of detectors (2.1.x) — the normative oracle is +# hypatia:rsr-conformance; this recipe is a local, non-normative mirror. validate-docs: @echo "📚 Checking documentation..." - @test -f README.adoc || (echo "❌ Missing README.adoc" && exit 1) - @test -f CONTRIBUTING.adoc || (echo "❌ Missing CONTRIBUTING.adoc" && exit 1) - @test -f CODE_OF_CONDUCT.md || (echo "❌ Missing CODE_OF_CONDUCT.md" && exit 1) - @test -f SECURITY.md || (echo "❌ Missing SECURITY.md" && exit 1) - @test -f MAINTAINERS.adoc || (echo "❌ Missing MAINTAINERS.adoc" && exit 1) - @test -f CHANGELOG.adoc || (echo "❌ Missing CHANGELOG.adoc" && exit 1) + @{ test -f README.adoc || test -f README.md; } || (echo "❌ Missing README (2.1.1)" && exit 1) + @{ test -f CONTRIBUTING.adoc || test -f .github/CONTRIBUTING.md || test -f CONTRIBUTING.md; } || (echo "❌ Missing CONTRIBUTING (2.1.5)" && exit 1) + @{ test -f CODE_OF_CONDUCT.adoc || test -f .github/CODE_OF_CONDUCT.md || test -f CODE_OF_CONDUCT.md; } || (echo "❌ Missing CODE_OF_CONDUCT (2.1.4)" && exit 1) + @{ test -f SECURITY.adoc || test -f .github/SECURITY.md || test -f SECURITY.md; } || (echo "❌ Missing SECURITY (2.1.3)" && exit 1) + @{ test -f MAINTAINERS.adoc || test -f docs/MAINTAINERS.adoc; } || (echo "❌ Missing MAINTAINERS (2.1.7)" && exit 1) + @{ test -f CHANGELOG.adoc || test -f CHANGELOG.md; } || (echo "❌ Missing CHANGELOG (2.1.6)" && exit 1) + @{ test -f GOVERNANCE.adoc || test -f docs/GOVERNANCE.adoc || test -f .github/GOVERNANCE.md; } || (echo "❌ Missing GOVERNANCE (2.1.8)" && exit 1) @test -f CLAUDE.md || (echo "❌ Missing CLAUDE.md" && exit 1) + @{ test -f 0-AI-MANIFEST.a2ml || test -f 0-AI-MANIFEST.deed; } || (echo "❌ Missing AI manifest front door (2.3.1)" && exit 1) @echo "✅ Documentation complete" # Validate security files validate-security: @echo "🛡️ Checking security files..." - @test -f SECURITY.md || (echo "❌ Missing SECURITY.md" && exit 1) + @{ test -f SECURITY.adoc || test -f .github/SECURITY.md || test -f SECURITY.md; } || (echo "❌ Missing SECURITY policy (2.1.3)" && exit 1) + @for f in security.txt ai.txt humans.txt; do if [ -f "www/.well-known/$f" ]; then :; elif [ -f ".well-known/$f" ]; then echo "⚠️ legacy root .well-known/$f — canonical is www/.well-known/"; else echo "❌ Missing www/.well-known/$f (2.2.1)"; exit 1; fi; done + @bash www/tests/check-wellknown.sh @echo "✅ Security files present" # Validate licenses diff --git a/README.adoc b/README.adoc index 61c56fe..9e61b14 100644 --- a/README.adoc +++ b/README.adoc @@ -208,8 +208,9 @@ nextgen-languages-evangeliser/ +-- test/run_all.js # Cartridge invariant tests (Deno) +-- scripts/ # Deno utilities (cartridge validator, GUI launcher) +-- docs/theory/CORRESPONDENCE-MODEL.adoc # Canonical design spec -+-- abi.ipkg # Idris2 package (typecheck the ABI) -+-- deno.json # Deno configuration ++-- src/interface/abi.ipkg # Idris2 package (typecheck the ABI) ++-- www/.well-known/ # RFC 9116 security.txt + ai.txt + humans.txt (canonical) ++-- .machine_readable/descriptiles/ # STATE / META / ECOSYSTEM / AGENTIC / NEUROSYM / PLAYBOOK +-- Justfile # Task orchestration +-- config.ncl # Nickel configuration +-- CLAUDE.md # AI context @@ -314,7 +315,7 @@ If you use this project in academic research, please cite: * *PanLL / eNSAID*: the downstream contact layer this engine feeds * *Makaton*: inspiration for the glyph view-layer * *Deno Team*: for the excellent runtime -* *Contributors*: see link:.well-known/humans.txt[humans.txt] +* *Contributors*: see link:www/.well-known/humans.txt[humans.txt] == Status diff --git a/SECURITY.adoc b/SECURITY.adoc index 95c7ba7..4ae0a35 100644 --- a/SECURITY.adoc +++ b/SECURITY.adoc @@ -1,5 +1,10 @@ == Security Policy +// Modernized 2026-10-10 (#78): the VS Code extension this policy was first +// written for was removed with the TypeScript eradication; the runtime +// surface is now Deno + cartridge data (offline CLI, browser workspace +// server). Extension-era content has been replaced with the current model. + === 🛡️ Security Philosophy Security is a *first-class concern* in Nextgen Languages Evangeliser. We @@ -8,7 +13,7 @@ follow a *10+ dimensional security model*: [arabic] . *Input Validation* - All user input is validated and sanitized . *Output Encoding* - Code transformations are safe from injection -. *Authentication* - VS Code workspace trust model +. *Authentication* - Not applicable (offline, local-first; no accounts) . *Authorization* - File system access controls . *Session Management* - No network sessions required (offline-first) . *Cryptography* - No cryptographic operations (not needed) @@ -25,7 +30,7 @@ If you discover a security vulnerability, please report it responsibly: *DO:* - ✅ Email security details to the maintainers (see MAINTAINERS.adoc) - ✅ Use encrypted email if possible (PGP key in -.well-known/security.txt) - ✅ Wait for response before public +www/.well-known/security.txt) - ✅ Wait for response before public disclosure (max 90 days) - ✅ Provide detailed reproduction steps *DON’T:* - ❌ Open public GitHub issues for vulnerabilities - ❌ @@ -49,29 +54,31 @@ Denial of service - Supply chain attacks ==== Threat Model -*In Scope:* - Malicious JavaScript/TypeScript files opened in VS Code - -Crafted pattern definitions - File system traversal attempts - Resource -exhaustion (DoS) +*In Scope:* - Crafted cartridge JSON data (schema-validated before use) - +Path traversal attempts in the workspace server and CLI - Resource +exhaustion (DoS) of the local workspace server - XSS in the browser +workspace (SafeDOM: no innerHTML; Hypatia CWE-79 gate) *Out of Scope:* - Physical access to developer machine - OS-level -vulnerabilities - VS Code API vulnerabilities - Network-based attacks -(extension is offline-first) +vulnerabilities - Network-based attacks on end users (the runtime surface +is offline-first; the workspace server binds locally for the browser) ==== Security Boundaries .... ┌─────────────────────────────────────────┐ -│ VS Code Workspace (Untrusted) │ -│ ├─ User JavaScript files │ -│ └─ Custom pattern definitions │ +│ Local Workspace (Untrusted inputs) │ +│ ├─ Community cartridge JSON data │ +│ └─ Browser origin consuming the GUI │ └─────────────────────────────────────────┘ - ↓ (sandboxed parsing) + ↓ (schema-validated loading) ┌─────────────────────────────────────────┐ -│ Nextgen Languages Evangeliser Extension │ -│ ├─ AST Parser (Babel, sandboxed) │ -│ ├─ Pattern Matcher (validated) │ -│ ├─ Webview (Content Security Policy) │ -│ └─ File System (read-only by default) │ +│ Nextgen Languages Evangeliser (Deno) │ +│ ├─ Cartridge loader (JSON-Schema 2020) │ +│ ├─ Workspace server (localhost bind) │ +│ ├─ GUI rendering (textContent only) │ +│ └─ File system (--allow-read, least │ +│ privilege; never -A) │ └─────────────────────────────────────────┘ .... @@ -79,13 +86,12 @@ vulnerabilities - VS Code API vulnerabilities - Network-based attacks [arabic] . *Input Sanitization* -* All JavaScript/TypeScript parsed via Babel AST (not eval) -* Pattern regexes validated for ReDoS attacks -* File paths validated against traversal +* Cartridge data validated against the JSON-Schema (draft 2020-12) before use +* No `eval` / `Function` constructors; GUI builds DOM via textContent only +* File paths validated against traversal in the CLI and workspace server . *Content Security Policy* -* Webview UI runs with strict CSP -* No inline scripts or styles -* No external resource loading +* Browser workspace serves local assets only — no external resource loading +* No `innerHTML` escape hatch in the renderer (SafeDOM by construction) . *Resource Limits* * Maximum file size: 10MB * Parser timeout: 5 seconds @@ -110,7 +116,7 @@ If enabled (opt-in only): - Aggregated, anonymous usage counts - Pattern category usage (no code snippets) - Performance metrics (timing only) - Crash reports (no PII) -All telemetry is: - Local-first (stored in VS Code settings) - +All telemetry is: - Local-first (stored on the user's machine only) - User-controlled (can be deleted anytime) - Transparent (see docs/TELEMETRY.md) @@ -159,7 +165,7 @@ dependency tree (<10 packages) ==== No Network Dependencies -This extension: - *Never* makes network requests - *Never* loads +This tool: - *Never* makes network requests at runtime - *Never* loads external resources - Works in air-gapped environments - No CDN dependencies @@ -170,14 +176,15 @@ dependencies * No man-in-the-middle vulnerabilities * Works in high-security environments -=== 🎯 VS Code Workspace Trust - -This extension respects VS Code’s workspace trust model: +=== 🎯 Least-Privilege Runtime Permissions -* *Untrusted Workspaces*: Limited functionality (read-only) -* *Trusted Workspaces*: Full pattern detection and transformation +The Deno runtime enforces a permission model the tool leans into: -See: https://code.visualstudio.com/docs/editor/workspace-trust +* *CLI* (`bin/evangeliser.js`): `--allow-read` only — it reads cartridges +* *Workspace server* (`gui/server.js`): `--allow-read` + `--allow-net` (local bind) +* *Launcher* (`scripts/launch-gui.sh`): adds `--allow-env` (port override) and + `--allow-run` (spawn the browser for `--open`) +* `-A` / `--allow-all` is banned repo-wide (CWE-250; Hypatia gate) === 📋 Security Checklist for Contributors @@ -212,9 +219,8 @@ See CONTRIBUTING.adoc for full guidelines. === 📚 Additional Resources -* https://owasp.org/[OWASP VS Code Extension Security] -* https://code.visualstudio.com/api/references/extension-manifest[VS -Code Extension Security Best Practices] +* https://owasp.org/[OWASP] +* https://docs.deno.com/runtime/fundamentals/security/[Deno Runtime Security Model] * https://www.nist.gov/cyberframework[NIST Cybersecurity Framework] === 🔄 Security Updates @@ -227,16 +233,16 @@ Security updates are released as soon as possible: * *Low*: Next regular release Subscribe to releases to stay informed: -https://github.com/Hyperpolymath/nextgen-languages-evangeliser/releases +https://github.com/hyperpolymath/nextgen-language-evangeliser/releases === 📞 Contact For security concerns: * *Email*: See MAINTAINERS.adoc -* *PGP Key*: See .well-known/security.txt -* *Security.txt*: RFC 9116 compliant file in .well-known/ +* *PGP Key*: See www/.well-known/security.txt +* *Security.txt*: RFC 9116 compliant file in www/.well-known/ (canonical) ''''' -*Last Updated*: 2024-11-22 *Policy Version*: 1.0 +*Last Updated*: 2026-10-10 *Policy Version*: 2.0 diff --git a/TOPOLOGY.adoc b/TOPOLOGY.adoc index 9dfff32..a564b20 100644 --- a/TOPOLOGY.adoc +++ b/TOPOLOGY.adoc @@ -26,7 +26,7 @@ nextgen-languages-evangeliser/ ├── config.ncl # Nickel configuration ├── docs/ # Documentation (AsciiDoc) ├── Justfile # Task runner recipes -└── deno.json # Deno module config +└── .machine_readable/ # Descriptiles (STATE/META/…), contractiles, agent instructions ---- == Data flow diff --git a/docs/RSR_COMPLIANCE.adoc b/docs/RSR_COMPLIANCE.adoc index 59ae1cb..c2604f1 100644 --- a/docs/RSR_COMPLIANCE.adoc +++ b/docs/RSR_COMPLIANCE.adoc @@ -35,14 +35,16 @@ toc::[] == 4. Complete documentation * `README.adoc`, `EXPLAINME.adoc`, `ROADMAP.adoc`, `CONTRIBUTING.adoc`, - `CODE_OF_CONDUCT.md`, `SECURITY.md`, `MAINTAINERS.adoc`, `CHANGELOG`, - `CLAUDE.md`, and the canonical design spec + `CODE_OF_CONDUCT.adoc`, `SECURITY.adoc`, `MAINTAINERS.adoc`, + `CHANGELOG.adoc`, `CLAUDE.md`, and the canonical design spec `docs/theory/CORRESPONDENCE-MODEL.adoc`. == 5. Security-first design -* `panic-attack` static-analysis gate (`just assail`); CodeQL; SHA-pinned GitHub - Actions; RFC 9116 `.well-known/security.txt`; no hardcoded secrets; HTTPS-only. +* `panic-attack` static-analysis gate (`just assail`); Hypatia neurosymbolic + scan (inline gate + pinned `hypatia-scan.yml` reusable); CodeQL; SHA-pinned + GitHub Actions; RFC 9116 `www/.well-known/security.txt`; least-privilege + Deno permissions (never `-A`); no hardcoded secrets; HTTPS-only. == 6. Open governance @@ -66,11 +68,15 @@ toc::[] == 9. Build reproducibility * No compile step for the runtime surface (Deno + data). Task orchestration via - *Justfile*; reproducible environments via *Guix* (primary) / *Nix* (fallback). + *Justfile*; reproducible environments via *Guix* (Nix was retired + estate-wide in 2026). == 10. `.well-known/` -* `security.txt` (RFC 9116), `ai.txt`, `humans.txt`. +* `security.txt` (RFC 9116), `ai.txt`, `humans.txt` — canonical location + `www/.well-known/` (rsr-template-repo#53); validated by + `www/tests/check-wellknown.sh` (`just validate-security`) and the + `Well-Known Standards` workflow. == 11. No vendor lock-in diff --git a/docs/plans/PLAN-issue-78-red-checks.adoc b/docs/plans/PLAN-issue-78-red-checks.adoc new file mode 100644 index 0000000..281ba58 --- /dev/null +++ b/docs/plans/PLAN-issue-78-red-checks.adoc @@ -0,0 +1,640 @@ +// SPDX-License-Identifier: CC-BY-SA-4.0 += Ultraplan — Issue #78: Deferred Red Checks (Hypatia neurosymbolic scan + RSR Compliance Check) +:toc: macro +:toclevels: 3 +:sectnums: +:docdate: 2026-10-10 +:status: ACCEPTED — executed 2026-10-10 (owner rulings DP-1..DP-4 applied; see §8) + +[.lead] +Remediation plan for the two non-required contexts that are red on `main` +(standing ruling 2026-09-15: a red non-required check is an issue with +acceptance criteria, not a merge blocker). Cross-cutting tracker: +`hyperpolymath/standards#994`. Authoritative inputs: the RSR v2.0 criteria +SSOT (`hyperpolymath/standards` `0-canon/rsr/rsr-criteria-v2.a2ml`, +2.1.0-draft), `hyperpolymath/rsr-template-repo` (canonical layout), and the +Hypatia rule sources (`hyperpolymath/hypatia` `lib/rules/*`). + +toc::[] + +== 1. Mission and acceptance criteria + +Issue #78 acceptance criteria: *both contexts green on `main`, or retired by +owner ruling.* + +[cols="2,3", options="header"] +|=== +|Context |Producer +|`Hypatia neurosymbolic scan` |`.github/workflows/static-analysis-gate.yml`, job `hypatia-scan` +|`RSR Compliance Check` |`.github/workflows/ci.yml`, job `rsr-compliance` +|=== + +Evidence of red on `main` at HEAD `1cdc1e5e`: + +* `CI` (RSR Compliance Check → step ``Check required files'') fails on the + daily schedule run every day 2026-10-03 → 2026-10-10 (runs 38016613696 etc.). +* `Static Analysis Gate` fails on the 2026-10-02 push (and on the dependabot + branch run 37932360065, 2026-10-09) at step ``Fail on critical security + findings'' — *Hypatia found 10 critical security issue(s)*. +* Every other context on `main` is green (Governance, Scorecard, CodeQL, + Secret Scanner, Mirror, Well-Known Standards, Workflow Security Linter, + Security Policy, Makefile Blocker, Runtime Policy, RSR Anti-Pattern Check). + +*Recommendation: fix, do not retire.* Both checks have live estate roles: +the Hypatia gate feeds the gitbot-fleet learning pipeline (its +`deposit-findings` job), and the RSR check is the repo's local mirror of the +normative oracle (`hypatia:rsr-conformance`). Retiring either diverges from +`rsr-template-repo`, which ships both. + +== 2. Root cause analysis (verified against rule sources) + +=== 2.1 Red #1 — `Hypatia neurosymbolic scan`: 10 criticals + +GitHub caps check-run annotations, so the visible list understates the +critical set. Reconstructed from the Hypatia rule sources +(`structural_drift.ex`, `workflow_audit.ex`, `code_safety.ex`), the 10 +criticals are: + +[cols="1,2,2,3", options="header"] +|=== +|n |Rule |Finding |Fix direction +|9 |`structural_drift/SD004` (critical) |Descriptiles in retired locations: +`.machine_readable/{META,STATE,ECOSYSTEM}.a2ml` (legacy s-expression dialect) +and `.machine_readable/6a2/{AGENTIC,ECOSYSTEM,META,NEUROSYM,PLAYBOOK,STATE}.a2ml` +(current record dialect). Canonical: `.machine_readable/descriptiles/` — +`6a2/` deprecated 2026-06-30 (SSOT 3.1.1). |Move + consolidate (§5.1) +|1 |`workflow_audit/missing_workflow` (critical via +`severity_for_workflow("hypatia-scan.yml")`) |Required workflow +`.github/workflows/hypatia-scan.yml` absent (SSOT 6.1.2, silver, universal; +template ships it, calling the standards reusable scan). |Add canonical +workflow (§5.2) +|=== + +Non-critical findings from the same scan (annotated `failure` because the +gate emits `::error` for high too; these do not currently gate, but the +reusable scan blocks on them once a baseline is committed, and they are real +defects): + +[cols="1,2,2,3", options="header"] +|=== +|Sev |Rule |Finding |Fix direction +|high |`code_safety/shell_download_then_run` (CWE-494) |`setup.sh` lines 144+156: +`curl -fsSL https://just.systems/install.sh \| bash` (×2) |Remove +download-and-execute (§5.3.1) +|high |`code_safety/js_innerhtml` (CWE-79) |`gui/app.js:41`: +`else if (k === "html") el.innerHTML = v;` — dead escape hatch; **no caller +passes `html:`** |Delete branch (§5.3.2) +|high |`code_safety/js_deno_all_perms` (CWE-250) |`scripts/validate-cartridges.js:5` +(comment): `deno run -A` — same pattern also at `Justfile:93` and +`scripts/launch-gui.sh:9` (not scanned, but same defect class) |Least-privilege +flags (§5.3.3) +|high |`workflow_audit/WF022` |`rsr-antipattern.yml`: inline-python heading regex +`re.search(r'TypeScript [Ee]xemptions', line)` unanchored — matches prose, can +silently walk the wrong section (the affinescript multi-week silent-gate +failure class, standards#183) |Anchor to `^#{1,4}\s+` (§5.3.4) +|high |`workflow_audit/WF017` |`instant-sync.yml`: +`peter-evans/repository-dispatch` with `token: ${{ secrets.FARM_DISPATCH_TOKEN }}` +and no presence gate — fails every run where the secret is absent (the +65-repo Instant Sync bug class, 2026-05-30 audit) |env-boolean gate idiom +(§5.3.5) +|medium ×10 |`workflow_audit/missing_timeout_minutes` (ERR-WF-013) |No +`timeout-minutes:` on: `instant-sync.yml/dispatch`, `guix-nix-policy.yml/check`, +`codeql.yml/analyze`, and **8 jobs** in `ci.yml` (test, security, +rsr-compliance, build, performance, docs, release) |Add proportional +timeouts (§5.3.6) +|=== + +Gate-design drift (context, not a finding): the inline `hypatia-scan` job in +`static-analysis-gate.yml` clones **floating Hypatia `main`** and has no +baseline mechanism; the estate control plane +(`standards/docs/decisions/2026-06-06-hypatia-estate-control-plane.adoc`) is +the *pinned* reusable `hypatia-scan-reusable.yml` (vetted scanner SHA input, +`.hypatia-baseline.json` + `scripts/apply-baseline.sh`, SARIF to the Security +tab). The template deliberately runs **both**: the inline gate feeds the +fleet learning pipeline, the reusable is the repo's independent security +scan. `static-analysis-gate.yml` here is byte-identical in shape to the +template's — *do not modify it* (drift from template); add the missing +`hypatia-scan.yml` instead. + +=== 2.2 Red #2 — `RSR Compliance Check`: stale gate vs. binding layout + +The `rsr-compliance` job checks a 2025-era file set; three estate migrations +moved on without it: + +[cols="2,2,3", options="header"] +|=== +|Gate demands |Repo reality |Binding ruling +|`CODE_OF_CONDUCT.md` (root) |`CODE_OF_CONDUCT.adoc` (root, after PR #62 +Markdown→AsciiDoc) |SSOT 2.1.4 (silver): `CODE_OF_CONDUCT.md` **or** +`.github/CODE_OF_CONDUCT.md`. Template ships full-content +`.github/CODE_OF_CONDUCT.md`. GitHub's community profile only recognises +`.md`. +|`SECURITY.md` (root) |`SECURITY.adoc` (root) |SSOT 2.1.3 (**bronze**, +universal): `SECURITY.md` or `.github/SECURITY.md`. Template ships +`.github/SECURITY.md`. +|`.well-known/{security,ai,humans}.txt` (root) |`www/.well-known/` (after PR +#72) |Template canonical is `www/.well-known/` (rsr-template-repo#53, +stage-5 sweep of ~270 repos; `scripts/migrate-wellknown-to-www.sh`; +`www/tests/check-wellknown.sh`). Root is legacy-tolerated during the +migration window. The repo is *correct*; the gate is stale. +|=== + +Same staleness in the local task runner: `just validate-rsr` fails +(`validate-docs` demands root `CODE_OF_CONDUCT.md`/`SECURITY.md`; +`validate-structure` demands `deno.json`, which has **never existed in this +repo's git history** — the `deno task {build,fmt,lint,test,test:gui}` recipes +have therefore never worked). And `wellknown-enforcement.yml` only inspects +root `.well-known/`, warns-only, exits 0 — a *vacuous gate* (the defect class +PR #67 was merged to eliminate). + +Known-bad data inside the migrated files (found in recon): + +* `www/.well-known/security.txt`: `Canonical:` and `Policy:` URLs name + **`nextgen-languages-evangeliser`** — the repository is + **`nextgen-language-evangeliser`** (no ``s''). `Canonical:` points at the + retired root path; `Policy:` points at `SECURITY.md`, which does not exist. +* `www/.well-known/ai.txt` references `AI.a2ml`, which does not exist. +* `README.adoc:317` and `SECURITY.adoc:28,237,238` and + `docs/RSR_COMPLIANCE.adoc` §5/§10 still link root `.well-known/` paths; + `docs/RSR_COMPLIANCE.adoc` §9 still offers ``Nix (fallback)'' — Nix was + deleted estate-wide (PR #59); §4 lists the community docs at root. +* `SECURITY.adoc` still describes the ``VS Code workspace trust model'' — + the extension was removed with the TypeScript eradication (#61). + +=== 2.3 Estate-level contradiction to report upstream (not fix here) + +The normative oracle's built-in detector for SSOT 2.2.1 (`wellknown-core`) +checks only **root** `.well-known/` (`rsr_conformance.ex` `all_graded`), and +the SSOT's own `template_ref` says `.well-known/` — while the template repo +itself ships them **only** at `www/.well-known/`. The oracle therefore fails +the canonical template for 2.2.1. The detector (`structural_drift/SD020`) +named by the SSOT is not implemented in the scanner. This is exactly the +``rules diverged from the norm'' case standards#994 exists for: *report +upstream; do not regress this repo's layout back to root.* + +== 3. Binding constraints (must not violate) + +. *RSR v2.0 SSOT* (`0-canon/rsr/rsr-criteria-v2.a2ml`, 2.1.0-draft) — the sole +normative source; the one normative checker is `hypatia:rsr-conformance`; +all other checkers (ours included) are non-normative mirrors and should be +*generated in its shape* (any_of/present/all_graded), never invent stricter +demands. +. *rsr-template-repo layout* — community-health files live at `.github/*.md` +(full content, `CC-BY-SA-4.0` SPDX comment headers); prose docs stay AsciiDoc; +`www/.well-known/` canonical; descriptiles at `.machine_readable/descriptiles/`; +`hypatia-scan.yml` = pinned call into the standards reusable. +. *Licence discipline* (CLAUDE.md, owner-managed): MPL-2.0 code / +CC-BY-SA-4.0 docs; add SPDX to **new** files; **never** bulk-sweep or +relicence existing headers — existing header drift (e.g. `SECURITY.adoc`, +`CODE_OF_CONDUCT.adoc`, `GOVERNANCE.adoc` lack SPDX lines) is FLAG-ONLY to +the owner. +. *Language policy* (LANGUAGE-POLICY.adoc §1 via `runtime-policy.yml`): Bun +tier 1, Deno tier 2 (**grandfathered** — this repo's Deno surface may stay), +TypeScript/Node/npm/Makefile/Python/Go banned; JS glue allowed per +`.claude/CLAUDE.md` exemptions. +. *Honesty doctrine*: no vacuous gates, no `\|| echo SKIP` silent-green (SSOT +6.1.5), ``author now, verify in CI — never claim a green build you did not +run''. +. *Baseline discipline*: prefer fix-the-code; `.hypatia-baseline.json` +entries need `expires_at` + `tracking_issue`; `.hypatia-ignore` entries need +a stated reason and are for whole-path exemptions only. +. *Workflow pinning*: SHA-pinned actions; reusable workflows pinned to a +40-hex standards commit; new/edited workflows must pass `workflow-linter.yml` +and keep `permissions: contents: read` floors. +. *Governance workflow pin* (`governance.yml` → standards +`governance-reusable.yml@81dbf2dd…`) is green — untouched. + +== 4. Strategy + +One branch (this session's `arena/f31fb256-nextgen-language-evangeliser`), +one PR, four logical commits, ordered so no intermediate state is redder +than the base: + +[cols="1,3,2", options="header"] +|=== +|Phase |Content |Kills +|1 |Descriptile relocation + reference repair + add canonical +`hypatia-scan.yml` |10/10 criticals → `Hypatia neurosymbolic scan` green +|2 |High/medium hygiene: `setup.sh`, `gui/app.js`, Deno perms, WF022, WF017, +timeouts |5 highs + 11 mediums (future-proofing the reusable gate & baseline +promotion) +|3 |RSR realignment: `.github/` community files, oracle-shaped `rsr-compliance` +job, Justfile repair, well-known enforcement + URL fixes, docs consistency +|`RSR Compliance Check` green; `just validate-rsr` honest; vacuous +well-known gate repaired +|4 |Land, verify on `main`, close #78 with evidence, re-point dependabot PR +#79, file upstream reports |Issue acceptance criteria +|=== + +Deferred (owner-gated, §8) and out-of-scope backlog: §9. + +== 5. Phase detail (exact changes) + +=== 5.1 Phase 1a — descriptile relocation (SD004 ×9) + +Canonical target: `.machine_readable/descriptiles/`. + +[cols="2,2,3", options="header"] +|=== +|Source |Disposition |Rationale +|`.machine_readable/6a2/AGENTIC.a2ml` |`git mv` → `descriptiles/AGENTIC.a2ml` |Record dialect, sole copy +|`.machine_readable/6a2/NEUROSYM.a2ml` |`git mv` → `descriptiles/NEUROSYM.a2ml` |Record dialect, sole copy +|`.machine_readable/6a2/PLAYBOOK.a2ml` |`git mv` → `descriptiles/PLAYBOOK.a2ml` |Record dialect, sole copy +|`.machine_readable/6a2/{META,STATE,ECOSYSTEM}.a2ml` |`git mv` → `descriptiles/` **as the base content** |Record dialect = the dialect SSOT 3.2.1 requires to parse +|`.machine_readable/{META,STATE,ECOSYSTEM}.a2ml` |**Delete** after a line-by-line fact reconciliation against the record-dialect twins |Legacy s-expression dialect; both copies dated 2026-06-18 — reconcile, port any s-expr-only facts (e.g. root STATE's `route-to-mvp` milestone detail) into the record version, bump `last-updated` to the migration date with a header comment |`.machine_readable/6a2/` (directory) |Removed (empty after moves) |Prevents SD023 divergence re-trigger +|=== + +Untouched: `ENSAID_CONFIG.a2ml` (not a descriptile; live references in +README/EXPLAINME/CORRESPONDENCE-MODEL), `contractiles/`, `agent_instructions/`, +`bot_directives/`, `root-allow.txt`. + +Reference repair (SD007 scans `0-AI-MANIFEST.a2ml`, `README.adoc`, +`.claude/CLAUDE.md`; SD024 scans workflows/scripts/Justfile for retired +paths — currently none, keep it that way): + +* `0-AI-MANIFEST.a2ml` — canonical-locations section: the six descriptiles +``MUST exist in `.machine_readable/descriptiles/` ONLY''; anchor path → +`.machine_readable/descriptiles/anchors/ANCHOR.a2ml` (SSOT 3.1.8). +* `HANDOFF.adoc:9` — `.machine_readable/STATE.a2ml` → +`.machine_readable/descriptiles/STATE.a2ml`. +* `CLAUDE.md:88` — historical ``6a2 manifests'' mention → ``descriptile +manifests'' (keep the ✅ history honest: note the 2026-10 relocation). +* `.machine_readable/descriptiles/STATE.a2ml` internal text (it narrates +``6a2 manifests'' as merged history) — update the working-features wording. + +=== 5.2 Phase 1b — add `.github/workflows/hypatia-scan.yml` (missing_workflow) + +Template-shaped (the template's file, minted for this repo): + +[source,yaml] +---- +# SPDX-License-Identifier: MPL-2.0 +name: Hypatia Security Scan +on: + push: + branches: [main, master, develop] + pull_request: + branches: [main, master] + schedule: + - cron: '0 0 * * 0' + workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +permissions: + actions: read # reusable staleness check reads workflow runs + contents: read + security-events: write +jobs: + hypatia: + uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@ +---- + +Pin choice (§8, DP-3): `` = +`10e0b6ffb979f0a27fe58206512055133e67460a` (latest standards commit touching +the reusable, 2026-10-01, ``fix(gates): pin what reusables fetch'') — +*recommended*; alternative: the template's own pin +`da2c748aad55c1a1dcba00b60fe4a35017bc6540` (2026-09-10) for byte-parity with +the template. + +Behaviour on this repo (no `.hypatia-baseline.json`, no +`scripts/apply-baseline.sh`): the reusable scan is **ADVISORY** — it reports +criticals loudly in the summary and Security tab but does not fail; blocking +promotion happens automatically the day a baseline lands (standards#399/#437 +mechanism). Scanner is pinned inside the reusable (vetted SHA input default), +so this check is reproducible — unlike the floating inline gate. + +=== 5.3 Phase 2 — highs and mediums + +==== 5.3.1 `setup.sh` (CWE-494 ×2) + +Replace both `curl … | bash` fallbacks with **no download-and-execute**: +package managers stay (dnf/apt/pacman/apk/brew/scoop/winget/rpm-ostree/guix); +where `just` is unavailable, print precise manual-install instructions +(pinned release URL + ``verify the checksum published by the project before +extracting'') and continue with a warning (the rest of setup does not hard- +require `just` at script time). *Alternative (DP-2):* pinned-version tarball ++ SHA-256 constant embedded in `setup.sh` — stronger, but the checksum must +be fetched by a networked environment (this sandbox cannot reach release +asset hosts); recommend revisiting when the owner can mint the constant. +No rule-dodge rewrites (`curl -o tmp && bash tmp` is banned by intent, not +just by regex). + +==== 5.3.2 `gui/app.js` (CWE-79) + +Delete line 41 (`else if (k === "html") el.innerHTML = v;`). Verified: zero +callers pass `html:`; every render path uses `text:`/`textContent` or +attributes. Add a one-line comment: SafeDOM-by-construction — no innerHTML +escape hatch (Hypatia CWE-79). + +==== 5.3.3 Deno least privilege (CWE-250) + +* `scripts/validate-cartridges.js:5` comment → `deno run --allow-read scripts/validate-cartridges.js` +* `Justfile:93` (`validate-cartridges` recipe) → `@deno run --allow-read scripts/validate-cartridges.js` +* `scripts/launch-gui.sh:9` → `exec deno run --allow-read --allow-net gui/server.js --open "$@"` + (verify `gui/server.js` needs: file reads + HTTP listen; add `--allow-env` + only if it reads env vars — check `Deno.env` usage during implementation). +* Shebangs of `bin/evangeliser.js`, `test/*.js` already least-privilege + (`--allow-read`) — untouched. + +==== 5.3.4 `rsr-antipattern.yml` (WF022) + +Anchor the exemption-table heading detection: +`re.search(r'TypeScript [Ee]xemptions', line)` → +`re.search(r'^#{1,4}\s+.*TypeScript [Ee]xemptions', line)` + a comment +naming the intended heading (`.claude/CLAUDE.md` ``### TypeScript +Exemptions'' — verified present, line 88; the anchored regex matches it and +no longer matches prose). + +==== 5.3.5 `instant-sync.yml` (WF017 + ERR-WF-013) + +Estate-recognised env-boolean gate idiom (measured on gitbot-fleet's copy): + +[source,yaml] +---- +jobs: + dispatch: + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + HAS_FARM_TOKEN: ${{ secrets.FARM_DISPATCH_TOKEN != '' }} + steps: + - name: Trigger Propagation + id: dispatch + if: env.HAS_FARM_TOKEN == 'true' + continue-on-error: true + uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 + … +---- + +`Confirm` step: handle `outcome == 'skipped'` (secret absent → `::notice:: +FARM_DISPATCH_TOKEN not set — propagation skipped`) distinctly from +`failure` (keep the rotate-token warning). + +==== 5.3.6 `timeout-minutes` sweep (ERR-WF-013 ×10) + +[cols="2,2", options="header"] +|=== +|Job |Timeout +|`ci.yml`: test |45 +|`ci.yml`: security |15 +|`ci.yml`: rsr-compliance |10 +|`ci.yml`: build |30 +|`ci.yml`: performance |30 +|`ci.yml`: docs |15 +|`ci.yml`: release |45 +|`codeql.yml`: analyze |30 +|`guix-nix-policy.yml`: check |10 +|`instant-sync.yml`: dispatch |10 (§5.3.5) +|=== + +=== 5.4 Phase 3 — RSR realignment + +==== 5.4.1 Community-health files (SSOT 2.1.3 bronze / 2.1.4, 2.1.5 silver) + +*Owner ruling 2026-10-10 (DP-1): keep the AsciiDoc files; no `.md` +conversion.* Therefore: + +* `SECURITY.adoc`, `CODE_OF_CONDUCT.adoc`, `CONTRIBUTING.adoc` stay where +they are. `SECURITY.adoc` gets a content repair instead of a move: the +retired VS Code-extension threat model is replaced with the current +Deno + cartridge-data surface; `.well-known/` references point at +`www/.well-known/`; the wrong-name releases URL is fixed; Policy Version +bumped. +* The SSOT detectors 2.1.3/2.1.4/2.1.5 list only `.md` variants, so the +*non-normative* checks in this repo (`ci.yml` rsr-compliance, Justfile +`validate-docs`/`validate-security`) accept **both** `.adoc` (canonical +here) and `.md` — and the detector lag goes upstream (§5.5): GitHub's +community-profile recognition of `.adoc` is the owner's own feature requests +(github/orgs/community discussions #181843, #189645). +* Referrers repaired: `CONTRIBUTING.adoc` links (→ `.adoc` files), +`docs/RSR_COMPLIANCE.adoc` §4 (→ `.adoc`), `www/.well-known/security.txt` +Policy URL (§5.4.4). + +==== 5.4.2 `ci.yml` `rsr-compliance` — regenerate in oracle shape + +Rewrite ``Check required files'' as a non-normative mirror of the +rsr-criteria-v2 detectors (any_of semantics; www-canonical well-known; +honest failures; no silent-green): + +[source,bash] +---- +any_of() { for f in "$@"; do [ -f "$f" ] && return 0; done; return 1; } +# 2.1.1/2.1.6/2.1.7/2.1.8/2.3.1 + bronze basics +any_of README.adoc README.md || fail "README (2.1.1)" +any_of CHANGELOG.adoc CHANGELOG.md || fail "CHANGELOG (2.1.6)" +any_of MAINTAINERS.adoc docs/MAINTAINERS.adoc || fail "MAINTAINERS (2.1.7)" +any_of GOVERNANCE.adoc docs/GOVERNANCE.adoc .github/GOVERNANCE.md || fail "GOVERNANCE (2.1.8)" +any_of 0-AI-MANIFEST.a2ml 0-AI-MANIFEST.deed || fail "AI manifest (2.3.1)" +test -f CLAUDE.md || fail "CLAUDE.md" +# 2.1.3 bronze / 2.1.4 / 2.1.5 silver — .md community files, root or .github/ +any_of SECURITY.md .github/SECURITY.md || fail "SECURITY.md (2.1.3)" +any_of CODE_OF_CONDUCT.md .github/CODE_OF_CONDUCT.md || fail "CODE_OF_CONDUCT.md (2.1.4)" +any_of CONTRIBUTING.md .github/CONTRIBUTING.md CONTRIBUTING.adoc || fail "CONTRIBUTING (2.1.5)" +# 2.1.2 / 7.1.2 licensing +test -f LICENSE && test -f LICENSES/MPL-2.0.txt && test -f LICENSES/CC-BY-SA-4.0.txt || fail "licences" +# 1.1.2/1.1.3 build system +{ test -f Justfile || test -f justfile; } && ! test -f Makefile || fail "Justfile (1.1.2/1.1.3)" +# 2.2.1 well-known — canonical www/, legacy root tolerated with warning +for f in security.txt ai.txt humans.txt; do + if [ -f "www/.well-known/$f" ]; then : + elif [ -f ".well-known/$f" ]; then warn "legacy root .well-known/$f — canonical is www/" + else fail "www/.well-known/$f (2.2.1)" + fi +done +# 3.1.1 descriptiles present, 6a2 retired +test -d .machine_readable/descriptiles && ! test -d .machine_readable/6a2 || fail "descriptiles (3.1.1)" +---- + +Plus `timeout-minutes: 10`, and a job comment stating the normative oracle +is `hypatia:rsr-conformance` and this check mirrors criteria ids (so the +next person regenerates rather than hand-drifts it). + +==== 5.4.3 `Justfile` repair (local parity, honest failures) + +* `validate-docs` / `validate-security` — same any_of sets as §5.4.2 +(`.github/*.md` accepted; `www/.well-known/` canonical). +* `validate-structure` — drop the `deno.json` demand (file never existed); +require `src/`, `cartridges/`, `www/.well-known/`, +`.machine_readable/descriptiles/`. +* Repair the never-worked `deno task` recipes with direct commands: `build` +→ `deno run --allow-read scripts/validate-cartridges.js`; `fmt` → `deno fmt`; +`lint` → `deno lint`; `test` → `deno run --allow-read test/run_all.js`; +`test:gui` → `deno run --allow-read test/gui_smoke_test.js`. +* Header comment: replace ``Use Deno, not npm/bun'' with the current +LANGUAGE-POLICY tiering (Bun tier 1; Deno tier 2, grandfathered here). + +==== 5.4.4 Well-known enforcement + URL truth + +* `wellknown-enforcement.yml` → port the template's +`dot-wellknown-enforcement.yml` semantics: `www/.well-known/` canonical, +legacy root = warning, **error when security.txt is absent everywhere** +(kills the vacuous exit-0), RFC 9116 Contact/Expires/expiry validation +against the www copy, path triggers include `www/.well-known/**`. Keep the +file name (check context name unchanged: ``Well-Known Standards (RFC 9116 + +RSR)''). +* `www/.well-known/security.txt`: `Canonical:` → +`https://github.com/hyperpolymath/nextgen-language-evangeliser/blob/main/www/.well-known/security.txt`; +`Policy:` → +`https://github.com/hyperpolymath/nextgen-language-evangeliser/blob/main/SECURITY.adoc` +(fixes the wrong repo name *and* the dead paths). +* `www/.well-known/ai.txt`: drop the `AI.a2ml` reference → point at +`CLAUDE.md` / `.claude/CLAUDE.md`. +* Adopt the template's `www/tests/check-wellknown.sh` (verbatim, minted) so +`just` and CI can run the canonical validator. + +==== 5.4.5 Documentation consistency sweep + +* `README.adoc:317` → `link:www/.well-known/humans.txt[humans.txt]`. +* `docs/RSR_COMPLIANCE.adoc` — §4 community-file paths (`.github/*.md`), §5 +`www/.well-known/security.txt`, §9 delete ``Nix (fallback)'' (retired +estate-wide, PR #59), §10 `www/.well-known/`; note the Deno surface is +tier-2-grandfathered. +* `CLAUDE.md` — Project Structure: remove the `deno.json` line, add +`.machine_readable/descriptiles/`; Technology Stack / Language Policy: +align with LANGUAGE-POLICY tiering (Bun 1st, Deno 2nd grandfathered) as PR +#63 did for `.claude/CLAUDE.md`; note community files live at `.github/*.md`. +* `CHANGELOG.adoc` — **do not** rewrite historical entries (append-only); +add a new entry describing this change set. +* `HANDOFF.adoc`, `EXPLAINME.adoc` — path touch-ups where they name moved +files. + +=== 5.5 Phase 4 — landing and issue lifecycle + +. Push branch → open PR (title +`fix(ci): clear the deferred red checks — descriptiles, hypatia-scan, RSR realignment (#78)`), +body naming both contexts, the 10-critical breakdown, and the binding +citations (SSOT ids, template refs, standards#994 link). +. Watch PR checks to green: `Static Analysis Gate` (hypatia job: 0 +criticals), `CI` (rsr-compliance pass), new `Hypatia Security Scan` +(advisory, expect a green run with 0 criticals post-fix), `Governance`, +`Workflow Security Linter` (new/edited workflows), `RSR Anti-Pattern Check` +(re-verify the anchored parser still reads the exemption table), `Well-Known +Standards`, `Runtime Policy`, `Makefile Blocker`, `CodeQL`, `Secret Scanner`. +. Merge (owner flow; signed commits per CONTRIBUTING §Signed commits); +confirm both contexts green on `main` (the nightly `CI` schedule run is the +standing proof; trigger `workflow_dispatch` runs where available for +immediate evidence). +. Comment on #78 with the run IDs proving green-on-`main`, then close. +. Dependabot PR #79 (open): update branch / re-run checks so it inherits +green; per the standing ruling it was never blocked, but leaving it red +re-files the same debt. +. Upstream reports (comment on standards#994 or new issues, per owner +preference): +.. oracle detector 2.2.1 is root-only while the template canonical is +`www/.well-known/` (the oracle fails the template itself); SSOT +`template_ref` for 2.2.1 stale; `structural_drift/SD020` unimplemented. +.. SSOT detectors 2.1.3/2.1.4/2.1.5 list only `.md` variants; owner ruling +2026-10-10 keeps `.adoc` community files canonical here — propose the +`.adoc` variants as an additive SSOT patch (versioning rule-minor permits +detector additions; GitHub-recognition context: orgs/community discussions +#181843 / #189645). +.. `static-analysis-gate.yml` (template-owned) clones floating Hypatia +`main` — control-plane says pin; suggest the template adopt the vetted-SHA +clone or delegate. +.. GitHub's 10-annotation cap made the gate's critical set unreadable from +the UI; suggest the inline gate emit a per-rule count summary (upstream +template change). + +== 6. Verification plan (honest: author now, verify in CI) + +* *Local (sandbox):* re-run the rewritten `rsr-compliance` script; simulate +SD004 (assert the seven names exist only under `descriptiles/`); simulate +SD023/SD024/SD007 by grep; YAML-parse every touched workflow (`python3 +-yaml`); run `www/tests/check-wellknown.sh`; run `just validate-rsr` **if** +`just`+`deno` are installable here, else say so and defer to CI. Hypatia +itself cannot be built here (hex.pm unreachable) — the PR run is ground +truth for the scan. +* *CI:* the check matrix in §5.5 step 2, then green-on-`main` confirmation. +* *Regression watch:* the nightly scheduled `CI` run and the weekly +`Hypatia Security Scan` cron for one cycle post-merge. + +== 7. Risks and mitigations + +[cols="2,3,3", options="header"] +|=== +|Risk |Impact |Mitigation +|Floating Hypatia `main` in `static-analysis-gate.yml` adds new critical +rules after our fix |Gate re-reddens for reasons unrelated to any commit +|All 10 current criticals fixed at the root (no ignores/baseline); upstream +report §5.5.6.b proposes pinning the template's clone; optionally adopt +`.hypatia-baseline.json` + `scripts/apply-baseline.sh` later for the +reusable gate (blocking, expiry-tracked) +|Deleting `SECURITY.adoc`/`CODE_OF_CONDUCT.adoc` collides with the PR #62 +``migrate to AsciiDoc'' precedent |Owner disagreement on file set |DP-1 +below; SSOT 2.1.3/2.1.4 + template + GitHub community-profile recognition +are the binding evidence; conversion is a *move*, not a relicence — SPDX +headers on the new `.md` files follow the template (CC-BY-SA-4.0) +|S-expr descriptile copies contain facts missing from the record-dialect +twins |Silent knowledge loss |Line-by-line reconciliation before delete; +reconciled facts land in the record files with `last-updated` bumped; git +history preserves the originals +|Anchored WF022 regex breaks the exemption-table parser |`RSR Anti-Pattern +Check` false-fails or silently ignores exemptions |Heading verified +(`.claude/CLAUDE.md:88`); parser unit-tested locally with a fixture before +push +|New `hypatia-scan.yml` check name adds a 5th Hypatia spelling estate-wide +(standards#994 §2) |Required-context bookkeeping churn |Name matches the +template exactly (``Hypatia Security Scan'' / job ``Hypatia Neurosymbolic +Analysis'') — convergence, not divergence +|`security.txt` edits re-trigger `Well-Known Standards` |None if content +valid |Expires 2027-12-31 stays; Contact present; validator ported from +template +|=== + +== 8. Decision points (owner ruling requested) + +[cols="1,3,2", options="header"] +|=== +|ID |Question |Recommendation +|DP-1 |Community files: conversion to `.github/*.md`, or keep `.adoc`? +|*RULED (owner, 2026-10-10): stick with `.adoc`* — SSOT detector lag +reported upstream (§5.5) +|DP-2 |`setup.sh` curl\|bash: remove remote-exec fallback, or pin a +release + SHA-256? |*RULED: remove the fallback* (a checksum constant can be +minted later from a networked environment) +|DP-3 |`hypatia-scan.yml` reusable pin |*RULED: latest* — +`10e0b6ffb979f0a27fe58206512055133e67460a` +|DP-4 |Landing shape: one PR or two? |One PR (this session's branch is fixed +to a single ref; #78 closes once both contexts are green) +|DP-5 |Upstream reports: comment on standards#994, or discrete issues? |Drafted +in §5.5; posting left to the owner (cross-repo action) +|=== + +== 9. Out of scope for #78 (recorded so it is not lost) + +* *Gold-tier scorecard uplift* (SSOT gold criteria, none currently gating): +`descriptiles/anchors/ANCHOR.a2ml` (3.1.8), `CLADE.a2ml` (3.1.9), +`.machine_readable/rsr-profile.a2ml` (3.2.2), `dogfood-gate.yml` (6.2.1), +root `*_chora.deed` (template canary + rhodibot expectations). +* *A2ML-vs-deed doctrine*: the template's rhodibot canary treats **every** +`.a2ml` as drift (``facts belong in the repo deed'') while the SSOT's 3.x +criteria are descriptile-`.a2ml`-shaped — an estate contradiction to raise +with the owner before any deed migration here. +* *actions.lock adoption* (``managed by gh actions-lock'' headers on +template workflows; this repo pins inline without a lock). +* *Bun migration* of the grandfathered Deno surface (#61 Step-3 successor +work; Deno is permitted tier 2). +* *SPDX header drift* on existing files (`SECURITY.adoc`, +`CODE_OF_CONDUCT.adoc`, `GOVERNANCE.adoc`, others) — FLAG-ONLY per CLAUDE.md; +list handed to the owner, no sweep. +* `.machine_readable/root-allow.txt` is inert here (no +`check-root-shape.sh`); reconcile if/when the root-shape checker is adopted. +* `pages.yml` builds from `README.md` (absent — falls back to a stub +`index.md`); the Ddraig pipeline likely wants `README.adoc` or the `www/` +bundle — separate fix. + +== 10. Execution checklist (condensed) + +* [ ] P1a `git mv` 6a2 → `descriptiles/`; reconcile + delete s-expr twins; + repair references (manifest, HANDOFF, CLAUDE.md, STATE internals) +* [ ] P1b add `.github/workflows/hypatia-scan.yml` (pinned reusable, DP-3) +* [ ] P2 `setup.sh` (DP-2) · `gui/app.js` · deno perms ×3 · WF022 anchor · + WF017 gate · timeouts ×11 +* [ ] P3 `.github/{SECURITY,CODE_OF_CONDUCT,CONTRIBUTING}.md` (DP-1) · + `ci.yml` rsr-compliance rewrite · Justfile repair · + wellknown-enforcement port · security.txt/ai.txt URL fixes · + adopt `www/tests/check-wellknown.sh` · docs sweep · CHANGELOG entry +* [ ] P4 local sims → push → PR (#78) → green matrix → merge → green on + `main` → close #78 with run-ID evidence → refresh PR #79 → upstream + reports (DP-5) diff --git a/gui/app.js b/gui/app.js index 0ddd9cb..1665fa8 100644 --- a/gui/app.js +++ b/gui/app.js @@ -32,13 +32,16 @@ const state = { items: [], view: "side-by-side", kind: "all", query: "", selecte const $ = (id) => document.getElementById(id); +// SafeDOM by construction: text goes through textContent and everything else +// through setAttribute. There is deliberately NO innerHTML escape hatch — +// an `html:` option here would be a stored-DOM XSS seam (CWE-79; Hypatia +// code_safety/js_innerhtml). If rich markup is ever needed, build nodes. function node(tag, opts = {}, kids = []) { const el = document.createElement(tag); for (const [k, v] of Object.entries(opts)) { if (v == null) continue; if (k === "class") el.className = v; else if (k === "text") el.textContent = v; - else if (k === "html") el.innerHTML = v; else el.setAttribute(k, v); } for (const kid of [].concat(kids)) if (kid != null) el.append(kid); diff --git a/scripts/launch-gui.sh b/scripts/launch-gui.sh index e9e0212..f54ed0c 100755 --- a/scripts/launch-gui.sh +++ b/scripts/launch-gui.sh @@ -6,4 +6,9 @@ set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT" -exec deno run -A --no-lock gui/server.js --open "$@" +# Least-privilege Deno permissions (CWE-250 — never `-A`): +# --allow-read cartridge data + gui assets +# --allow-net the workspace HTTP server +# --allow-env honour EVANGELISER_GUI_PORT (server.js degrades gracefully without it) +# --allow-run spawn the browser for --open (sensible-browser / gio / xdg-open) +exec deno run --allow-read --allow-net --allow-env --allow-run gui/server.js --open "$@" diff --git a/scripts/validate-cartridges.js b/scripts/validate-cartridges.js index e8bfbbe..bb481e9 100644 --- a/scripts/validate-cartridges.js +++ b/scripts/validate-cartridges.js @@ -2,7 +2,7 @@ // Validate every *.cartridge.json under cartridges/ against the correspondence // schema (JSON Schema draft 2020-12). Deno + ajv. // -// deno run -A --no-lock scripts/validate-cartridges.js +// deno run --allow-read scripts/validate-cartridges.js // just validate-cartridges // // Exits non-zero if any cartridge fails — suitable for CI. diff --git a/setup.sh b/setup.sh index 3e8f6ef..cc62215 100755 --- a/setup.sh +++ b/setup.sh @@ -5,8 +5,10 @@ # Detects your shell, platform, and installs prerequisites. # Then hands off to `just setup` for project-specific configuration. # -# Usage: -# curl -fsSL https://raw.githubusercontent.com/hyperpolymath/nextgen-languages-evangeliser/main/setup.sh | sh +# Usage (clone + run; this repo does NOT recommend piping remote scripts +# into a shell — download, review, then execute): +# curl -fsSL -o setup.sh https://raw.githubusercontent.com/hyperpolymath/nextgen-language-evangeliser/main/setup.sh +# sh setup.sh # # or after cloning: # ./setup.sh # @@ -129,6 +131,21 @@ detect_platform() { } # ── Install just ── +# +# No download-and-execute: piping a remote installer straight into a shell +# runs unverified code with the user's privileges (CWE-494; Hypatia +# code_safety/shell_download_then_run). Where the package manager cannot +# provide `just`, we print verified-manual-install instructions instead of +# fetching and running a remote script. +manual_just_instructions() { + warn "Package manager could not provide just. Install it manually, with integrity verification:" + info " 1. Download the release archive for your platform (pin a version tag):" + info " https://github.com/casey/just/releases" + info " 2. Verify the archive's SHA-256 checksum before extracting." + info " 3. Extract and place the 'just' binary on your PATH (e.g. /usr/local/bin)." + info " Alternatively: cargo install just (verifies crate signatures via crates.io)." +} + install_just() { if command -v just >/dev/null 2>&1; then ok "just already installed: $(just --version 2>/dev/null | head -1)" @@ -140,8 +157,8 @@ install_just() { case "$PKG_MGR" in dnf) sudo dnf install -y just ;; apt) sudo apt-get install -y just 2>/dev/null || { - # just not in older apt repos — use installer - curl -fsSL https://just.systems/install.sh | bash -s -- --to /usr/local/bin + # just not in older apt repos — no remote-exec fallback (CWE-494) + manual_just_instructions } ;; pacman) sudo pacman -S --noconfirm just ;; apk) sudo apk add just ;; @@ -152,8 +169,7 @@ install_just() { guix) guix install just ;; nix) nix-env -iA nixpkgs.just ;; *) - info "Using just installer script..." - curl -fsSL https://just.systems/install.sh | bash -s -- --to /usr/local/bin + manual_just_instructions ;; esac diff --git a/www/.well-known/ai.txt b/www/.well-known/ai.txt index db0e473..553c8e0 100644 --- a/www/.well-known/ai.txt +++ b/www/.well-known/ai.txt @@ -14,5 +14,5 @@ Disallow-Generation: yes # # For AI agent integration instructions, see: # 0-AI-MANIFEST.a2ml (universal AI entry point) -# AI.a2ml (Claude-specific instructions) -# .machine_readable/ (structured project state) +# CLAUDE.md + .claude/CLAUDE.md (agent instructions, incl. TypeScript exemptions) +# .machine_readable/descriptiles/ (STATE / META / … project state) diff --git a/www/.well-known/security.txt b/www/.well-known/security.txt index 9f7d926..969f2d9 100644 --- a/www/.well-known/security.txt +++ b/www/.well-known/security.txt @@ -5,5 +5,5 @@ Contact: mailto:j.d.a.jewell@open.ac.uk Expires: 2027-12-31T23:59:59.000Z Preferred-Languages: en -Canonical: https://github.com/hyperpolymath/nextgen-languages-evangeliser/.well-known/security.txt -Policy: https://github.com/hyperpolymath/nextgen-languages-evangeliser/blob/main/SECURITY.md +Canonical: https://github.com/hyperpolymath/nextgen-language-evangeliser/blob/main/www/.well-known/security.txt +Policy: https://github.com/hyperpolymath/nextgen-language-evangeliser/blob/main/SECURITY.adoc diff --git a/www/tests/check-wellknown.sh b/www/tests/check-wellknown.sh new file mode 100755 index 0000000..2d82644 --- /dev/null +++ b/www/tests/check-wellknown.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: CC-BY-SA-4.0 +# +# check-wellknown.sh — validate the canonical www/.well-known/ contents. +# Minted from hyperpolymath/rsr-template-repo www/tests/check-wellknown.sh +# (the stage-5 well-known sweep validator, rsr-template-repo#53/#119). + +set -uo pipefail +WWW="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +WK="$WWW/.well-known" +FAIL=0 +bad() { echo "WELLKNOWN FAIL: $*" >&2; FAIL=1; } +ok() { echo "ok: $*"; } + +for f in security.txt ai.txt humans.txt; do + [ -f "$WK/$f" ] && ok "$f present" || bad "$f missing from www/.well-known/" +done + +# RFC 9116: Contact and Expires are required fields. +if [ -f "$WK/security.txt" ]; then + grep -q '^Contact:' "$WK/security.txt" && ok "security.txt has Contact" || bad "security.txt missing Contact" + if grep -q '^Expires:' "$WK/security.txt"; then + EXP=$(grep '^Expires:' "$WK/security.txt" | head -1 | cut -d: -f2- | tr -d ' ') + case "$EXP" in + *'{{'*) ok "security.txt Expires carries a mint placeholder (pre-mint)" ;; + *) + if date -d "$EXP" >/dev/null 2>&1; then + DAYS=$(( ($(date -d "$EXP" +%s) - $(date +%s)) / 86400 )) + [ "$DAYS" -ge 0 ] && ok "security.txt Expires valid ($DAYS days)" || bad "security.txt EXPIRED" + else + bad "security.txt Expires is not a parseable timestamp: $EXP" + fi ;; + esac + else + bad "security.txt missing Expires" + fi +fi + +# ai.txt: the stance lines the estate's ai.txt convention requires. +if [ -f "$WK/ai.txt" ]; then + grep -q '^User-Agent:' "$WK/ai.txt" && ok "ai.txt has User-Agent" || bad "ai.txt missing User-Agent" + grep -q '^Disallow-Training:' "$WK/ai.txt" && ok "ai.txt has Disallow-Training" || bad "ai.txt missing Disallow-Training" +fi + +# humans.txt: humanstxt.org section markers. +if [ -f "$WK/humans.txt" ]; then + grep -q '/\* TEAM \*/' "$WK/humans.txt" && ok "humans.txt has TEAM" || bad "humans.txt missing TEAM section" + grep -q '/\* SITE \*/' "$WK/humans.txt" && ok "humans.txt has SITE" || bad "humans.txt missing SITE section" +fi + +# Migration window: a repository-root .well-known/ alongside the bundle is +# legacy; warn (do not fail) — the canonical location is www/.well-known/. +if [ -d "$WWW/../.well-known" ]; then + echo "WELLKNOWN WARN: legacy root .well-known/ still present — move it into www/.well-known/" >&2 +fi + +exit "$FAIL"