Skip to content

Commit d7250d4

Browse files
supachai-jclaude
andcommitted
Harden: fix install.sh exit code + add toolchain self-test + CI
Comprehensive test pass over every implemented use case surfaced one bug: - install.sh returned exit 1 on success — the final `[ "$MODE" = global ] && echo` short-circuited to non-zero when not global. Fixed with an if-block. Improvements: - tools/okf-selftest.sh: end-to-end self-test (init → validate clean + catch-bad → index → BM25 search → air-gap viz → lease acquire/conflict/release → optional Ollama embed+hybrid). 10 checks, exits non-zero on failure. - .github/workflows/tools.yml: runs the self-test + an installer smoke test (asserts exit 0) on every change to tools/skill/installer. - README: quickstart now shows the self-test. Verified locally: self-test 10/10 (incl. hybrid via Ollama). All MCP tools (read, hybrid search, propose_change PR-gated, lease-gated commit_concept) exercised through the real protocol against the demo bundle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 7d77829 commit d7250d4

5 files changed

Lines changed: 85 additions & 79 deletions

File tree

.claude/skills/okf/SKILL.md

Lines changed: 0 additions & 78 deletions
This file was deleted.

.github/workflows/tools.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Regression-test the OKF toolchain (pure-stdlib) on every change to tools/skill/installer.
2+
name: tools-selftest
3+
on:
4+
push:
5+
paths: ["tools/**", "skill/**", "install.sh", ".github/workflows/tools.yml"]
6+
pull_request:
7+
paths: ["tools/**", "skill/**", "install.sh"]
8+
workflow_dispatch:
9+
10+
jobs:
11+
selftest:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v6
15+
- uses: actions/setup-python@v6
16+
with:
17+
python-version: "3.13"
18+
- name: OKF toolchain self-test
19+
run: bash tools/okf-selftest.sh # embeddings/hybrid auto-skip (no Ollama in CI)
20+
- name: installer smoke test (must exit 0)
21+
run: |
22+
./install.sh --dir /tmp/okf-skill
23+
test -f /tmp/okf-skill/okf/SKILL.md
24+
test "$(ls /tmp/okf-skill/okf/scripts/*.py | wc -l)" -ge 6
25+
./install.sh --uninstall --dir /tmp/okf-skill
26+
test ! -d /tmp/okf-skill/okf

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ git clone https://github.com/supachai-j/open-knowledge-format-starter.git
5252
cd open-knowledge-format-starter
5353
python3 tools/okf-validate.py # → ✓ CONFORMANT with OKF v0.1
5454
python3 tools/okf-viz.py # → writes wiki/viz.html, open it in any browser
55+
bash tools/okf-selftest.sh # → exercises the whole toolchain (10 checks)
5556
```
5657

5758
1. Read **[AGENTS.md](AGENTS.md)** — governs how concepts are structured and how the agent behaves.

install.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,4 @@ echo " $SKILL_DIR/SKILL.md"
5050
echo " $SKILL_DIR/scripts/ ($(ls "$SKILL_DIR/scripts"/*.py | wc -l | tr -d ' ') scripts + vendor/)"
5151
echo
5252
echo "Use it: open Claude Code and say e.g. \"init an OKF knowledge base here\" or \"/okf\"."
53-
[ "$MODE" = "global" ] && echo "Installed globally — available in every project."
53+
if [ "$MODE" = "global" ]; then echo "Installed globally — available in every project."; fi

tools/okf-selftest.sh

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env bash
2+
# okf-selftest.sh — exercise the whole OKF toolchain end-to-end in a temp project.
3+
# Pure-stdlib tools are required; embeddings/hybrid are tested only if a local Ollama
4+
# is reachable (otherwise skipped). Exits non-zero if any check fails. Run: bash tools/okf-selftest.sh
5+
set -uo pipefail
6+
HERE="$(cd "$(dirname "$0")" && pwd)"
7+
PY="${PYTHON:-python3}"
8+
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
9+
pass=0; fail=0
10+
ok() { echo "$1"; pass=$((pass+1)); }
11+
bad() { echo "$1"; fail=$((fail+1)); }
12+
13+
echo "OKF self-test (tools: $HERE)"
14+
15+
# 1. init
16+
$PY "$HERE/okf-init.py" "$TMP/proj" --date 2026-01-01 >/dev/null 2>&1 && [ -f "$TMP/proj/wiki/index.md" ] \
17+
&& ok "okf-init scaffolds a bundle" || bad "okf-init"
18+
W="$TMP/proj/wiki"
19+
20+
# 2. validate — conformant
21+
$PY "$HERE/okf-validate.py" "$W" >/dev/null 2>&1 && ok "okf-validate passes a clean bundle" || bad "okf-validate (clean)"
22+
23+
# 3. validate — negative (missing type must fail)
24+
printf '%s\n' '---' 'title: x' '---' 'no type here' > "$W/broken.md"
25+
if $PY "$HERE/okf-validate.py" "$W" >/dev/null 2>&1; then bad "okf-validate should FAIL on missing type"; else ok "okf-validate catches missing type"; fi
26+
rm -f "$W/broken.md"
27+
28+
# 4. index + search (BM25)
29+
$PY "$HERE/okf-index.py" build "$W" >/dev/null 2>&1 && ok "okf-index builds" || bad "okf-index build"
30+
$PY "$HERE/okf-search.py" "how this knowledge base is organized" --bundle "$W" -k 1 2>/dev/null | grep -q "getting-started" \
31+
&& ok "okf-search (BM25) returns expected hit" || bad "okf-search (BM25)"
32+
33+
# 5. viz — air-gap single file
34+
$PY "$HERE/okf-viz.py" "$W" --name selftest >/dev/null 2>&1 \
35+
&& [ -f "$W/viz.html" ] && ! grep -q "cdn.jsdelivr" "$W/viz.html" \
36+
&& ok "okf-viz emits a self-contained (no-CDN) viewer" || bad "okf-viz (air-gap)"
37+
38+
# 6. lease lifecycle
39+
export OKF_LEASE_DIR="$TMP/leases"
40+
TOK=$($PY "$HERE/okf-lease.py" acquire t/x --owner A --ttl 60 2>/dev/null | $PY -c "import sys,json;print(json.load(sys.stdin).get('token',''))" 2>/dev/null)
41+
[ -n "$TOK" ] && ok "okf-lease acquire" || bad "okf-lease acquire"
42+
if $PY "$HERE/okf-lease.py" acquire t/x --owner B >/dev/null 2>&1; then bad "okf-lease should block a 2nd owner"; else ok "okf-lease blocks contending owner"; fi
43+
$PY "$HERE/okf-lease.py" release t/x --owner A --token "$TOK" >/dev/null 2>&1 && ok "okf-lease release" || bad "okf-lease release"
44+
45+
# 7. embeddings + hybrid (optional — only if Ollama is up)
46+
if curl -s -m 2 http://localhost:11434/api/tags >/dev/null 2>&1; then
47+
if $PY "$HERE/okf-embed.py" build "$W" >/dev/null 2>&1 \
48+
&& $PY "$HERE/okf-search.py" "organize knowledge" --bundle "$W" 2>/dev/null | grep -q "hybrid"; then
49+
ok "okf-embed + hybrid search (Ollama)"
50+
else bad "okf-embed/hybrid (Ollama reachable but failed)"; fi
51+
else
52+
echo " – skipped embeddings/hybrid (no Ollama on :11434)"
53+
fi
54+
55+
echo "──────────────────────────────"
56+
echo " $pass passed, $fail failed"
57+
[ "$fail" -eq 0 ]

0 commit comments

Comments
 (0)