org-knowledge-layer 0.3.1__tar.gz → 0.4.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.
Files changed (137) hide show
  1. org_knowledge_layer-0.4.0/.coverage +0 -0
  2. org_knowledge_layer-0.4.0/.github/dependabot.yml +32 -0
  3. org_knowledge_layer-0.4.0/.github/workflows/ci.yml +88 -0
  4. org_knowledge_layer-0.4.0/.github/workflows/publish.yml +90 -0
  5. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/AGENTS.md +3 -2
  6. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/CHANGELOG.md +49 -0
  7. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/CLAUDE.md +3 -2
  8. org_knowledge_layer-0.3.1/README.md → org_knowledge_layer-0.4.0/PKG-INFO +101 -1
  9. org_knowledge_layer-0.3.1/PKG-INFO → org_knowledge_layer-0.4.0/README.md +67 -33
  10. org_knowledge_layer-0.4.0/ci/check-diagram-figures.sh +82 -0
  11. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/docs/decisions/2026-07-21-subject-tags-controlled-vocabulary.md +7 -0
  12. org_knowledge_layer-0.4.0/docs/okl-how-it-works.excalidraw +3053 -0
  13. org_knowledge_layer-0.4.0/docs/okl-how-it-works.svg +2 -0
  14. org_knowledge_layer-0.4.0/docs/okl-sixth-surface.excalidraw +3819 -0
  15. org_knowledge_layer-0.4.0/docs/okl-sixth-surface.svg +2 -0
  16. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/REPORT.md +68 -0
  17. org_knowledge_layer-0.4.0/evals/results/ab-20260901-1238.json +441 -0
  18. {org_knowledge_layer-0.3.1/src/okl/scaffold → org_knowledge_layer-0.4.0}/gates/check-doc-orphans.sh +5 -1
  19. org_knowledge_layer-0.4.0/pyproject.toml +118 -0
  20. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/bootstrap.py +6 -0
  21. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/cli.py +124 -15
  22. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/client.py +68 -18
  23. org_knowledge_layer-0.4.0/src/okl/core.py +524 -0
  24. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/drift.py +18 -3
  25. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/mcp_server.py +6 -4
  26. org_knowledge_layer-0.4.0/src/okl/scaffold/claude/commands/seed-from-docs.md +123 -0
  27. org_knowledge_layer-0.4.0/src/okl/scaffold/gates/check-canon-size.sh +11 -0
  28. org_knowledge_layer-0.4.0/src/okl/scaffold/gates/check-diagram-pairs.sh +39 -0
  29. org_knowledge_layer-0.4.0/src/okl/scaffold/gates/check-doc-orphans.sh +23 -0
  30. org_knowledge_layer-0.4.0/src/okl/scaffold/gates/check-links.sh +41 -0
  31. org_knowledge_layer-0.4.0/src/okl/scaffold/gates/check-retractions.sh +22 -0
  32. org_knowledge_layer-0.4.0/src/okl/scaffold/gates/check-tombstones.sh +22 -0
  33. org_knowledge_layer-0.4.0/src/okl/scaffold/gates/run-gates.sh +33 -0
  34. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold_cmd.py +21 -7
  35. org_knowledge_layer-0.4.0/src/okl/seed.py +119 -0
  36. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/service.py +7 -3
  37. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/store.py +109 -17
  38. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/tests/test_okl.py +320 -1
  39. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/tests/test_scaffold.py +41 -2
  40. org_knowledge_layer-0.3.1/.claude/settings.local.json +0 -183
  41. org_knowledge_layer-0.3.1/.github/workflows/ci.yml +0 -22
  42. org_knowledge_layer-0.3.1/docs/okl-sixth-surface.excalidraw +0 -3819
  43. org_knowledge_layer-0.3.1/docs/okl-sixth-surface.svg +0 -2
  44. org_knowledge_layer-0.3.1/pyproject.toml +0 -49
  45. org_knowledge_layer-0.3.1/src/okl/core.py +0 -301
  46. org_knowledge_layer-0.3.1/src/okl/seed.py +0 -55
  47. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/.claude/hooks/stop-okl-encode.sh +0 -0
  48. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/.claude/hooks/userpromptsubmit-okl-check.sh +0 -0
  49. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/.claude/settings.json +0 -0
  50. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  51. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  52. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/.github/pull_request_template.md +0 -0
  53. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/.github/workflows/okl-verify.yml +0 -0
  54. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/.gitignore +0 -0
  55. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/CONTRIBUTING.md +0 -0
  56. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/LICENSE +0 -0
  57. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/SECURITY.md +0 -0
  58. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/ci/okl-verify.yml +0 -0
  59. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/docs/DEPLOY.md +0 -0
  60. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/docs/ab-results-chart.png +0 -0
  61. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/docs/ab-results-chart.svg +0 -0
  62. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/docs/decisions/2026-07-17-flat-retrieval-until-scale.md +0 -0
  63. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/docs/posts/01-memory-that-outlives-the-run.md +0 -0
  64. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/docs/posts/02-dont-let-a-step-grade-itself.md +0 -0
  65. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/docs/posts/03-enforcement-or-good-intentions.md +0 -0
  66. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/README.md +0 -0
  67. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/ab_harness.py +0 -0
  68. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/results/ab-20260829-2300.json +0 -0
  69. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/results/ab-20260829-2315.json +0 -0
  70. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/results/ab-20260830-0003.json +0 -0
  71. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/results/ab-20260830-0148.json +0 -0
  72. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/results/ab-20260901-0133.json +0 -0
  73. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/results/e2e-20260830/README.md +0 -0
  74. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/results/e2e-20260830/briefed-userpromptsubmit-lint.yml +0 -0
  75. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/results/e2e-20260830/control-lint.yml +0 -0
  76. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/results/e2e-20260830/hook.log +0 -0
  77. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/results/e2e-20260830/service-record-500.log +0 -0
  78. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/results/e2e-20260830/session-briefed-pretooluse.txt +0 -0
  79. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/results/e2e-20260830/session-briefed-userpromptsubmit.txt +0 -0
  80. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/results/e2e-20260830/session-control.txt +0 -0
  81. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/evals/tasks.jsonl +0 -0
  82. {org_knowledge_layer-0.3.1/src/okl/scaffold → org_knowledge_layer-0.4.0}/gates/check-canon-size.sh +0 -0
  83. {org_knowledge_layer-0.3.1/src/okl/scaffold → org_knowledge_layer-0.4.0}/gates/check-diagram-pairs.sh +0 -0
  84. {org_knowledge_layer-0.3.1/src/okl/scaffold → org_knowledge_layer-0.4.0}/gates/check-links.sh +0 -0
  85. {org_knowledge_layer-0.3.1/src/okl/scaffold → org_knowledge_layer-0.4.0}/gates/check-retractions.sh +0 -0
  86. {org_knowledge_layer-0.3.1/src/okl/scaffold → org_knowledge_layer-0.4.0}/gates/check-tombstones.sh +0 -0
  87. {org_knowledge_layer-0.3.1/src/okl/scaffold → org_knowledge_layer-0.4.0}/gates/run-gates.sh +0 -0
  88. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/hooks/stop-okl-encode.sh +0 -0
  89. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/hooks/userpromptsubmit-okl-check.sh +0 -0
  90. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/seed/dotnet-canon.json +0 -0
  91. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/seed/dotnet-decisions.json +0 -0
  92. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/seed/dotnet-defects.json +0 -0
  93. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/seed/dotnet-review-surfaces.json +0 -0
  94. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/seed/frontend-canon.json +0 -0
  95. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/seed/geospatial-deeptime-defects.json +0 -0
  96. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/seed/geospatial-defects.json +0 -0
  97. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/seed/geospatial-enforcement-defects.json +0 -0
  98. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/seed/geospatial-eval-defects.json +0 -0
  99. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/seed/rag-defects.json +0 -0
  100. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/seed/react-defects.json +0 -0
  101. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/__init__.py +0 -0
  102. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/__main__.py +0 -0
  103. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/MANIFEST.md +0 -0
  104. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/ci/method-gates.yml +0 -0
  105. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/ci/okl-verify.yml +0 -0
  106. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/claude/agents/architecture-reviewer.md +0 -0
  107. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/claude/commands/check-rules.md +0 -0
  108. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/claude/commands/feature-spec.md +0 -0
  109. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/claude/commands/seed-from-codebase.md +0 -0
  110. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/claude/rules/example-area.md +0 -0
  111. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/claude/skills/RECOMMENDED-COMPANIONS.md +0 -0
  112. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/claude/skills/encoding-loop/SKILL.md +0 -0
  113. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/claude/skills/verify-before-claiming/SKILL.md +0 -0
  114. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/evals/README.md +0 -0
  115. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/evals/cases.jsonl +0 -0
  116. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/evals/run_evals.py +0 -0
  117. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/hooks/hooks.json +0 -0
  118. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/hooks/stop-okl-encode.sh +0 -0
  119. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/hooks/userpromptsubmit-okl-check.sh +0 -0
  120. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/plugin/plugin.json +0 -0
  121. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/profiles/dotnet/README.md +0 -0
  122. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/profiles/dotnet/rules/architecture.md +0 -0
  123. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/profiles/dotnet/rules/messaging.md +0 -0
  124. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/profiles/dotnet/rules/performance-and-data.md +0 -0
  125. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/profiles/dotnet/rules/security.md +0 -0
  126. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/profiles/geospatial/README.md +0 -0
  127. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/profiles/geospatial/rules/geospatial-ml.md +0 -0
  128. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/profiles/python-rag/README.md +0 -0
  129. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/profiles/python-rag/rules/fastapi-backend.md +0 -0
  130. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/profiles/python-rag/rules/project-structure.md +0 -0
  131. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/profiles/python-rag/rules/rag-pipeline.md +0 -0
  132. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/profiles/react/README.md +0 -0
  133. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/profiles/react/rules/frontend.md +0 -0
  134. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/registries/RETRACTIONS.md +0 -0
  135. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/registries/tombstones.txt +0 -0
  136. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/root/CLAUDE.md +0 -0
  137. {org_knowledge_layer-0.3.1 → org_knowledge_layer-0.4.0}/src/okl/scaffold/root/METHOD.md +0 -0
Binary file
@@ -0,0 +1,32 @@
1
+ # Dependabot.
2
+ #
3
+ # This exists because of the SHA pinning, not despite it. Pinning actions to a commit
4
+ # closes the "a mutable tag moved under me" hole and opens a quieter one: the pin never
5
+ # updates, so a security fix in an action never arrives. Pinning without an update path
6
+ # is how a repo ends up on a two-year-old checkout action and calls it hardening.
7
+ # Dependabot rewrites the SHA and the trailing `# v5` comment together.
8
+ version: 2
9
+
10
+ updates:
11
+ - package-ecosystem: github-actions
12
+ directory: "/"
13
+ schedule:
14
+ interval: weekly
15
+ labels: ["dependencies", "ci"]
16
+ commit-message:
17
+ prefix: "ci"
18
+
19
+ - package-ecosystem: pip
20
+ directory: "/"
21
+ schedule:
22
+ interval: weekly
23
+ labels: ["dependencies"]
24
+ commit-message:
25
+ prefix: "deps"
26
+ # The gating tools are pinned exactly on purpose (a range lets a new release
27
+ # retro-fail a branch that changed nothing). Dependabot is the intended way to move
28
+ # those pins: a PR that runs the full suite against the new version before it lands,
29
+ # rather than a range that upgrades silently mid-branch.
30
+ groups:
31
+ dev-tooling:
32
+ patterns: ["pytest", "ruff", "mypy"]
@@ -0,0 +1,88 @@
1
+ # Repo CI — lints, type-checks and tests okl itself. (okl-verify.yml is different: it
2
+ # demonstrates the product's own drift/method gates, the workflow consumers copy.)
3
+ #
4
+ # Hardened per the org rule this repo's own store carries ("CI workflow baseline:
5
+ # pipefail, no persisted credentials, least-privilege permissions, concurrency groups —
6
+ # pin actions"). It satisfied none of the five until this commit, which is the rule's own
7
+ # symptom: an enforcement surface nothing triggers runs never.
8
+ name: ci
9
+
10
+ on:
11
+ push:
12
+ branches: [ "main" ]
13
+ pull_request:
14
+ branches: [ "main" ]
15
+
16
+ # Least privilege: this job reads code and reports status. It never pushes.
17
+ permissions:
18
+ contents: read
19
+
20
+ # One run per ref. A superseded push should stop burning minutes on a result nobody
21
+ # will read.
22
+ concurrency:
23
+ group: ci-${{ github.ref }}
24
+ cancel-in-progress: true
25
+
26
+ defaults:
27
+ run:
28
+ # Without pipefail a run block continues past a failed segment of a pipeline, so a
29
+ # green step can hide a red command.
30
+ shell: bash -euo pipefail {0}
31
+
32
+ jobs:
33
+ lint-and-test:
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ # Actions pinned to a commit SHA, not a tag: a tag is mutable, so `@v5` is an
37
+ # unpinned dependency with write access to the runner.
38
+ - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
39
+ with:
40
+ # The diagram gate reads committed history (git show HEAD:...), not the
41
+ # checkout tree, so it needs more than the default shallow fetch.
42
+ fetch-depth: 0
43
+ # This job never pushes; leaving the token in .git/config lets any later step
44
+ # (or a compromised dependency) use it.
45
+ persist-credentials: false
46
+
47
+ - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
48
+ with:
49
+ python-version: "3.12"
50
+
51
+ - name: Install (dev extras)
52
+ run: pip install -e ".[dev]"
53
+
54
+ - name: Lint — ruff (config in pyproject.toml)
55
+ run: ruff check .
56
+
57
+ - name: Types — mypy
58
+ run: mypy src/okl
59
+
60
+ - name: Tests (with the coverage ratchet)
61
+ run: pytest -q --cov=okl
62
+
63
+ - name: Diagram figures trace to committed receipts
64
+ run: ./ci/check-diagram-figures.sh
65
+
66
+ # The content audits okl ships to consumers, run against okl itself. They were
67
+ # shipped for weeks without this repo running any of them — the "enforcement
68
+ # surface nothing triggers" failure, one level up: a gate whose author has never
69
+ # run it on their own tree.
70
+ - name: Method gates (links, doc orphans, diagram pairs, tombstones, canon size)
71
+ run: bash gates/run-gates.sh
72
+
73
+ # Secret scan over the FULL history, not the working tree. okl writes a bearer
74
+ # token into .okl/config.json, and the .gitignore it now drops there is the only
75
+ # thing between that and a commit — this is the check for when that fails.
76
+ #
77
+ # The upstream gitleaks-action is deliberately not used: it requires a paid licence
78
+ # key for organization-owned repositories, so it would break for anyone who forks
79
+ # this into an org. The binary has no such condition, and pinning the release keeps
80
+ # a new detection rule from retro-failing a branch that changed nothing.
81
+ - name: Secret scan — gitleaks
82
+ env:
83
+ GITLEAKS_VERSION: "8.30.1"
84
+ run: |
85
+ curl -sSfL -o /tmp/gitleaks.tar.gz \
86
+ "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
87
+ tar -xzf /tmp/gitleaks.tar.gz -C /tmp gitleaks
88
+ /tmp/gitleaks git . --no-banner --redact --exit-code 1
@@ -0,0 +1,90 @@
1
+ # Release to PyPI on a version tag, using Trusted Publishing.
2
+ #
3
+ # There is no API token anywhere in this workflow, and none stored in the repository.
4
+ # PyPI accepts the upload because GitHub vouches for it: the publish job mints a
5
+ # short-lived OIDC token that names this repository, this workflow file and this
6
+ # environment, and PyPI checks that against the trusted publisher it was told to expect.
7
+ # A leaked repository secret cannot publish okl because there is no such secret.
8
+ #
9
+ # ONE-TIME SETUP ON PYPI (this workflow does nothing until it is done):
10
+ # pypi.org → the org-knowledge-layer project → Publishing → Add a pending publisher
11
+ # Owner: emeraldleaf
12
+ # Repository: okl
13
+ # Workflow name: publish.yml
14
+ # Environment name: pypi
15
+ # Then create the `pypi` environment under repo Settings → Environments. Adding a
16
+ # required reviewer there makes every release a deliberate, approved act.
17
+ name: publish
18
+
19
+ on:
20
+ push:
21
+ tags: ["v*"]
22
+
23
+ permissions:
24
+ contents: read
25
+
26
+ jobs:
27
+ build:
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
31
+ with:
32
+ persist-credentials: false
33
+
34
+ - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
35
+ with:
36
+ python-version: "3.12"
37
+
38
+ # The tag is the release's claim about what version this is; pyproject.toml is the
39
+ # version that actually gets uploaded. When they disagree, PyPI takes pyproject's
40
+ # answer and the tag becomes a lie that is permanent — you cannot reuse a version
41
+ # number on PyPI, so the mistake is not fixable, only worked around.
42
+ - name: Tag must match the version in pyproject.toml
43
+ run: |
44
+ tag="${GITHUB_REF_NAME#v}"
45
+ pkg="$(python -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['version'])")"
46
+ echo "tag=$tag pyproject=$pkg"
47
+ if [ "$tag" != "$pkg" ]; then
48
+ echo "::error::tag v$tag does not match pyproject version $pkg"
49
+ exit 1
50
+ fi
51
+
52
+ - name: Build
53
+ run: |
54
+ python -m pip install --upgrade build twine
55
+ python -m build
56
+ # `twine check` catches a malformed long_description, which PyPI rejects only
57
+ # after the upload has otherwise succeeded.
58
+ python -m twine check dist/*
59
+
60
+ - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
61
+ with:
62
+ name: dist
63
+ path: dist/
64
+
65
+ publish:
66
+ needs: build
67
+ runs-on: ubuntu-latest
68
+ # A named environment is what the PyPI trusted publisher is bound to, and it is where
69
+ # a required-reviewer rule can gate the release.
70
+ environment:
71
+ name: pypi
72
+ url: https://pypi.org/project/org-knowledge-layer/
73
+ permissions:
74
+ id-token: write # mint the OIDC token PyPI verifies
75
+ attestations: write # sign the artifacts
76
+ contents: read
77
+ steps:
78
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
79
+ with:
80
+ name: dist
81
+ path: dist/
82
+
83
+ # Provenance: a signed statement that these exact files were built by this workflow
84
+ # from this commit. It is what lets someone verify the wheel on PyPI came from the
85
+ # source they are reading, rather than trusting that it did.
86
+ - uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
87
+ with:
88
+ subject-path: "dist/*"
89
+
90
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
@@ -31,8 +31,9 @@ okl drift # rules whose governed source changed after v
31
31
  ## Rules that are enforced (and why)
32
32
 
33
33
  - **Mirror files are byte-identical, test-enforced**: `ci/okl-verify.yml` ==
34
- `.github/workflows/okl-verify.yml` == `src/okl/scaffold/ci/okl-verify.yml`, and
35
- `hooks/*.sh` == `src/okl/scaffold/hooks/*.sh`. The scaffold copies are what consumers
34
+ `.github/workflows/okl-verify.yml` == `src/okl/scaffold/ci/okl-verify.yml`,
35
+ `hooks/*.sh` == `src/okl/scaffold/hooks/*.sh`, and
36
+ `gates/*.sh` == `src/okl/scaffold/gates/*.sh`. The scaffold copies are what consumers
36
37
  receive; the repo copies are the dogfood. Edit ONE, copy to the others in the same
37
38
  change — `tests/test_scaffold.py::test_mirror_files_identical` fails otherwise.
38
39
  - **ruff `E702` is ignored deliberately** (semicolon one-liners): the tests use a
@@ -1,5 +1,54 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Behaviour changes worth reading before upgrading
6
+
7
+ - **Stack tags now filter exclusively.** A record naming a stack (`dotnet`, `react`,
8
+ `geospatial`, `python`, `python-rag`) is only shown to a repo that declared that stack
9
+ in its `interests`. Previously any shared tag let it through, so a rule tagged
10
+ `dotnet,method` reached every repo interested in `method` — a subject 75 records carry.
11
+ **If you declare `interests`, expect fewer records after upgrading.** That is the point,
12
+ but it is a change in what your briefings contain. Repos that declare no interests are
13
+ unaffected, and untagged records still always pass.
14
+ - **`symptom` and `fix` are now searchable.** They were not, which meant a record written
15
+ the way the docs tell you to write it — short title, the distinguishing words in the
16
+ symptom — could not be retrieved at all. Existing stores rebuild their index on first
17
+ open; you do not need to re-record anything.
18
+
19
+ ### Added
20
+
21
+ - **`okl dedup`** reports near-duplicate records for review. Lexical and explainable:
22
+ per-field weighted Jaccard over title, symptom and fix, IDF-weighted from your own
23
+ corpus. It never merges or drops anything — the measured score bands for true
24
+ paraphrases and for distinct-but-related records overlap, so the call is a person's.
25
+ The same check runs as an advisory when importing an agent-proposed pack.
26
+ - **`/seed-from-docs`** mines the specs, ADRs and rules files you already wrote into typed
27
+ records. Built around one distinction: a record is a standing instruction that outlives
28
+ the work item it came from, so "deep offsets use keyset pagination" belongs and "add
29
+ pagination to /orders this sprint" does not.
30
+ - A pack declaring `_proposed_by` is refused unless every node carries a `found_by`
31
+ citation, so the rule the seeding commands state is enforced at import rather than
32
+ remembered by a reviewer.
33
+
34
+ ### Fixed
35
+
36
+ - `Client._remote_url` validates the URL scheme once. A `service_url` from config could
37
+ name `file://`, turning a remote read into a local file read.
38
+ - `OKLUnreachable` is now `OKLUnreachableError`, with the old name kept as an alias so
39
+ existing `except` clauses still work.
40
+ - Drift timestamps are timezone-aware; `utcfromtimestamp` is deprecated from Python 3.12
41
+ and returned a naive datetime that read as local time when compared across machines.
42
+
43
+ ### Internal
44
+
45
+ - `_Backend` is a `typing.Protocol` whose docstring states the behavioural contract, with
46
+ one conformance test both backends run — the defect it guards (Postgres satisfying every
47
+ signature while running an unranked match) was invisible to signatures alone.
48
+ - mypy, and ruff widened from 6 rule families to 15 including security and complexity,
49
+ both wired into CI alongside a secret scan, the method gates and a coverage floor.
50
+
51
+
3
52
  ## 0.3.1
4
53
 
5
54
  ### Security
@@ -31,8 +31,9 @@ okl drift # rules whose governed source changed after v
31
31
  ## Rules that are enforced (and why)
32
32
 
33
33
  - **Mirror files are byte-identical, test-enforced**: `ci/okl-verify.yml` ==
34
- `.github/workflows/okl-verify.yml` == `src/okl/scaffold/ci/okl-verify.yml`, and
35
- `hooks/*.sh` == `src/okl/scaffold/hooks/*.sh`. The scaffold copies are what consumers
34
+ `.github/workflows/okl-verify.yml` == `src/okl/scaffold/ci/okl-verify.yml`,
35
+ `hooks/*.sh` == `src/okl/scaffold/hooks/*.sh`, and
36
+ `gates/*.sh` == `src/okl/scaffold/gates/*.sh`. The scaffold copies are what consumers
36
37
  receive; the repo copies are the dogfood. Edit ONE, copy to the others in the same
37
38
  change — `tests/test_scaffold.py::test_mirror_files_identical` fails otherwise.
38
39
  - **ruff `E702` is ignored deliberately** (semicolon one-liners): the tests use a
@@ -1,3 +1,37 @@
1
+ Metadata-Version: 2.5
2
+ Name: org-knowledge-layer
3
+ Version: 0.4.0
4
+ Summary: Org Knowledge Layer — an installable sixth surface that carries encoded engineering lessons across repos.
5
+ Author: Joshua Dell
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: agentic-coding,ai-engineering,encoding-loop,knowledge-layer,mcp
9
+ Requires-Python: >=3.10
10
+ Provides-Extra: all
11
+ Requires-Dist: fastapi>=0.110; extra == 'all'
12
+ Requires-Dist: mcp>=1.2; extra == 'all'
13
+ Requires-Dist: psycopg[binary]>=3.1; extra == 'all'
14
+ Requires-Dist: pydantic>=2; extra == 'all'
15
+ Requires-Dist: uvicorn>=0.29; extra == 'all'
16
+ Provides-Extra: dev
17
+ Requires-Dist: fastapi>=0.110; extra == 'dev'
18
+ Requires-Dist: httpx>=0.27; extra == 'dev'
19
+ Requires-Dist: mypy==2.3.1; extra == 'dev'
20
+ Requires-Dist: pydantic>=2; extra == 'dev'
21
+ Requires-Dist: pytest-cov==7.1.0; extra == 'dev'
22
+ Requires-Dist: pytest==8.4.2; extra == 'dev'
23
+ Requires-Dist: ruff==0.16.5; extra == 'dev'
24
+ Requires-Dist: uvicorn>=0.29; extra == 'dev'
25
+ Provides-Extra: mcp
26
+ Requires-Dist: mcp>=1.2; extra == 'mcp'
27
+ Provides-Extra: postgres
28
+ Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
29
+ Provides-Extra: service
30
+ Requires-Dist: fastapi>=0.110; extra == 'service'
31
+ Requires-Dist: pydantic>=2; extra == 'service'
32
+ Requires-Dist: uvicorn>=0.29; extra == 'service'
33
+ Description-Content-Type: text/markdown
34
+
1
35
  # okl — a shared knowledge layer for AI-assisted engineering
2
36
 
3
37
  > A small database of the specific lessons a codebase has learned — the bugs it
@@ -46,6 +80,17 @@ Two things ship in the package. They are not coequal:
46
80
  retrieved into an agent's context before a task, and go stale loudly when the code
47
81
  they describe moves on. Everything measured in [evals/REPORT.md](evals/REPORT.md)
48
82
  measures this.
83
+
84
+ It is worth being precise about what that store fills up with, because "lessons a
85
+ codebase has learned" invites the picture of a bug database. In the 161-record corpus
86
+ in [seed/](seed/) it is mostly not that: **90 Rules, 20 Decisions and 7 Gates against
87
+ 34 Defects** — conventions the code follows and trade-offs already settled, not a
88
+ ledger of things that broke. Count it yourself:
89
+
90
+ ```bash
91
+ python3 -c "import json,glob,collections; c=collections.Counter(
92
+ n['type'] for f in glob.glob('seed/*.json') for n in json.load(open(f))['nodes']); print(c)"
93
+ ```
49
94
  - **`okl scaffold`** is a starter kit for the in-repo discipline the store assumes: a
50
95
  lean canon file, mechanical gates, registries, a review agent, and an eval harness.
51
96
  It is useful on its own and it has never been measured. Use it to get a new repo to
@@ -163,6 +208,8 @@ with receipts, not a benchmark.
163
208
 
164
209
  ## How it works
165
210
 
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
+
166
213
  ### The mental model
167
214
 
168
215
  `okl` stores small, typed **notes** and the **links** between them.
@@ -258,6 +305,59 @@ pip install "org-knowledge-layer[mcp]" # MCP server for Claude Code / Cur
258
305
  pip install "org-knowledge-layer[all]"
259
306
  ```
260
307
 
308
+ ## What it costs, and how to turn it down
309
+
310
+ Installing okl is not free. It is worth knowing exactly what you are signing up for
311
+ before you wire it into every prompt, and every number below was measured on this repo's
312
+ own 199-record store rather than estimated.
313
+
314
+ **Per prompt, once the hook is installed:**
315
+
316
+ | | |
317
+ |---|---|
318
+ | Latency | **~0.11s** — one local SQLite query, no network in local mode |
319
+ | Context | **~2,300 tokens** at the default `--limit 12`, down to **~250** at `--format actions --limit 3` |
320
+
321
+ **Per session:** the Stop hook interrupts once at the end to ask what was learned. It
322
+ blocks the first stop only, and answering it is the whole write side of the loop.
323
+
324
+ **In your repo:** `okl init` writes `.okl/` (config, the local database, a `.gitignore`
325
+ covering both) and, if `.claude/` exists, two hook scripts plus their registration. It
326
+ also installs `.github/workflows/okl-verify.yml`, which runs the drift gate on every PR.
327
+ `okl scaffold` is separate and optional — nothing installs it unless you ask.
328
+
329
+ ### The knobs, cheapest first
330
+
331
+ ```bash
332
+ okl check --task "..." --format actions # imperatives only, ~60% smaller
333
+ okl check --task "..." --limit 3 # fewer records; the briefing says how many it trimmed
334
+ okl init --interests "python,security" # drop records tagged for stacks you do not use
335
+ ```
336
+
337
+ - **`--format actions`** is the single biggest saving and loses the least: you keep every
338
+ "when you see X → do Y" and drop the explanatory prose.
339
+ - **`--limit N`** caps how many records are drawn on. The briefing always reports what it
340
+ trimmed, so a short briefing can never quietly hide a miss.
341
+ - **`interests`** is the one to reach for on a mature shared store. Stack tags filter
342
+ exclusively — declaring `python` means records tagged `dotnet` stay out even when they
343
+ share a subject tag with something you asked for.
344
+ - **Scope records `repo:` rather than `org`** when a lesson is local. Org scope is a claim
345
+ that every project in the organization should see it, and it costs every project's
346
+ budget to be wrong about that.
347
+
348
+ ### Turning parts off
349
+
350
+ The hooks are registered in `.claude/settings.json`; delete the entry to stop one firing.
351
+ The pre-task hook is the read side and the Stop hook is the write side, and they are
352
+ independent — running the read without the write is a reasonable way to start.
353
+
354
+ Nothing is load-bearing on the hooks: `okl check` and `okl record` work from the terminal,
355
+ from CI, and through the MCP server whether or not any hook is installed.
356
+
357
+ To remove okl from a repo entirely, delete `.okl/`, the two hook scripts and their entries
358
+ in `.claude/settings.json`, and `.github/workflows/okl-verify.yml`. Nothing else was
359
+ written, and nothing outside that repo was touched.
360
+
261
361
  ## Wire a repo
262
362
 
263
363
  ```bash
@@ -371,7 +471,7 @@ okl metric # recurrence-after-arming: defect classes that came back in
371
471
 
372
472
  ## Subagents and small context budgets
373
473
 
374
- A full briefing costs roughly **4,400 tokens** — fine for a main session with a large
474
+ A full briefing costs roughly **2,300 tokens** — fine for a main session with a large
375
475
  window, punishing for a subagent working in a few thousand. That asymmetry matters
376
476
  because subagents are exactly where org rules get lost: a focused worker handling one
377
477
  subtask has the least context and the most need for "here is the mistake this codebase
@@ -1,35 +1,3 @@
1
- Metadata-Version: 2.5
2
- Name: org-knowledge-layer
3
- Version: 0.3.1
4
- Summary: Org Knowledge Layer — an installable sixth surface that carries encoded engineering lessons across repos.
5
- Author: Joshua Dell
6
- License: MIT
7
- License-File: LICENSE
8
- Keywords: agentic-coding,ai-engineering,encoding-loop,knowledge-layer,mcp
9
- Requires-Python: >=3.10
10
- Provides-Extra: all
11
- Requires-Dist: fastapi>=0.110; extra == 'all'
12
- Requires-Dist: mcp>=1.2; extra == 'all'
13
- Requires-Dist: psycopg[binary]>=3.1; extra == 'all'
14
- Requires-Dist: pydantic>=2; extra == 'all'
15
- Requires-Dist: uvicorn>=0.29; extra == 'all'
16
- Provides-Extra: dev
17
- Requires-Dist: fastapi>=0.110; extra == 'dev'
18
- Requires-Dist: httpx>=0.27; extra == 'dev'
19
- Requires-Dist: pydantic>=2; extra == 'dev'
20
- Requires-Dist: pytest>=8; extra == 'dev'
21
- Requires-Dist: ruff>=0.8; extra == 'dev'
22
- Requires-Dist: uvicorn>=0.29; extra == 'dev'
23
- Provides-Extra: mcp
24
- Requires-Dist: mcp>=1.2; extra == 'mcp'
25
- Provides-Extra: postgres
26
- Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
27
- Provides-Extra: service
28
- Requires-Dist: fastapi>=0.110; extra == 'service'
29
- Requires-Dist: pydantic>=2; extra == 'service'
30
- Requires-Dist: uvicorn>=0.29; extra == 'service'
31
- Description-Content-Type: text/markdown
32
-
33
1
  # okl — a shared knowledge layer for AI-assisted engineering
34
2
 
35
3
  > A small database of the specific lessons a codebase has learned — the bugs it
@@ -78,6 +46,17 @@ Two things ship in the package. They are not coequal:
78
46
  retrieved into an agent's context before a task, and go stale loudly when the code
79
47
  they describe moves on. Everything measured in [evals/REPORT.md](evals/REPORT.md)
80
48
  measures this.
49
+
50
+ It is worth being precise about what that store fills up with, because "lessons a
51
+ codebase has learned" invites the picture of a bug database. In the 161-record corpus
52
+ in [seed/](seed/) it is mostly not that: **90 Rules, 20 Decisions and 7 Gates against
53
+ 34 Defects** — conventions the code follows and trade-offs already settled, not a
54
+ ledger of things that broke. Count it yourself:
55
+
56
+ ```bash
57
+ python3 -c "import json,glob,collections; c=collections.Counter(
58
+ n['type'] for f in glob.glob('seed/*.json') for n in json.load(open(f))['nodes']); print(c)"
59
+ ```
81
60
  - **`okl scaffold`** is a starter kit for the in-repo discipline the store assumes: a
82
61
  lean canon file, mechanical gates, registries, a review agent, and an eval harness.
83
62
  It is useful on its own and it has never been measured. Use it to get a new repo to
@@ -195,6 +174,8 @@ with receipts, not a benchmark.
195
174
 
196
175
  ## How it works
197
176
 
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
+
198
179
  ### The mental model
199
180
 
200
181
  `okl` stores small, typed **notes** and the **links** between them.
@@ -290,6 +271,59 @@ pip install "org-knowledge-layer[mcp]" # MCP server for Claude Code / Cur
290
271
  pip install "org-knowledge-layer[all]"
291
272
  ```
292
273
 
274
+ ## What it costs, and how to turn it down
275
+
276
+ Installing okl is not free. It is worth knowing exactly what you are signing up for
277
+ before you wire it into every prompt, and every number below was measured on this repo's
278
+ own 199-record store rather than estimated.
279
+
280
+ **Per prompt, once the hook is installed:**
281
+
282
+ | | |
283
+ |---|---|
284
+ | Latency | **~0.11s** — one local SQLite query, no network in local mode |
285
+ | Context | **~2,300 tokens** at the default `--limit 12`, down to **~250** at `--format actions --limit 3` |
286
+
287
+ **Per session:** the Stop hook interrupts once at the end to ask what was learned. It
288
+ blocks the first stop only, and answering it is the whole write side of the loop.
289
+
290
+ **In your repo:** `okl init` writes `.okl/` (config, the local database, a `.gitignore`
291
+ covering both) and, if `.claude/` exists, two hook scripts plus their registration. It
292
+ also installs `.github/workflows/okl-verify.yml`, which runs the drift gate on every PR.
293
+ `okl scaffold` is separate and optional — nothing installs it unless you ask.
294
+
295
+ ### The knobs, cheapest first
296
+
297
+ ```bash
298
+ okl check --task "..." --format actions # imperatives only, ~60% smaller
299
+ okl check --task "..." --limit 3 # fewer records; the briefing says how many it trimmed
300
+ okl init --interests "python,security" # drop records tagged for stacks you do not use
301
+ ```
302
+
303
+ - **`--format actions`** is the single biggest saving and loses the least: you keep every
304
+ "when you see X → do Y" and drop the explanatory prose.
305
+ - **`--limit N`** caps how many records are drawn on. The briefing always reports what it
306
+ trimmed, so a short briefing can never quietly hide a miss.
307
+ - **`interests`** is the one to reach for on a mature shared store. Stack tags filter
308
+ exclusively — declaring `python` means records tagged `dotnet` stay out even when they
309
+ share a subject tag with something you asked for.
310
+ - **Scope records `repo:` rather than `org`** when a lesson is local. Org scope is a claim
311
+ that every project in the organization should see it, and it costs every project's
312
+ budget to be wrong about that.
313
+
314
+ ### Turning parts off
315
+
316
+ The hooks are registered in `.claude/settings.json`; delete the entry to stop one firing.
317
+ The pre-task hook is the read side and the Stop hook is the write side, and they are
318
+ independent — running the read without the write is a reasonable way to start.
319
+
320
+ Nothing is load-bearing on the hooks: `okl check` and `okl record` work from the terminal,
321
+ from CI, and through the MCP server whether or not any hook is installed.
322
+
323
+ To remove okl from a repo entirely, delete `.okl/`, the two hook scripts and their entries
324
+ in `.claude/settings.json`, and `.github/workflows/okl-verify.yml`. Nothing else was
325
+ written, and nothing outside that repo was touched.
326
+
293
327
  ## Wire a repo
294
328
 
295
329
  ```bash
@@ -403,7 +437,7 @@ okl metric # recurrence-after-arming: defect classes that came back in
403
437
 
404
438
  ## Subagents and small context budgets
405
439
 
406
- A full briefing costs roughly **4,400 tokens** — fine for a main session with a large
440
+ A full briefing costs roughly **2,300 tokens** — fine for a main session with a large
407
441
  window, punishing for a subagent working in a few thousand. That asymmetry matters
408
442
  because subagents are exactly where org rules get lost: a focused worker handling one
409
443
  subtask has the least context and the most need for "here is the mistake this codebase
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env bash
2
+ # Every figure a committed diagram publishes must trace to a committed receipt.
3
+ #
4
+ # The sixth-surface diagram rendered "50% → 6%" and "75% → 8%" under a heading reading
5
+ # "MEASURED (held-fixed A/B, blind judge≠generator)" for weeks, in the repo and on a
6
+ # public site. Those are the 2026-07-17 numbers that REPORT.md §7 quarantines as
7
+ # historical: the raw artifacts were never checked in, so they are not reproducible. The
8
+ # site's own prose cited the receipted figures right beside the image, so the page
9
+ # contradicted itself and a reader had no way to tell which was real.
10
+ #
11
+ # It survived because the drift stamp on the diagram record was earned by grepping for a
12
+ # couple of expected strings. "Some expected text is present" is not "no unsupported
13
+ # claim is present" — only the second is worth a verification stamp.
14
+ #
15
+ # Reads the repo, not the working tree, per the audit rule in CLAUDE.md: an uncommitted
16
+ # edit must not be able to pass or fail an audit that main would answer differently.
17
+ set -euo pipefail
18
+ cd "$(git rev-parse --show-toplevel)"
19
+
20
+ # A marker file, because the checks run inside a `while read` subshell and a variable set
21
+ # there cannot reach this scope.
22
+ work=$(mktemp -d)
23
+ trap 'rm -rf "$work"' EXIT
24
+
25
+ # Every diagram under docs/, rather than a hard-coded path: a diagram added later is
26
+ # covered the day it lands, not the day someone remembers to extend this file.
27
+ #
28
+ # `while read` rather than `mapfile`, which is bash 4+. macOS ships bash 3.2, so mapfile
29
+ # would have passed on ubuntu CI and failed for anyone running it locally — the shape of
30
+ # bug this repo already keeps a rule about.
31
+ git ls-files 'docs/*.excalidraw' | while IFS= read -r src; do
32
+ svg="${src%.excalidraw}.svg"
33
+ echo "-- $src"
34
+
35
+ # 1. Every receipt the diagram names is committed. A diagram that shows a percentage
36
+ # while naming no receipt at all is the original failure and fails here too.
37
+ named=$(git show "HEAD:$src" | grep -oE 'ab-[0-9]{8}-[0-9]{4}' | sort -u || true)
38
+ if [ -z "$named" ]; then
39
+ if git show "HEAD:$src" | grep -qE '[0-9]+ ?%'; then
40
+ echo " FAIL: shows a percentage but names no receipt"
41
+ touch "$work/fail"
42
+ else
43
+ echo " ok: publishes no figures"
44
+ fi
45
+ fi
46
+ for r in $named; do
47
+ if git ls-files --error-unmatch "evals/results/$r.json" >/dev/null 2>&1; then
48
+ echo " ok: $r has a committed receipt"
49
+ else
50
+ echo " FAIL: cites $r, which is not committed under evals/results/"
51
+ touch "$work/fail"
52
+ fi
53
+ done
54
+
55
+ # 2. The figures REPORT.md §7 marks as historical must never appear as current claims.
56
+ for q in '50% → 6%' '75% → 8%'; do
57
+ if git show "HEAD:$src" | grep -qF "$q"; then
58
+ echo " FAIL: publishes '$q', quarantined as historical in REPORT.md §7"
59
+ touch "$work/fail"
60
+ fi
61
+ done
62
+
63
+ # 3. The SVG is a render of the source, so it must carry the same receipts. This is
64
+ # what catches an edited .excalidraw whose SVG was never regenerated — the image is
65
+ # what people actually read, and it is the artifact that ships to the site.
66
+ if git ls-files --error-unmatch "$svg" >/dev/null 2>&1; then
67
+ for r in $named; do
68
+ if ! git show "HEAD:$svg" | grep -qF "$r"; then
69
+ echo " FAIL: $svg is missing $r — re-render it from the source"
70
+ touch "$work/fail"
71
+ fi
72
+ done
73
+ else
74
+ echo " FAIL: $svg is not committed; the source has no published render"
75
+ touch "$work/fail"
76
+ fi
77
+ done
78
+
79
+ if [ -e "$work/fail" ]; then
80
+ exit 1
81
+ fi
82
+ echo "DIAGRAM_FIGURES_RECEIPTED"
@@ -50,3 +50,10 @@ seed-file comments ("eval-integrity lessons are org-scoped") and the scaffold's
50
50
 
51
51
  - 2026-07-21: `messaging` added to the vocabulary during the .NET platform canon import — the
52
52
  broker/queue/event-driven rules fit no existing subject.
53
+ - 2026-09-02: `python` added. A code review of this repo found 190 records of which 75 were
54
+ tagged `dotnet` — CQRS, aggregates, outbox, DI scopes — governing a Python codebase that has
55
+ none of those things, while okl's own conventions had no subject to file under. `python-rag`
56
+ was the nearest existing tag and is wrong: it is a stack tag for one service's retrieval
57
+ pipeline, not a language. The distinction the vocabulary already draws (stacks vs subjects)
58
+ did not have a slot for "the language this is written in", and adding one is cheaper than
59
+ overloading a stack tag whose meaning other repos depend on.