diff --git a/.audittraining/path-reference-drift/TYPOLOGY.adoc b/.audittraining/path-reference-drift/TYPOLOGY.adoc index 3d284ea8..4788910c 100644 --- a/.audittraining/path-reference-drift/TYPOLOGY.adoc +++ b/.audittraining/path-reference-drift/TYPOLOGY.adoc @@ -44,7 +44,7 @@ tree, prose, table |lexical |*TP_self_drift* |the repo’s OWN manifest/doc asserts a layout that contradicts its tree |TRUE positive |fix the doc -|*TP_real* |a real non-path defect (e.g. workflow missing +|*TP_real* |a real non-path defect (e.g. workflow missing `+timeout-minutes+`) |TRUE positive |fix the source |*FP_relative* |path is real but unanchored — diff --git a/.audittraining/release-candidates/REPORT.adoc b/.audittraining/release-candidates/REPORT.adoc index 5021c8e7..a666b168 100644 --- a/.audittraining/release-candidates/REPORT.adoc +++ b/.audittraining/release-candidates/REPORT.adoc @@ -143,7 +143,7 @@ file in subdirectory) * Consider publishing to crates.io (for Rust) . *For bunsenite (existing releases):* * Check commits since v1.0.2 -* Review for breaking changes vs. patches +* Review for breaking changes vs. patches * Follow semver for version bump . *Manual verification needed:* * supernorma - check deno.json diff --git a/.github/workflows/dogfood-gate.yml b/.github/workflows/dogfood-gate.yml index bfe74e6a..53c73d24 100644 --- a/.github/workflows/dogfood-gate.yml +++ b/.github/workflows/dogfood-gate.yml @@ -148,42 +148,109 @@ jobs: # Inline invisible character detection (from empty-linter's core patterns). # Checks for: zero-width spaces, zero-width joiners, BOM, soft hyphens, # non-breaking spaces, null bytes, and other invisible Unicode in source files. - set +e - PATTERNS='\xc2\xa0|\xe2\x80\x8b|\xe2\x80\x8c|\xe2\x80\x8d|\xef\xbb\xbf|\xc2\xad|\xe2\x80\x8e|\xe2\x80\x8f|\xe2\x80\xaa|\xe2\x80\xab|\xe2\x80\xac|\xe2\x80\xad|\xe2\x80\xae|\x00' - find "$GITHUB_WORKSPACE" \ - -not -path '*/.git/*' -not -path '*/node_modules/*' \ - -not -path '*/.deno/*' -not -path '*/target/*' \ - -not -path '*/_build/*' -not -path '*/deps/*' \ - -not -path '*/external_corpora/*' -not -path '*/.lake/*' \ - -type f \( -name '*.rs' -o -name '*.ex' -o -name '*.exs' -o -name '*.res' \ - -o -name '*.js' -o -name '*.ts' -o -name '*.json' -o -name '*.toml' \ - -o -name '*.yml' -o -name '*.yaml' -o -name '*.md' -o -name '*.adoc' \ - -o -name '*.idr' -o -name '*.zig' -o -name '*.v' -o -name '*.jl' \ - -o -name '*.gleam' -o -name '*.hs' -o -name '*.ml' -o -name '*.sh' \) \ - -exec grep -Prl "$PATTERNS" {} \; > /tmp/empty-lint-results.txt 2>/dev/null - EL_EXIT=$? - set -e - - FINDINGS=$(wc -l < /tmp/empty-lint-results.txt 2>/dev/null || echo 0) - echo "findings=$FINDINGS" >> "$GITHUB_OUTPUT" - echo "exit_code=$EL_EXIT" >> "$GITHUB_OUTPUT" - echo "ready=true" >> "$GITHUB_OUTPUT" - - # Emit annotations for each file with invisible chars - while IFS= read -r filepath; do - [ -z "$filepath" ] && continue - REL_PATH="${filepath#$GITHUB_WORKSPACE/}" - echo "::warning file=${REL_PATH}::Invisible Unicode characters detected (zero-width space, BOM, NBSP, etc.)" - done < /tmp/empty-lint-results.txt + python3 - <<'PY' + import os + from pathlib import Path + + root = Path(os.environ["GITHUB_WORKSPACE"]) + skipped_dirs = { + ".cache", ".deno", ".elixir_ls", ".git", ".lake", ".zig-cache", + "_build", "build", "coverage", "deps", "dist", "external_corpora", + "node_modules", "out", "target", "vendor", "zig-cache", "zig-out", + } + intentional_fixture_dirs = { + ("tests", "fixtures", "bom-detection"), + ("tests", "fixtures", "empty-linter"), + } + source_suffixes = { + ".adoc", ".adb", ".ads", ".agda", ".c", ".cc", ".clj", ".cljs", + ".cpp", ".erl", ".ex", ".exs", ".fs", ".fsi", ".fsx", ".gleam", + ".h", ".hh", ".hpp", ".hrl", ".hs", ".idr", ".java", ".jl", + ".js", ".json", ".kt", ".kts", ".lean", ".lua", ".md", ".ml", + ".php", ".r", ".rb", ".res", ".rs", ".scala", ".sh", ".swift", + ".toml", ".ts", ".v", ".yaml", ".yml", ".zig", + } + invisible_codepoints = { + 0x00A0, 0x00AD, 0x2060, 0xFEFF, + *range(0x200B, 0x2010), + *range(0x202A, 0x2030), + *range(0x2066, 0x206A), + } + + def command_escape(value): + return str(value).replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A") + + def property_escape(value): + return command_escape(value).replace(":", "%3A").replace(",", "%2C") + + # Runtime regression for GitHub workflow-command property delimiters. + assert property_escape("docs/a,b::c.md") == "docs/a%2Cb%3A%3Ac.md" + + def intentionally_invalid_fixture(relative): + return any(relative.parts[:len(prefix)] == prefix for prefix in intentional_fixture_dirs) + + findings = [] + errors = [] + for directory, dirnames, filenames in os.walk(root, topdown=True): + dirnames[:] = [name for name in dirnames if name not in skipped_dirs] + directory_path = Path(directory) + for filename in filenames: + path = directory_path / filename + relative = path.relative_to(root) + if ( + path.is_symlink() + or path.suffix.lower() not in source_suffixes + or intentionally_invalid_fixture(relative) + ): + continue + try: + data = path.read_bytes() + except OSError as error: + errors.append((relative, f"could not read file: {error}")) + continue + + reasons = set() + if data.startswith(b"\xef\xbb\xbf"): + reasons.add("leading UTF-8 BOM") + if any(byte <= 0x08 or byte in (0x0B, 0x0C) or 0x0E <= byte <= 0x1F for byte in data): + reasons.add("C0 control character") + try: + text_content = data.decode("utf-8", errors="strict") + except UnicodeDecodeError as error: + errors.append((relative, f"invalid UTF-8 at byte {error.start}")) + continue + if any(ord(character) in invisible_codepoints for character in text_content): + reasons.add("invisible Unicode code point") + if reasons: + findings.append((relative, ", ".join(sorted(reasons)))) + + with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output: + output.write(f"findings={len(findings)}\n") + output.write(f"exit_code={2 if errors else 0}\n") + output.write("ready=true\n") + + for relative, reasons in findings: + print(f"::warning file={property_escape(relative)}::Invisible characters detected: {command_escape(reasons)}") + for relative, reason in errors: + print(f"::error file={property_escape(relative)}::Invisible-character scan failed: {command_escape(reason)}") + PY - name: Write summary run: | if [ "${{ steps.lint.outputs.ready }}" = "true" ]; then FINDINGS="${{ steps.lint.outputs.findings }}" + EXIT_CODE="${{ steps.lint.outputs.exit_code }}" + if [ "$EXIT_CODE" -ne 0 ] 2>/dev/null; then + echo "## Empty-Linter Results" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo ":x: Scanner execution failed; see error annotations above." >> "$GITHUB_STEP_SUMMARY" + exit 1 + fi if [ "$FINDINGS" -gt 0 ] 2>/dev/null; then echo "## Empty-Linter Results" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" echo "Found **${FINDINGS}** invisible character issue(s). See annotations above." >> "$GITHUB_STEP_SUMMARY" + exit 1 else echo "## Empty-Linter Results" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index 3710eb92..8317b550 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -15,4 +15,4 @@ permissions: jobs: governance: - uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@d5fe075a50ab3ce4f41614d66ed77f152fda134f + uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@6b38eb50104901e2fec80f9455a972bc3eced813 diff --git a/.hypatia-exemptions.adoc b/.hypatia-exemptions.adoc index e839c8a1..4e740579 100644 --- a/.hypatia-exemptions.adoc +++ b/.hypatia-exemptions.adoc @@ -38,8 +38,8 @@ They are accepted as a category, not enumerated row-by-row: * `+.audittraining/**+` — training corpus. * `+scripts/fix-scripts/**+` — remediation scripts. -* `+test/**+` and `+**/tests/**+` — test fixtures -(e.g. `+password: "test123"+`). +* `+test/**+` and `+**/tests/**+` — test fixtures containing deliberately + credential-shaped sample data. Findings against these paths under `+security_errors/secret_detected+` are kept in `+.hypatia-baseline.json+` and should remain there. diff --git a/docs/proof-debt.adoc b/docs/proof-debt.adoc index 945fe7c2..461aeffe 100644 --- a/docs/proof-debt.adoc +++ b/docs/proof-debt.adoc @@ -17,7 +17,7 @@ is the worst-case local-tree value the seed should accept without flagging. *Marker count (canonical / tracked):* 5. *Marker count (local-tree max, -incl. agent worktrees):* 15. +incl. agent worktrees):* 15. This file is the *initial seed* — every marker starts in §(d) DEBT and the maintainer triages each into §(a) / §(b) / §(c) / §(d) as diff --git a/docs/proofs/HANDOVER-neural-convergence.adoc b/docs/proofs/HANDOVER-neural-convergence.adoc index f8ae36c0..c1d2e056 100644 --- a/docs/proofs/HANDOVER-neural-convergence.adoc +++ b/docs/proofs/HANDOVER-neural-convergence.adoc @@ -58,7 +58,7 @@ assertion; Agda retired) |✅ |parser totality |`+verification/proofs/lean4/ParserTotality.lean+` |✅ |ABI package + verify package |`+src/abi/*.idr+` -(incl. `+RuleEngine.idr+`), `+verify/src/*.idr+` |✅ +(incl. `+RuleEngine.idr+`), `+verify/src/*.idr+` |✅ |*Neural convergence — PageRank* |`+verification/proofs/lean4/PageRankInvariants.lean+` |⛔ preconditions diff --git a/docs/status/handover-2026-06-20.adoc b/docs/status/handover-2026-06-20.adoc index 29da026d..2042dbc9 100644 --- a/docs/status/handover-2026-06-20.adoc +++ b/docs/status/handover-2026-06-20.adoc @@ -70,7 +70,7 @@ backlog, 71-alert code-scanning backlog. 4 low). Needs the repo’s Dependabot security tab or a `+security_events+`-scoped token; no MCP tool exposes Dependabot vulnerability alerts in-session. 0 open Dependabot PRs currently; -action-group bumps (e.g. #294) have merged since the issue was filed, so +action-group bumps (e.g. #294) have merged since the issue was filed, so some lows may already be cleared. === Fresh-thread items diff --git a/docs/tech-debt-2026-05-26.adoc b/docs/tech-debt-2026-05-26.adoc index ec890d77..1874f4e6 100644 --- a/docs/tech-debt-2026-05-26.adoc +++ b/docs/tech-debt-2026-05-26.adoc @@ -30,7 +30,7 @@ soundness-relevant escape hatches in Haskell/Rust source. *Recommended next move:* triage each finding into one of: (a) discharge by proof, (b) cover with property-tests + a documented refutation -budget, or (c) annotate as a known/necessary axiom (e.g. `+funExt+`) in +budget, or (c) annotate as a known/necessary axiom (e.g. `+funExt+`) in `+docs/proof-debt.md+`. === 2. Licence debt diff --git a/src/ui/gossamer/BURBLE-DEFERRAL.adoc b/src/ui/gossamer/BURBLE-DEFERRAL.adoc index a989b204..e84d31e5 100644 --- a/src/ui/gossamer/BURBLE-DEFERRAL.adoc +++ b/src/ui/gossamer/BURBLE-DEFERRAL.adoc @@ -32,7 +32,7 @@ lands: dashboard expects Hypatia to emit findings/dispatches via a Burble session rather than via a direct HTTP read of the harness endpoints. . *Multi-operator session.* Two or more operators need to share Hypatia -state (e.g. a review seat watching the safety triangle live while +state (e.g. a review seat watching the safety triangle live while another operator drives dispatches). . *Voice control reaches the GUI.* Burble’s voice-control plane wants to fire `+Msg.Navigate(Department.Verification)+` or similar from outside