Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ jobs:
mdbook-admonish install .
mdbook build

- name: Check generated HTML links
run: tests/release/mdbook-links.test.sh

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
with:
Expand Down
3 changes: 3 additions & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [MCP Server](mcp.md)
- [Registry and Directory Listings](mcp-registry.md)
- [CLI Reference](cli.md)
- [User Stories](user-stories.md)
- [Configure Your LLM](configuration.md)
- [Distro Support](distro-support.md)

Expand Down Expand Up @@ -43,7 +44,9 @@

- [Developer Guide](developer-guide.md)
- [Testing Guide](contributing/testing.md)
- [Ubuntu VM Testing](contributing/ubuntu-vm-testing.md)
- [Contributing](contributing/CONTRIBUTING.md)
- [Ubuntu Story Audit](contributing/ubuntu-story-audit.md)
- [Release Process](release.md)
- [Release Readiness](release-readiness.md)
- [Testing User Stories](testing/user-stories.md)
4 changes: 2 additions & 2 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ natural-language intent into a risk-labelled plan, asks for approval where
needed, and streams execution output in real time.

If you want SysKnife inside Claude Code / Cursor / Codex CLI instead, see
the [main README](../README.md) and run `npx sysknife-setup`. Both paths
the [main README](https://github.com/lacs-project/sysknife/blob/main/README.md) and run `npx sysknife-setup`. Both paths
share the daemon, the audit chain, and the typed-action set.

<img
Expand Down Expand Up @@ -556,4 +556,4 @@ sysknife completions fish | source
- [Architecture overview](architecture.md) — trust boundary between CLI, shell,
and daemon
- [Developer guide](developer-guide.md) — building and testing locally
- [User stories](user-stories.md) — end-to-end scenario descriptions
- [User stories](https://github.com/lacs-project/sysknife/blob/main/docs/user-stories.md) — end-to-end scenario descriptions
2 changes: 1 addition & 1 deletion docs/contributing/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ rather answer questions than lose a contributor to a misunderstanding.
- **`help wanted`** — higher-impact tasks where outside help is
especially welcome.
- **`security`** — security issues take priority over everything else.
See [SECURITY.md](../../SECURITY.md) for the disclosure process.
See [SECURITY.md](https://github.com/lacs-project/sysknife/blob/main/SECURITY.md) for the disclosure process.

High-impact areas where contributions are most needed:

Expand Down
4 changes: 2 additions & 2 deletions docs/contributing/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ the model that produced it are recorded in `tests/evidence/story-runs/`, written
by the run itself. The `ubuntu-vm.sh` script mirrors the `atomic-vm.sh` workflow but
uses a Ubuntu 24.04 cloud image instead of a Fedora Atomic ISO.

See [docs/contributing/ubuntu-vm-testing.md](ubuntu-vm-testing.md) for the
See [docs/contributing/ubuntu-vm-testing.md](https://github.com/lacs-project/sysknife/blob/main/docs/contributing/ubuntu-vm-testing.md) for the
full setup and daily-use instructions. Quick reference:

```sh
Expand Down Expand Up @@ -453,7 +453,7 @@ Per-story timeout defaults to 10 minutes (`SYSKNIFE_STORY_TIMEOUT=600`) —
small tool-capable models on 4 vCPUs need that much headroom.

For the full history of what we tried and why, see
[HACKING.md](../../HACKING.md) §8.
[HACKING.md](https://github.com/lacs-project/sysknife/blob/main/HACKING.md) §8.

**Tip:** once provision succeeds end-to-end, immediately `stop` the VM
and `snapshot baseline`. Then every subsequent test cycle becomes
Expand Down
2 changes: 1 addition & 1 deletion docs/the-audit-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ anything, until the next row is written.
> startup and `sysknife audit verify` repeats it beside every verdict, because
> `OK: N rows verified` would otherwise read as "nothing was removed". Setup
> instructions are in
> [SECURITY.md](../SECURITY.md#audit-anchoring-in-the-default-deployment).
> [SECURITY.md](https://github.com/lacs-project/sysknife/blob/main/SECURITY.md#audit-anchoring-in-the-default-deployment).

A hash chain alone cannot detect one specific attack: **tail truncation**.
If an attacker with write access to the audit database deletes the most
Expand Down
41 changes: 41 additions & 0 deletions scripts/check-mdbook-links.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail

book_dir="${1:-book}"

python3 - "$book_dir" <<'PY'
import pathlib
import re
import sys
import urllib.parse

book = pathlib.Path(sys.argv[1])
if not book.is_dir():
raise SystemExit(f"mdbook-links: book directory does not exist: {book}")

href_pattern = re.compile(r'href="([^"]+)"')
checked = 0
broken = []

for page in sorted(book.rglob("*.html")):
if page.name == "print.html":
continue
for href in href_pattern.findall(page.read_text(errors="replace")):
if href.startswith(("http://", "https://", "#", "mailto:", "//")):
continue
target = urllib.parse.unquote(href.split("#", 1)[0].split("?", 1)[0])
if not target.endswith(".html"):
continue
checked += 1
if not (page.parent / target).resolve().exists():
broken.append(f"{page}\t{href}")

if checked == 0:
raise SystemExit("mdbook-links: no internal .html links were checked")
if broken:
print("mdbook-links: broken generated links:", file=sys.stderr)
print("\n".join(broken), file=sys.stderr)
raise SystemExit(1)

print(f"mdbook-links: checked {checked} internal .html links")
PY
49 changes: 49 additions & 0 deletions tests/release/mdbook-links.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
checker="$repo_root/scripts/check-mdbook-links.sh"
fixture="$(mktemp -d)"
trap 'rm -rf "$fixture"' EXIT

mkdir -p "$fixture/book"
cat > "$fixture/book/index.html" <<'EOF'
<a href="guide.html">guide</a>
EOF
cat > "$fixture/book/guide.html" <<'EOF'
<a href="https://example.com">external</a>
EOF

"$checker" "$fixture/book"

rm "$fixture/book/guide.html"
if output="$($checker "$fixture/book" 2>&1)"; then
printf 'mdbook-links: missing generated page unexpectedly passed\n' >&2
exit 1
fi
grep -Fq 'index.html' <<< "$output" || {
printf 'mdbook-links: missing-page error omitted its source: %s\n' "$output" >&2
exit 1
}

cat > "$fixture/book/index.html" <<'EOF'
<a href="https://example.com">external only</a>
EOF
if output="$($checker "$fixture/book" 2>&1)"; then
printf 'mdbook-links: zero-link fixture unexpectedly passed\n' >&2
exit 1
fi
grep -Fq 'no internal .html links were checked' <<< "$output" || {
printf 'mdbook-links: zero-link error was not explicit: %s\n' "$output" >&2
exit 1
}

if ! command -v mdbook >/dev/null 2>&1 || ! command -v mdbook-admonish >/dev/null 2>&1; then
printf 'mdbook-links: SKIP real mdBook build (mdbook and mdbook-admonish are required)\n'
exit 0
fi

build_dir="$fixture/real-book"
mdbook-admonish install "$repo_root"
mdbook build --dest-dir "$build_dir" "$repo_root"
"$checker" "$build_dir"
Loading