org-knowledge-layer 0.4.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {org_knowledge_layer-0.4.0/src/okl/scaffold → org_knowledge_layer-0.5.0/.claude}/hooks/userpromptsubmit-okl-check.sh +4 -2
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/.coverage +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/.github/workflows/ci.yml +12 -0
- {org_knowledge_layer-0.4.0/ci → org_knowledge_layer-0.5.0/.github/workflows}/okl-verify.yml +34 -8
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/.gitignore +2 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/AGENTS.md +8 -1
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/CHANGELOG.md +63 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/CLAUDE.md +8 -1
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/PKG-INFO +42 -2
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/README.md +40 -0
- {org_knowledge_layer-0.4.0/src/okl/scaffold → org_knowledge_layer-0.5.0}/ci/okl-verify.yml +34 -8
- org_knowledge_layer-0.5.0/ci/review-agent.sh +133 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/docs/decisions/2026-07-21-subject-tags-controlled-vocabulary.md +41 -0
- org_knowledge_layer-0.5.0/docs/okl-retrieval-pipeline.svg +89 -0
- org_knowledge_layer-0.5.0/docs/posts/04-the-elegant-change-was-the-wrong-one.md +55 -0
- org_knowledge_layer-0.5.0/docs/render_pipeline_diagram.py +287 -0
- org_knowledge_layer-0.5.0/evals/REPORT.md +838 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/ab_harness.py +134 -6
- org_knowledge_layer-0.5.0/evals/preflight.py +94 -0
- org_knowledge_layer-0.5.0/evals/results/ab-20260902-0538.json +439 -0
- org_knowledge_layer-0.5.0/evals/results/ab-20260902-1216.json +27 -0
- org_knowledge_layer-0.5.0/evals/results/ab-20260903-0157.json +441 -0
- org_knowledge_layer-0.5.0/evals/results/ab-20260903-0308.json +441 -0
- org_knowledge_layer-0.5.0/evals/results/ab-20260903-1214.json +441 -0
- org_knowledge_layer-0.5.0/evals/results/ab-20260903-1323.json +443 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/tasks.jsonl +1 -1
- {org_knowledge_layer-0.4.0/src/okl/scaffold → org_knowledge_layer-0.5.0}/hooks/stop-okl-encode.sh +11 -3
- {org_knowledge_layer-0.4.0/.claude → org_knowledge_layer-0.5.0}/hooks/userpromptsubmit-okl-check.sh +4 -2
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/pyproject.toml +2 -2
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/seed/dotnet-canon.json +8 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/seed/dotnet-decisions.json +11 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/seed/dotnet-defects.json +4 -2
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/seed/frontend-canon.json +2 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/seed/geospatial-deeptime-defects.json +2 -1
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/seed/geospatial-defects.json +8 -4
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/seed/geospatial-enforcement-defects.json +2 -1
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/seed/react-defects.json +4 -2
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/cli.py +53 -4
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/client.py +28 -8
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/core.py +35 -16
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/MANIFEST.md +8 -0
- org_knowledge_layer-0.5.0/src/okl/scaffold/ci/dependabot.yml +17 -0
- org_knowledge_layer-0.5.0/src/okl/scaffold/ci/method-gates.yml +74 -0
- {org_knowledge_layer-0.4.0/.github/workflows → org_knowledge_layer-0.5.0/src/okl/scaffold/ci}/okl-verify.yml +34 -8
- org_knowledge_layer-0.5.0/src/okl/scaffold/ci/review-agent.sh +133 -0
- org_knowledge_layer-0.5.0/src/okl/scaffold/claude/agents/architecture-reviewer.md +41 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/claude/commands/seed-from-codebase.md +1 -1
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/claude/commands/seed-from-docs.md +1 -1
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0/src/okl/scaffold}/hooks/stop-okl-encode.sh +11 -3
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0/src/okl/scaffold}/hooks/userpromptsubmit-okl-check.sh +4 -2
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold_cmd.py +5 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/store.py +66 -10
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/tests/test_okl.py +331 -36
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/tests/test_scaffold.py +205 -0
- org_knowledge_layer-0.4.0/evals/REPORT.md +0 -369
- org_knowledge_layer-0.4.0/src/okl/scaffold/ci/method-gates.yml +0 -32
- {org_knowledge_layer-0.4.0/src/okl/scaffold/claude → org_knowledge_layer-0.5.0/.claude}/agents/architecture-reviewer.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/.claude/hooks/stop-okl-encode.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/.claude/settings.json +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/.github/dependabot.yml +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/.github/pull_request_template.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/.github/workflows/publish.yml +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/CONTRIBUTING.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/LICENSE +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/SECURITY.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/ci/check-diagram-figures.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/docs/DEPLOY.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/docs/ab-results-chart.png +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/docs/ab-results-chart.svg +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/docs/decisions/2026-07-17-flat-retrieval-until-scale.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/docs/okl-how-it-works.excalidraw +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/docs/okl-how-it-works.svg +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/docs/okl-sixth-surface.excalidraw +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/docs/okl-sixth-surface.svg +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/docs/posts/01-memory-that-outlives-the-run.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/docs/posts/02-dont-let-a-step-grade-itself.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/docs/posts/03-enforcement-or-good-intentions.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/README.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/ab-20260829-2300.json +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/ab-20260829-2315.json +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/ab-20260830-0003.json +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/ab-20260830-0148.json +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/ab-20260901-0133.json +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/ab-20260901-1238.json +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/e2e-20260830/README.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/e2e-20260830/briefed-userpromptsubmit-lint.yml +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/e2e-20260830/control-lint.yml +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/e2e-20260830/hook.log +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/e2e-20260830/service-record-500.log +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/e2e-20260830/session-briefed-pretooluse.txt +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/e2e-20260830/session-briefed-userpromptsubmit.txt +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/evals/results/e2e-20260830/session-control.txt +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/gates/check-canon-size.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/gates/check-diagram-pairs.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/gates/check-doc-orphans.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/gates/check-links.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/gates/check-retractions.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/gates/check-tombstones.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/gates/run-gates.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/seed/dotnet-review-surfaces.json +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/seed/geospatial-eval-defects.json +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/seed/rag-defects.json +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/__init__.py +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/__main__.py +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/bootstrap.py +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/drift.py +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/mcp_server.py +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/claude/commands/check-rules.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/claude/commands/feature-spec.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/claude/rules/example-area.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/claude/skills/RECOMMENDED-COMPANIONS.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/claude/skills/encoding-loop/SKILL.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/claude/skills/verify-before-claiming/SKILL.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/evals/README.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/evals/cases.jsonl +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/evals/run_evals.py +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/gates/check-canon-size.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/gates/check-diagram-pairs.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/gates/check-doc-orphans.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/gates/check-links.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/gates/check-retractions.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/gates/check-tombstones.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/gates/run-gates.sh +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/hooks/hooks.json +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/plugin/plugin.json +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/profiles/dotnet/README.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/profiles/dotnet/rules/architecture.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/profiles/dotnet/rules/messaging.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/profiles/dotnet/rules/performance-and-data.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/profiles/dotnet/rules/security.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/profiles/geospatial/README.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/profiles/geospatial/rules/geospatial-ml.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/profiles/python-rag/README.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/profiles/python-rag/rules/fastapi-backend.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/profiles/python-rag/rules/project-structure.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/profiles/python-rag/rules/rag-pipeline.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/profiles/react/README.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/profiles/react/rules/frontend.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/registries/RETRACTIONS.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/registries/tombstones.txt +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/root/CLAUDE.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/scaffold/root/METHOD.md +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/seed.py +0 -0
- {org_knowledge_layer-0.4.0 → org_knowledge_layer-0.5.0}/src/okl/service.py +0 -0
|
@@ -36,8 +36,10 @@ resolve_okl() {
|
|
|
36
36
|
if ! OKL=$(resolve_okl); then
|
|
37
37
|
[ "${OKL_OFFLINE:-0}" = "1" ] && exit 0
|
|
38
38
|
echo "okl NOT FOUND — blocking (a check that can't run must not pass as clean)." >&2
|
|
39
|
-
echo "Install it
|
|
40
|
-
echo "
|
|
39
|
+
echo "Install it: pip install org-knowledge-layer. NOT 'pip install okl' — PyPI refuses that" >&2
|
|
40
|
+
echo "name as confusable, so it fails and looks like the tool does not exist." >&2
|
|
41
|
+
echo "Or set OKL_BIN, or re-run 'okl init' from a shell where okl works (that pins okl_bin" >&2
|
|
42
|
+
echo "into .okl/config.json). OKL_OFFLINE=1 proceeds without the layer." >&2
|
|
41
43
|
exit 2
|
|
42
44
|
fi
|
|
43
45
|
|
|
Binary file
|
|
@@ -78,6 +78,18 @@ jobs:
|
|
|
78
78
|
# key for organization-owned repositories, so it would break for anyone who forks
|
|
79
79
|
# this into an org. The binary has no such condition, and pinning the release keeps
|
|
80
80
|
# a new detection rule from retro-failing a branch that changed nothing.
|
|
81
|
+
# The reviewer okl ships, run on okl. It was listed as a review surface with nothing
|
|
82
|
+
# triggering it — the "enforcement surface that only runs on-demand runs never"
|
|
83
|
+
# failure this repo's own store names. Off unless REVIEW_CMD is set, so it costs
|
|
84
|
+
# nothing until someone decides it should.
|
|
85
|
+
- name: Architecture review (off unless REVIEW_CMD is set)
|
|
86
|
+
if: github.event_name == 'pull_request'
|
|
87
|
+
env:
|
|
88
|
+
REVIEW_CMD: ${{ vars.REVIEW_CMD }}
|
|
89
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
90
|
+
REVIEW_BASE_REF: origin/${{ github.base_ref }}
|
|
91
|
+
run: bash ci/review-agent.sh
|
|
92
|
+
|
|
81
93
|
- name: Secret scan — gitleaks
|
|
82
94
|
env:
|
|
83
95
|
GITLEAKS_VERSION: "8.30.1"
|
|
@@ -16,16 +16,45 @@ on:
|
|
|
16
16
|
push:
|
|
17
17
|
branches: [main]
|
|
18
18
|
|
|
19
|
+
# Least privilege: this workflow reads code and reports a status. It never writes.
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
|
|
23
|
+
concurrency:
|
|
24
|
+
group: okl-verify-${{ github.ref }}
|
|
25
|
+
cancel-in-progress: true
|
|
26
|
+
|
|
27
|
+
defaults:
|
|
28
|
+
run:
|
|
29
|
+
# Without pipefail a run block continues past a failed segment of a pipeline, so a
|
|
30
|
+
# green step can hide a red command.
|
|
31
|
+
shell: bash -euo pipefail {0}
|
|
32
|
+
|
|
19
33
|
jobs:
|
|
20
34
|
okl-verify:
|
|
21
35
|
runs-on: ubuntu-latest
|
|
36
|
+
env:
|
|
37
|
+
# Set at job level so every okl command below resolves the shared layer from the
|
|
38
|
+
# environment. This replaces an `okl connect --token` step, which wrote the bearer
|
|
39
|
+
# token into .okl/config.json on the runner — needless, since the client reads both
|
|
40
|
+
# variables directly, and one more place for a credential to end up.
|
|
41
|
+
# Unset secrets leave these empty, and okl falls back to the repo-local store.
|
|
42
|
+
OKL_SERVICE_URL: ${{ secrets.OKL_SERVICE_URL }}
|
|
43
|
+
OKL_TOKEN: ${{ secrets.OKL_TOKEN }}
|
|
22
44
|
steps:
|
|
23
|
-
|
|
45
|
+
# Actions are pinned to a commit SHA: a tag is mutable, so `@v5` is an unpinned
|
|
46
|
+
# dependency with access to your runner. If you copy this file into your own repo,
|
|
47
|
+
# add a .github/dependabot.yml with the `github-actions` ecosystem so these pins get
|
|
48
|
+
# security updates — a pin with no update path goes stale and that is its own risk.
|
|
49
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
24
50
|
with:
|
|
25
|
-
fetch-depth: 0 # drift needs history: it compares file
|
|
26
|
-
|
|
51
|
+
fetch-depth: 0 # drift needs history: it compares governed-file commits vs verified_at
|
|
52
|
+
persist-credentials: false # this job never pushes
|
|
53
|
+
|
|
54
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
27
55
|
with:
|
|
28
56
|
python-version: "3.13"
|
|
57
|
+
|
|
29
58
|
- name: Install okl
|
|
30
59
|
# Detect by the package source, not the distribution name: the name changed once
|
|
31
60
|
# (PyPI rejects "okl" as confusable) and a name-based check silently broke with it.
|
|
@@ -36,13 +65,10 @@ jobs:
|
|
|
36
65
|
pip install org-knowledge-layer
|
|
37
66
|
fi
|
|
38
67
|
|
|
39
|
-
- name:
|
|
40
|
-
env:
|
|
41
|
-
OKL_SERVICE_URL: ${{ secrets.OKL_SERVICE_URL }}
|
|
42
|
-
OKL_TOKEN: ${{ secrets.OKL_TOKEN }}
|
|
68
|
+
- name: Report which store the gate will run against
|
|
43
69
|
run: |
|
|
44
70
|
if [ -n "${OKL_SERVICE_URL:-}" ]; then
|
|
45
|
-
|
|
71
|
+
echo "verifying against the shared layer at $OKL_SERVICE_URL"
|
|
46
72
|
else
|
|
47
73
|
echo "no OKL_SERVICE_URL secret — verifying against the repo-local store"
|
|
48
74
|
fi
|
|
@@ -9,7 +9,14 @@ The hooks fire on your own session: `UserPromptSubmit` injects the store's brief
|
|
|
9
9
|
your context before you start; `Stop` blocks your first stop to ask what was learned.
|
|
10
10
|
Answer it honestly — record with a deliberate scope (`org` spreads to every repo,
|
|
11
11
|
`repo` stays local) and tags from the closed vocabulary in `store.KNOWN_TAGS`. Growing
|
|
12
|
-
that vocabulary is
|
|
12
|
+
that vocabulary is a deliberate act, never an ad-hoc tag: `okl record --type Vocabulary
|
|
13
|
+
--scope org --title <tag>` declares one for THIS store (a Go or Rust team needs no fork),
|
|
14
|
+
and editing `KNOWN_TAGS` changes the floor every store ships with.
|
|
15
|
+
|
|
16
|
+
- **`applies_to` is where a lesson is TRUE; tags are where it was FOUND.** Leave it unset
|
|
17
|
+
unless the lesson is false or meaningless off that stack — unset reaches every repo and
|
|
18
|
+
is the safe default, while a wrong value hides the record silently. Never derive it from
|
|
19
|
+
a tag: that is the reverted §4d defect, which measured worse than no filter at all.
|
|
13
20
|
|
|
14
21
|
- **Verification is evidence-based**: `okl verify <id> --run "<check>" --expect "<signal>"`.
|
|
15
22
|
Never re-record with `--verified` to clear drift — run the check.
|
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### The vocabulary is no longer fixed in the package
|
|
6
|
+
|
|
7
|
+
- **A store can declare its own subject tags.** `KNOWN_TAGS` remains, but as the *floor*
|
|
8
|
+
every store ships with rather than the whole vocabulary. Widen it by recording a
|
|
9
|
+
`Vocabulary` node, whose title is the tag:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
okl record --type Vocabulary --scope org --title rust --body "Rust services."
|
|
13
|
+
okl record --type Rule --scope org --tags rust --title "Prefer ? over unwrap in handlers"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Before this, a Go, Rust or PowerShell team could not file a lesson under its own language
|
|
17
|
+
without forking the package, which is a hard stop on adoption for a tool whose claim is
|
|
18
|
+
that a lesson learned in one place reaches another. What the closed vocabulary was *for*
|
|
19
|
+
survives: it catches the typo that files a record where nobody looks, and closed-per-store
|
|
20
|
+
still does, so `rustt` is refused in a store that declared `rust`. What changes is who may
|
|
21
|
+
grow it. `STACK_TAGS`, which gates `applies_to`, is deliberately not extended.
|
|
22
|
+
|
|
23
|
+
- **`frontend` and `prose` added to the floor.** Neither is specific to one org: any repo
|
|
24
|
+
that renders markup has frontend lessons, and any repo that governs its writing has prose
|
|
25
|
+
ones. `react` was the nearest existing tag for the first and is wrong, being a stack tag.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- **The hooks no longer tell you to run `pip install okl`, which cannot work.** PyPI refuses
|
|
30
|
+
`okl` as confusable, so the distribution is `org-knowledge-layer` and `okl` is only the
|
|
31
|
+
console script. The wrong name was in the `UserPromptSubmit` hook's not-found message,
|
|
32
|
+
which fires *exactly* when someone has no working install and sent them to a 404 that
|
|
33
|
+
reads as "this tool was never published." `okl init` stamps that hook into every repo it
|
|
34
|
+
touches, so the error propagated with adoption. Re-run `okl init` to pick up the new file.
|
|
35
|
+
- `cli.py`'s MCP hint said `pip install okl[mcp]`; now `pip install 'org-knowledge-layer[mcp]'`,
|
|
36
|
+
quoted because zsh globs unquoted brackets.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## 0.4.1
|
|
40
|
+
|
|
41
|
+
### Security — affects the CI workflow already in your repo
|
|
42
|
+
|
|
43
|
+
- **The shipped CI verifier no longer writes your bearer token to disk.** It ran
|
|
44
|
+
`okl connect --token`, which persisted `OKL_TOKEN` into `.okl/config.json` on the runner
|
|
45
|
+
for no reason: the client reads `OKL_SERVICE_URL` and `OKL_TOKEN` from the environment
|
|
46
|
+
directly, so setting them at job level does the same work with the credential never
|
|
47
|
+
touching the filesystem. Re-run `okl init` (or `okl scaffold`) to pick up the new file.
|
|
48
|
+
- **Both shipped workflows are hardened.** `okl-verify.yml` and `method-gates.yml` now
|
|
49
|
+
declare least-privilege `permissions`, a concurrency group, `pipefail`, and
|
|
50
|
+
`persist-credentials: false`, and pin their actions to commit SHAs rather than mutable
|
|
51
|
+
tags. They satisfied none of that before.
|
|
52
|
+
- `.github/dependabot.yml` ships with the kit, because pinning to a SHA without an update
|
|
53
|
+
path is how a repo ends up on a two-year-old action and calls it hardening.
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
- **Opt-in architecture review in CI.** `ci/review-agent.sh` runs the reviewer over a PR
|
|
58
|
+
diff and fails on must-fix findings. **Off unless you set the `REVIEW_CMD` repository
|
|
59
|
+
variable** to a CLI that reads a prompt on stdin — `claude -p` (authenticates with an
|
|
60
|
+
existing Claude Code login, no separate API key), `ollama run <model>` (local, free), or
|
|
61
|
+
any other. Unset, it prints one line and passes. It is the only gate in the kit that
|
|
62
|
+
calls a model, which is why it is the only one that is opt-in. Documented in the README
|
|
63
|
+
and the kit manifest.
|
|
64
|
+
|
|
65
|
+
|
|
3
66
|
## 0.4.0
|
|
4
67
|
|
|
5
68
|
### Behaviour changes worth reading before upgrading
|
|
@@ -9,7 +9,14 @@ The hooks fire on your own session: `UserPromptSubmit` injects the store's brief
|
|
|
9
9
|
your context before you start; `Stop` blocks your first stop to ask what was learned.
|
|
10
10
|
Answer it honestly — record with a deliberate scope (`org` spreads to every repo,
|
|
11
11
|
`repo` stays local) and tags from the closed vocabulary in `store.KNOWN_TAGS`. Growing
|
|
12
|
-
that vocabulary is
|
|
12
|
+
that vocabulary is a deliberate act, never an ad-hoc tag: `okl record --type Vocabulary
|
|
13
|
+
--scope org --title <tag>` declares one for THIS store (a Go or Rust team needs no fork),
|
|
14
|
+
and editing `KNOWN_TAGS` changes the floor every store ships with.
|
|
15
|
+
|
|
16
|
+
- **`applies_to` is where a lesson is TRUE; tags are where it was FOUND.** Leave it unset
|
|
17
|
+
unless the lesson is false or meaningless off that stack — unset reaches every repo and
|
|
18
|
+
is the safe default, while a wrong value hides the record silently. Never derive it from
|
|
19
|
+
a tag: that is the reverted §4d defect, which measured worse than no filter at all.
|
|
13
20
|
|
|
14
21
|
- **Verification is evidence-based**: `okl verify <id> --run "<check>" --expect "<signal>"`.
|
|
15
22
|
Never re-record with `--verified` to clear drift — run the check.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: org-knowledge-layer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Org Knowledge Layer — an installable sixth surface that carries encoded engineering lessons across repos.
|
|
5
5
|
Author: Joshua Dell
|
|
6
6
|
License: MIT
|
|
@@ -19,7 +19,7 @@ Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
|
19
19
|
Requires-Dist: mypy==2.3.1; extra == 'dev'
|
|
20
20
|
Requires-Dist: pydantic>=2; extra == 'dev'
|
|
21
21
|
Requires-Dist: pytest-cov==7.1.0; extra == 'dev'
|
|
22
|
-
Requires-Dist: pytest==
|
|
22
|
+
Requires-Dist: pytest==9.1.1; extra == 'dev'
|
|
23
23
|
Requires-Dist: ruff==0.16.5; extra == 'dev'
|
|
24
24
|
Requires-Dist: uvicorn>=0.29; extra == 'dev'
|
|
25
25
|
Provides-Extra: mcp
|
|
@@ -210,6 +210,20 @@ with receipts, not a benchmark.
|
|
|
210
210
|
|
|
211
211
|
<img src="docs/okl-how-it-works.svg" alt="One repo records a lesson; every other repo is briefed on it before its next task. The store is typed, scoped and tagged; every verification stamp carries the check that earned it." width="100%">
|
|
212
212
|
|
|
213
|
+
### How a briefing is actually built
|
|
214
|
+
|
|
215
|
+
`okl check` is a retrieval pipeline: seven stages turn a task sentence and the whole corpus
|
|
216
|
+
into the twelve records an agent reads. Two stages can drop a record, and only one of them
|
|
217
|
+
is entitled to — the distinction that this project got wrong once and measured its way out
|
|
218
|
+
of ([REPORT §4d](evals/REPORT.md)).
|
|
219
|
+
|
|
220
|
+
<img src="docs/okl-retrieval-pipeline.svg" alt="Seven stages: the corpus, a BM25 fetch of three times the limit, a scope gate, the exclusive applies_to filter, the inclusive tags-times-interests filter, a top-12 cutoff, bucketing by type, and rendering. Each stage shows how many records survive it." width="100%">
|
|
221
|
+
|
|
222
|
+
Every count in that diagram is **traced**, not transcribed — `docs/render_pipeline_diagram.py`
|
|
223
|
+
runs the real `store.search` and `core._in_scope` against a store seeded from `seed/`, and
|
|
224
|
+
`test_pipeline_diagram_is_current` fails if the committed render is not what the generator
|
|
225
|
+
produces today. Change a BM25 weight or a filter predicate and the diagram goes red with it.
|
|
226
|
+
|
|
213
227
|
### The mental model
|
|
214
228
|
|
|
215
229
|
`okl` stores small, typed **notes** and the **links** between them.
|
|
@@ -358,6 +372,32 @@ To remove okl from a repo entirely, delete `.okl/`, the two hook scripts and the
|
|
|
358
372
|
in `.claude/settings.json`, and `.github/workflows/okl-verify.yml`. Nothing else was
|
|
359
373
|
written, and nothing outside that repo was touched.
|
|
360
374
|
|
|
375
|
+
### Architecture review in CI (off by default)
|
|
376
|
+
|
|
377
|
+
The kit ships a reviewer that reads a PR diff against your encoded rules and fails the
|
|
378
|
+
build on a must-fix finding. It is **off unless you ask for it**, and it is not tied to any
|
|
379
|
+
vendor. Set the `REVIEW_CMD` repository variable to any CLI that reads a prompt on stdin:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
gh variable set REVIEW_CMD --body "claude -p --model sonnet" # your existing Claude Code login
|
|
383
|
+
gh variable set REVIEW_CMD --body "ollama run qwen2.5-coder" # local model, no API cost
|
|
384
|
+
gh variable set REVIEW_CMD --body "llm -m gpt-4o" # any other CLI
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Two things worth knowing:
|
|
388
|
+
|
|
389
|
+
- **`claude -p` needs no separate API key.** It authenticates with the Claude Code login you
|
|
390
|
+
already have, so if you use Claude Code there is nothing else to configure and no second
|
|
391
|
+
bill. Verified headless with `ANTHROPIC_API_KEY` unset.
|
|
392
|
+
- **Locally you do not need this at all.** The reviewer is a subagent
|
|
393
|
+
(`.claude/agents/architecture-reviewer.md`); ask your agent to run it on your changes and
|
|
394
|
+
it costs nothing beyond the session you are already in. The CI job exists for the case
|
|
395
|
+
where no human and no agent is in the loop — a PR nobody reviewed.
|
|
396
|
+
|
|
397
|
+
Unset, the step prints one line saying it is off and exits 0. Every other gate in the kit is
|
|
398
|
+
deterministic and free; this is the only one that calls a model, which is why it is the only
|
|
399
|
+
one that is opt-in.
|
|
400
|
+
|
|
361
401
|
## Wire a repo
|
|
362
402
|
|
|
363
403
|
```bash
|
|
@@ -176,6 +176,20 @@ with receipts, not a benchmark.
|
|
|
176
176
|
|
|
177
177
|
<img src="docs/okl-how-it-works.svg" alt="One repo records a lesson; every other repo is briefed on it before its next task. The store is typed, scoped and tagged; every verification stamp carries the check that earned it." width="100%">
|
|
178
178
|
|
|
179
|
+
### How a briefing is actually built
|
|
180
|
+
|
|
181
|
+
`okl check` is a retrieval pipeline: seven stages turn a task sentence and the whole corpus
|
|
182
|
+
into the twelve records an agent reads. Two stages can drop a record, and only one of them
|
|
183
|
+
is entitled to — the distinction that this project got wrong once and measured its way out
|
|
184
|
+
of ([REPORT §4d](evals/REPORT.md)).
|
|
185
|
+
|
|
186
|
+
<img src="docs/okl-retrieval-pipeline.svg" alt="Seven stages: the corpus, a BM25 fetch of three times the limit, a scope gate, the exclusive applies_to filter, the inclusive tags-times-interests filter, a top-12 cutoff, bucketing by type, and rendering. Each stage shows how many records survive it." width="100%">
|
|
187
|
+
|
|
188
|
+
Every count in that diagram is **traced**, not transcribed — `docs/render_pipeline_diagram.py`
|
|
189
|
+
runs the real `store.search` and `core._in_scope` against a store seeded from `seed/`, and
|
|
190
|
+
`test_pipeline_diagram_is_current` fails if the committed render is not what the generator
|
|
191
|
+
produces today. Change a BM25 weight or a filter predicate and the diagram goes red with it.
|
|
192
|
+
|
|
179
193
|
### The mental model
|
|
180
194
|
|
|
181
195
|
`okl` stores small, typed **notes** and the **links** between them.
|
|
@@ -324,6 +338,32 @@ To remove okl from a repo entirely, delete `.okl/`, the two hook scripts and the
|
|
|
324
338
|
in `.claude/settings.json`, and `.github/workflows/okl-verify.yml`. Nothing else was
|
|
325
339
|
written, and nothing outside that repo was touched.
|
|
326
340
|
|
|
341
|
+
### Architecture review in CI (off by default)
|
|
342
|
+
|
|
343
|
+
The kit ships a reviewer that reads a PR diff against your encoded rules and fails the
|
|
344
|
+
build on a must-fix finding. It is **off unless you ask for it**, and it is not tied to any
|
|
345
|
+
vendor. Set the `REVIEW_CMD` repository variable to any CLI that reads a prompt on stdin:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
gh variable set REVIEW_CMD --body "claude -p --model sonnet" # your existing Claude Code login
|
|
349
|
+
gh variable set REVIEW_CMD --body "ollama run qwen2.5-coder" # local model, no API cost
|
|
350
|
+
gh variable set REVIEW_CMD --body "llm -m gpt-4o" # any other CLI
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Two things worth knowing:
|
|
354
|
+
|
|
355
|
+
- **`claude -p` needs no separate API key.** It authenticates with the Claude Code login you
|
|
356
|
+
already have, so if you use Claude Code there is nothing else to configure and no second
|
|
357
|
+
bill. Verified headless with `ANTHROPIC_API_KEY` unset.
|
|
358
|
+
- **Locally you do not need this at all.** The reviewer is a subagent
|
|
359
|
+
(`.claude/agents/architecture-reviewer.md`); ask your agent to run it on your changes and
|
|
360
|
+
it costs nothing beyond the session you are already in. The CI job exists for the case
|
|
361
|
+
where no human and no agent is in the loop — a PR nobody reviewed.
|
|
362
|
+
|
|
363
|
+
Unset, the step prints one line saying it is off and exits 0. Every other gate in the kit is
|
|
364
|
+
deterministic and free; this is the only one that calls a model, which is why it is the only
|
|
365
|
+
one that is opt-in.
|
|
366
|
+
|
|
327
367
|
## Wire a repo
|
|
328
368
|
|
|
329
369
|
```bash
|
|
@@ -16,16 +16,45 @@ on:
|
|
|
16
16
|
push:
|
|
17
17
|
branches: [main]
|
|
18
18
|
|
|
19
|
+
# Least privilege: this workflow reads code and reports a status. It never writes.
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
|
|
23
|
+
concurrency:
|
|
24
|
+
group: okl-verify-${{ github.ref }}
|
|
25
|
+
cancel-in-progress: true
|
|
26
|
+
|
|
27
|
+
defaults:
|
|
28
|
+
run:
|
|
29
|
+
# Without pipefail a run block continues past a failed segment of a pipeline, so a
|
|
30
|
+
# green step can hide a red command.
|
|
31
|
+
shell: bash -euo pipefail {0}
|
|
32
|
+
|
|
19
33
|
jobs:
|
|
20
34
|
okl-verify:
|
|
21
35
|
runs-on: ubuntu-latest
|
|
36
|
+
env:
|
|
37
|
+
# Set at job level so every okl command below resolves the shared layer from the
|
|
38
|
+
# environment. This replaces an `okl connect --token` step, which wrote the bearer
|
|
39
|
+
# token into .okl/config.json on the runner — needless, since the client reads both
|
|
40
|
+
# variables directly, and one more place for a credential to end up.
|
|
41
|
+
# Unset secrets leave these empty, and okl falls back to the repo-local store.
|
|
42
|
+
OKL_SERVICE_URL: ${{ secrets.OKL_SERVICE_URL }}
|
|
43
|
+
OKL_TOKEN: ${{ secrets.OKL_TOKEN }}
|
|
22
44
|
steps:
|
|
23
|
-
|
|
45
|
+
# Actions are pinned to a commit SHA: a tag is mutable, so `@v5` is an unpinned
|
|
46
|
+
# dependency with access to your runner. If you copy this file into your own repo,
|
|
47
|
+
# add a .github/dependabot.yml with the `github-actions` ecosystem so these pins get
|
|
48
|
+
# security updates — a pin with no update path goes stale and that is its own risk.
|
|
49
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
24
50
|
with:
|
|
25
|
-
fetch-depth: 0 # drift needs history: it compares file
|
|
26
|
-
|
|
51
|
+
fetch-depth: 0 # drift needs history: it compares governed-file commits vs verified_at
|
|
52
|
+
persist-credentials: false # this job never pushes
|
|
53
|
+
|
|
54
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
27
55
|
with:
|
|
28
56
|
python-version: "3.13"
|
|
57
|
+
|
|
29
58
|
- name: Install okl
|
|
30
59
|
# Detect by the package source, not the distribution name: the name changed once
|
|
31
60
|
# (PyPI rejects "okl" as confusable) and a name-based check silently broke with it.
|
|
@@ -36,13 +65,10 @@ jobs:
|
|
|
36
65
|
pip install org-knowledge-layer
|
|
37
66
|
fi
|
|
38
67
|
|
|
39
|
-
- name:
|
|
40
|
-
env:
|
|
41
|
-
OKL_SERVICE_URL: ${{ secrets.OKL_SERVICE_URL }}
|
|
42
|
-
OKL_TOKEN: ${{ secrets.OKL_TOKEN }}
|
|
68
|
+
- name: Report which store the gate will run against
|
|
43
69
|
run: |
|
|
44
70
|
if [ -n "${OKL_SERVICE_URL:-}" ]; then
|
|
45
|
-
|
|
71
|
+
echo "verifying against the shared layer at $OKL_SERVICE_URL"
|
|
46
72
|
else
|
|
47
73
|
echo "no OKL_SERVICE_URL secret — verifying against the repo-local store"
|
|
48
74
|
fi
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# review-agent.sh — run the architecture reviewer over a PR diff, headless.
|
|
3
|
+
#
|
|
4
|
+
# Closes the gap the store names as "an enforcement surface that only runs on-demand runs
|
|
5
|
+
# never": .claude/agents/architecture-reviewer.md is listed as a review surface but nothing
|
|
6
|
+
# triggers it, so in practice it runs when someone remembers, which is never.
|
|
7
|
+
#
|
|
8
|
+
# OFF BY DEFAULT, AND VENDOR-NEUTRAL. Set REVIEW_CMD to any CLI that reads a prompt on
|
|
9
|
+
# stdin and writes the model's reply to stdout:
|
|
10
|
+
#
|
|
11
|
+
# REVIEW_CMD="claude -p --model sonnet"
|
|
12
|
+
# REVIEW_CMD="llm -m gpt-4o"
|
|
13
|
+
# REVIEW_CMD="ollama run qwen2.5-coder" # local, no API cost at all
|
|
14
|
+
#
|
|
15
|
+
# Unset, the step soft-passes and says how to switch it on. okl is harness-agnostic —
|
|
16
|
+
# hard-coding one vendor's CLI here would contradict that, and would hand every consumer
|
|
17
|
+
# an API bill they never asked for. This mirrors GENERATOR_CMD/JUDGE_CMD in evals/.
|
|
18
|
+
#
|
|
19
|
+
# Self-owned on purpose: no third-party review SaaS holds a token for your source, and the
|
|
20
|
+
# reviewer reads YOUR encoded rules (it calls `okl check` itself) rather than generic advice.
|
|
21
|
+
set -uo pipefail
|
|
22
|
+
cd "$(dirname "$0")/.."
|
|
23
|
+
|
|
24
|
+
# Located, not assumed: `okl scaffold --claude-dir` lets a repo name that directory
|
|
25
|
+
# something other than .claude, and a hard-coded path silently skips the review in exactly
|
|
26
|
+
# those repos — a gate that quietly does nothing is the failure this whole file exists to
|
|
27
|
+
# fix. AGENT_FILE overrides for anything unusual.
|
|
28
|
+
AGENT="${AGENT_FILE:-}"
|
|
29
|
+
if [ -z "$AGENT" ]; then
|
|
30
|
+
AGENT="$(find . -maxdepth 3 -path ./.git -prune -o \
|
|
31
|
+
-name architecture-reviewer.md -print 2>/dev/null | head -1)"
|
|
32
|
+
fi
|
|
33
|
+
BASE="${REVIEW_BASE_REF:-origin/main}"
|
|
34
|
+
|
|
35
|
+
if [ -z "${REVIEW_CMD:-}" ]; then
|
|
36
|
+
echo "review-agent: REVIEW_CMD not set — skipping (soft pass)."
|
|
37
|
+
echo " Set it to any CLI that takes a prompt on stdin to turn this into a blocking gate,"
|
|
38
|
+
echo " e.g. REVIEW_CMD=\"claude -p --model sonnet\" or REVIEW_CMD=\"ollama run qwen2.5-coder\"."
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
if [ ! -f "$AGENT" ]; then
|
|
42
|
+
echo "review-agent: no $AGENT — skipping (the reviewer ships with \`okl scaffold\`)."
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
# shellcheck disable=SC2086 — REVIEW_CMD is a command line, so word splitting is intended
|
|
46
|
+
if ! command -v ${REVIEW_CMD%% *} >/dev/null 2>&1; then
|
|
47
|
+
echo "review-agent: '${REVIEW_CMD%% *}' from REVIEW_CMD is not on PATH — skipping (soft pass)." >&2
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
diff_text="$(git diff "$BASE"...HEAD 2>/dev/null)"
|
|
52
|
+
if [ -z "$diff_text" ]; then
|
|
53
|
+
echo "review-agent: no diff against $BASE — nothing to review."
|
|
54
|
+
exit 0
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Truncate: a very large diff would blow the context budget and produce a worse review than
|
|
58
|
+
# a focused one. Reviewing the first N lines and saying so beats silently reviewing a
|
|
59
|
+
# truncated diff as though it were whole.
|
|
60
|
+
max_lines=4000
|
|
61
|
+
total_lines=$(printf '%s' "$diff_text" | wc -l | tr -d ' ')
|
|
62
|
+
if [ "$total_lines" -gt "$max_lines" ]; then
|
|
63
|
+
diff_text="$(printf '%s' "$diff_text" | head -n "$max_lines")"
|
|
64
|
+
echo "review-agent: diff is $total_lines lines; reviewing the first $max_lines."
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# The checklist lives in the agent file — one source of truth for the rules. Only the OUTPUT
|
|
68
|
+
# CONTRACT is supplied here, because the agent is written for interactive use and returns
|
|
69
|
+
# prose, which CI cannot act on.
|
|
70
|
+
prompt="$(cat <<EOF
|
|
71
|
+
You are the architecture reviewer defined below. Apply its checklist to the diff.
|
|
72
|
+
|
|
73
|
+
$(cat "$AGENT")
|
|
74
|
+
|
|
75
|
+
--- DIFF UNDER REVIEW ---
|
|
76
|
+
$diff_text
|
|
77
|
+
--- END DIFF ---
|
|
78
|
+
|
|
79
|
+
Reply with STRICT JSON and nothing else:
|
|
80
|
+
{"findings":[{"severity":"must-fix|consider","file":"path","line":0,"rule":"which checklist item","finding":"one sentence"}]}
|
|
81
|
+
|
|
82
|
+
Rules for your reply:
|
|
83
|
+
- "must-fix" is reserved for a violation of an encoded rule: a restated retraction, a
|
|
84
|
+
resurrected tombstoned identifier, a speculative abstraction, an assert-from-memory claim.
|
|
85
|
+
Style preferences and suggestions are "consider".
|
|
86
|
+
- Only report what this diff introduces. Pre-existing issues are out of scope.
|
|
87
|
+
- An empty findings list is a valid and common answer. Do not invent findings to seem useful.
|
|
88
|
+
EOF
|
|
89
|
+
)"
|
|
90
|
+
|
|
91
|
+
echo "review-agent: reviewing $(printf '%s' "$diff_text" | wc -l | tr -d ' ') lines of diff against $BASE"
|
|
92
|
+
# CLEAN ROOM. Run the reviewer from a temp directory, never the repo. If REVIEW_CMD is a
|
|
93
|
+
# coding agent, this repo's own hooks would otherwise fire on it: the UserPromptSubmit hook
|
|
94
|
+
# injects a briefing into the review, and the Stop hook answers instead of the reviewer.
|
|
95
|
+
# Reproduced while verifying this script — a smoke call in the repo cwd came back answering
|
|
96
|
+
# the Stop hook rather than the question, which is the same failure evals/ documents.
|
|
97
|
+
clean="$(mktemp -d)"
|
|
98
|
+
raw="$(cd "$clean" && printf '%s' "$prompt" | $REVIEW_CMD 2>/dev/null)"
|
|
99
|
+
rmdir "$clean" 2>/dev/null || true
|
|
100
|
+
|
|
101
|
+
# Strip any markdown fence the model adds around the JSON.
|
|
102
|
+
json="$(printf '%s' "$raw" | sed -e 's/^```json//' -e 's/^```//' -e '/^```$/d')"
|
|
103
|
+
|
|
104
|
+
if ! printf '%s' "$json" | python3 -c "import json,sys; json.load(sys.stdin)" 2>/dev/null; then
|
|
105
|
+
# Fail OPEN on an unparseable reply, loudly. A reviewer that cannot be understood has not
|
|
106
|
+
# found anything; blocking every merge on a malformed model response would train people to
|
|
107
|
+
# bypass the gate, which is worse than the gate not firing.
|
|
108
|
+
echo "review-agent: could not parse the reviewer's reply as JSON — not blocking." >&2
|
|
109
|
+
printf '%s\n' "$raw" | head -20 >&2
|
|
110
|
+
exit 0
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
printf '%s' "$json" | python3 -c '
|
|
114
|
+
import json, sys
|
|
115
|
+
|
|
116
|
+
# %-formatting rather than f-strings: this is embedded in a shell single-quoted string, so
|
|
117
|
+
# the keys need double quotes, and a backslash inside an f-string EXPRESSION is a SyntaxError
|
|
118
|
+
# before Python 3.12 — which this must survive, since it runs on whatever interpreter the
|
|
119
|
+
# consumer has. The first version hit exactly that and exited 1 on every run. A gate that
|
|
120
|
+
# always blocks looks identical to a gate that works until someone notices nothing has ever
|
|
121
|
+
# passed it.
|
|
122
|
+
findings = json.load(sys.stdin).get("findings", [])
|
|
123
|
+
must = [f for f in findings if f.get("severity") == "must-fix"]
|
|
124
|
+
for f in findings:
|
|
125
|
+
mark = "MUST-FIX" if f.get("severity") == "must-fix" else "consider"
|
|
126
|
+
print(" [%s] %s:%s %s" % (mark, f.get("file"), f.get("line"), f.get("rule")))
|
|
127
|
+
print(" %s" % f.get("finding"))
|
|
128
|
+
if not findings:
|
|
129
|
+
print(" no findings")
|
|
130
|
+
print()
|
|
131
|
+
print("review-agent: %d must-fix, %d to consider" % (len(must), len(findings) - len(must)))
|
|
132
|
+
sys.exit(1 if must else 0)
|
|
133
|
+
'
|
|
@@ -57,3 +57,44 @@ seed-file comments ("eval-integrity lessons are org-scoped") and the scaffold's
|
|
|
57
57
|
pipeline, not a language. The distinction the vocabulary already draws (stacks vs subjects)
|
|
58
58
|
did not have a slot for "the language this is written in", and adding one is cheaper than
|
|
59
59
|
overloading a stack tag whose meaning other repos depend on.
|
|
60
|
+
- 2026-09-03: **the "revisit when" condition above was met, and the vocabulary is now
|
|
61
|
+
extensible per store.** `KNOWN_TAGS` remains, but as the *floor* every store ships with
|
|
62
|
+
rather than the whole vocabulary: a store widens it by recording `Vocabulary` nodes,
|
|
63
|
+
whose title is the tag, and `Store.add_node` validates against floor ∪ declared.
|
|
64
|
+
|
|
65
|
+
The trigger was a language-support audit. Everything else in okl is language-agnostic —
|
|
66
|
+
drift asks git about path globs, verification runs whatever command you hand it, the
|
|
67
|
+
store holds text — and a Rust repo was driven end to end successfully except for one
|
|
68
|
+
thing: `okl record --tags rust` was refused, and the only remedy was to fork the package.
|
|
69
|
+
That is a hard stop on adoption for a tool whose whole claim is that a lesson learned in
|
|
70
|
+
one place reaches another.
|
|
71
|
+
|
|
72
|
+
What the closed vocabulary was *for* survives intact. It exists to catch the typo that
|
|
73
|
+
files a record where nobody looks, and closed-per-store still does: `rustt` is refused in
|
|
74
|
+
a store that declared `rust`. What changes is who is entitled to grow it — the org that
|
|
75
|
+
owns the store, rather than whoever can merge to this package.
|
|
76
|
+
|
|
77
|
+
A `Vocabulary` node is validated against the floor only. Otherwise the first declaration
|
|
78
|
+
in a fresh store would require the tag it is declaring.
|
|
79
|
+
|
|
80
|
+
**Not extended:** `STACK_TAGS`, which gates `applies_to`. Declaring `rust` makes it usable
|
|
81
|
+
as a subject tag, not as an applicability value. That is a narrower and rarer need — it
|
|
82
|
+
only matters for a lesson that is genuinely false off-stack — and §4h measured that the
|
|
83
|
+
filter `applies_to` feeds changes 0% of delivered briefing slots today. Extending the
|
|
84
|
+
exclusive mechanism without a measurement is exactly what §4d punished.
|
|
85
|
+
- 2026-09-17: `frontend` and `prose` added **to the floor**, when `emeraldleaf-dev` joined
|
|
86
|
+
the layer and every one of its records was rejected: markup and CSS defects, and rules
|
|
87
|
+
about writing. `react` was the nearest existing tag for the first group and is wrong for
|
|
88
|
+
the same reason `python-rag` was wrong for `python`. It is a stack tag, and that repo is a
|
|
89
|
+
hand-rolled Astro site with no UI framework. `frontend` names the subject those lessons are
|
|
90
|
+
actually about: markup whitespace semantics, CSS layout and responsive behaviour, browser
|
|
91
|
+
rendering. `prose` had no near-miss at all. Writing is governed in these repos the way code
|
|
92
|
+
is, with an em-dash budget and a claims-must-be-supported rule behind mechanical gates, and
|
|
93
|
+
a rule with a gate behind it is a rule the layer should carry.
|
|
94
|
+
|
|
95
|
+
The floor rather than a per-store `Vocabulary` declaration, now that the amendment above
|
|
96
|
+
makes both available. Neither tag is specific to one org: any repo that renders markup has
|
|
97
|
+
frontend lessons, and any repo that governs its writing has prose ones. The floor is for
|
|
98
|
+
subjects every store would otherwise have to declare for itself, which is the same reason
|
|
99
|
+
`messaging` and `python` are in it. Per-store declaration is for what an org's own stack
|
|
100
|
+
needs and nobody else's.
|