Skip to content
Open
2 changes: 1 addition & 1 deletion .audittraining/path-reference-drift/TYPOLOGY.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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 —
Expand Down
2 changes: 1 addition & 1 deletion .audittraining/release-candidates/REPORT.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
121 changes: 94 additions & 27 deletions .github/workflows/dogfood-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
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"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/governance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 2 additions & 2 deletions .hypatia-exemptions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 1 addition & 1 deletion docs/proof-debt.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/proofs/HANDOVER-neural-convergence.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/status/handover-2026-06-20.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/tech-debt-2026-05-26.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/ui/gossamer/BURBLE-DEFERRAL.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading