capability-reasoning-kernel 0.4.1__tar.gz → 0.4.2__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 (92) hide show
  1. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/.env.example +3 -1
  2. capability_reasoning_kernel-0.4.2/.github/workflows/pages.yml +48 -0
  3. capability_reasoning_kernel-0.4.2/.pre-commit-config.yaml +33 -0
  4. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/CHANGELOG.md +49 -0
  5. capability_reasoning_kernel-0.4.2/CLAUDE.md +35 -0
  6. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/PKG-INFO +18 -4
  7. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/README.md +17 -3
  8. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/SECURITY.md +3 -3
  9. capability_reasoning_kernel-0.4.2/docs/ANALYSIS-2026-07.md +246 -0
  10. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/docs/DEVELOPMENT.md +4 -3
  11. capability_reasoning_kernel-0.4.2/docs/favicon.svg +6 -0
  12. capability_reasoning_kernel-0.4.2/docs/index.html +760 -0
  13. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/justfile +3 -0
  14. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/pyproject.toml +2 -1
  15. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/__init__.py +2 -1
  16. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/config.py +4 -2
  17. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/demo/reasoner_error.py +4 -2
  18. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/kernel/gate.py +8 -4
  19. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/reasoner/anthropic.py +7 -2
  20. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/reasoner/base.py +13 -3
  21. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/reasoner/factory.py +15 -2
  22. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/reasoner/openai.py +10 -3
  23. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/reasoner/parse.py +12 -4
  24. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/reasoner/roles.py +7 -4
  25. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/schemas/provenance.py +6 -3
  26. capability_reasoning_kernel-0.4.2/src/reasoning_kernel/tools/__init__.py +0 -0
  27. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/conftest.py +1 -1
  28. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_gate_provenance_declassify.py +22 -0
  29. capability_reasoning_kernel-0.4.2/tests/test_gate_unrestricted_readers.py +84 -0
  30. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_parse_with_schema.py +25 -0
  31. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_provenance_propagation.py +19 -0
  32. capability_reasoning_kernel-0.4.2/tests/test_reasoner_factory.py +50 -0
  33. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_reasoner_robustness.py +115 -3
  34. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/uv.lock +1 -1
  35. capability_reasoning_kernel-0.4.1/.pre-commit-config.yaml +0 -26
  36. capability_reasoning_kernel-0.4.1/tests/test_reasoner_factory.py +0 -27
  37. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/.github/workflows/ci.yml +0 -0
  38. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/.github/workflows/release.yml +0 -0
  39. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/.gitignore +0 -0
  40. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/.python-version +0 -0
  41. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/LICENSE +0 -0
  42. /capability_reasoning_kernel-0.4.1/src/reasoning_kernel/context/__init__.py → /capability_reasoning_kernel-0.4.2/docs/.nojekyll +0 -0
  43. {capability_reasoning_kernel-0.4.1/src/reasoning_kernel/demo → capability_reasoning_kernel-0.4.2/src/reasoning_kernel/context}/__init__.py +0 -0
  44. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/context/assembler.py +0 -0
  45. {capability_reasoning_kernel-0.4.1/src/reasoning_kernel/kernel → capability_reasoning_kernel-0.4.2/src/reasoning_kernel/demo}/__init__.py +0 -0
  46. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/demo/_report.py +0 -0
  47. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/demo/email_exfil.py +0 -0
  48. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/demo/live_run.py +0 -0
  49. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/demo/merge.py +0 -0
  50. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/demo/run_limits.py +0 -0
  51. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/demo/subkernel.py +0 -0
  52. {capability_reasoning_kernel-0.4.1/src/reasoning_kernel/memory → capability_reasoning_kernel-0.4.2/src/reasoning_kernel/kernel}/__init__.py +0 -0
  53. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/kernel/effects.py +0 -0
  54. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/kernel/interpreter.py +0 -0
  55. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/kernel/taint.py +0 -0
  56. {capability_reasoning_kernel-0.4.1/src/reasoning_kernel/reasoner → capability_reasoning_kernel-0.4.2/src/reasoning_kernel/memory}/__init__.py +0 -0
  57. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/memory/store.py +0 -0
  58. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/memory/trace.py +0 -0
  59. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/py.typed +0 -0
  60. {capability_reasoning_kernel-0.4.1/src/reasoning_kernel/schemas → capability_reasoning_kernel-0.4.2/src/reasoning_kernel/reasoner}/__init__.py +0 -0
  61. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/reasoner/deepseek.py +0 -0
  62. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/reasoner/fake.py +0 -0
  63. {capability_reasoning_kernel-0.4.1/src/reasoning_kernel/tools → capability_reasoning_kernel-0.4.2/src/reasoning_kernel/schemas}/__init__.py +0 -0
  64. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/schemas/capability.py +0 -0
  65. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/schemas/ids.py +0 -0
  66. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/schemas/limits.py +0 -0
  67. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/schemas/plan.py +0 -0
  68. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/schemas/policy.py +0 -0
  69. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/schemas/registry.py +0 -0
  70. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/schemas/trace.py +0 -0
  71. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/schemas/values.py +0 -0
  72. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/tools/demo_mail.py +0 -0
  73. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/src/reasoning_kernel/tools/registry.py +0 -0
  74. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_capability.py +0 -0
  75. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_composition.py +0 -0
  76. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_declass_policy.py +0 -0
  77. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_demo_email_exfil.py +0 -0
  78. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_demo_mail_tools.py +0 -0
  79. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_gate_capability.py +0 -0
  80. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_interpreter_loop.py +0 -0
  81. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_invariant_a.py +0 -0
  82. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_merge.py +0 -0
  83. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_no_bypass_conformance.py +0 -0
  84. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_plan_schema.py +0 -0
  85. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_providers_live.py +0 -0
  86. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_public_api.py +0 -0
  87. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_registry.py +0 -0
  88. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_run_limits.py +0 -0
  89. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_store.py +0 -0
  90. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_subject_provenance.py +0 -0
  91. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_subkernel.py +0 -0
  92. {capability_reasoning_kernel-0.4.1 → capability_reasoning_kernel-0.4.2}/tests/test_trace_append_only.py +0 -0
@@ -7,7 +7,9 @@ DEEPSEEK_API_KEY=
7
7
 
8
8
  # Optional overrides (defaults shown)
9
9
  # RK_LLM_PROVIDER_DEFAULT=anthropic
10
- # RK_LLM_MODEL_ANTHROPIC=claude-sonnet-4-6 # or claude-opus-4-8 (more capable, different pricing)
10
+ # RK_LLM_MODEL_ANTHROPIC=claude-sonnet-5 # or claude-opus-4-8 (more capable, different pricing)
11
11
  # RK_LLM_MODEL_OPENAI=gpt-5.5 # or gpt-5.5-pro (more capable, different pricing)
12
12
  # RK_LLM_MODEL_DEEPSEEK=deepseek-v4-flash # or deepseek-v4-pro (more capable, different pricing)
13
13
  # RK_DEEPSEEK_BASE_URL=https://api.deepseek.com
14
+ # RK_LLM_TIMEOUT_SECONDS=120.0 # per-request SDK timeout passed to provider clients
15
+ # RK_LLM_MAX_TOKENS=4096 # default max_tokens for structured-output calls
@@ -0,0 +1,48 @@
1
+ name: Pages
2
+
3
+ # Deploy the static landing page in `docs/` to GitHub Pages. No build step — the page is
4
+ # self-contained HTML (CSS + SVG inline). First-time setup requires the repository's
5
+ # Settings → Pages → Source to be set to "GitHub Actions".
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ paths:
10
+ - "docs/**"
11
+ - ".github/workflows/pages.yml"
12
+ workflow_dispatch:
13
+
14
+ permissions:
15
+ contents: read
16
+ pages: write
17
+ id-token: write
18
+
19
+ # Allow one concurrent deployment; don't cancel an in-progress publish.
20
+ concurrency:
21
+ group: pages
22
+ cancel-in-progress: false
23
+
24
+ jobs:
25
+ deploy:
26
+ runs-on: ubuntu-latest
27
+ environment:
28
+ name: github-pages
29
+ url: ${{ steps.deployment.outputs.page_url }}
30
+ steps:
31
+ # Actions pinned to immutable commit SHAs (supply-chain hardening); tag in comment.
32
+ - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
33
+ with:
34
+ persist-credentials: false
35
+
36
+ - name: Configure Pages
37
+ uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
38
+ with:
39
+ enablement: true # self-enable Pages (build_type=workflow) on first run — no manual Settings step
40
+
41
+ - name: Upload artifact
42
+ uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
43
+ with:
44
+ path: docs
45
+
46
+ - name: Deploy to GitHub Pages
47
+ id: deployment
48
+ uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
@@ -0,0 +1,33 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v5.0.0
4
+ hooks:
5
+ - id: check-yaml
6
+ - id: check-toml
7
+ - id: check-added-large-files
8
+ args: ["--maxkb=1000"]
9
+ - id: end-of-file-fixer
10
+ - id: trailing-whitespace
11
+
12
+ # Ruff and pyright run as local hooks through uv so the hook, `just lint`/`just typecheck`,
13
+ # and CI all execute the exact versions pinned in uv.lock — no pinned-mirror drift.
14
+ - repo: local
15
+ hooks:
16
+ - id: ruff-check
17
+ name: ruff check --fix (uv.lock version, same as `just fix`)
18
+ language: system
19
+ entry: uv run ruff check --fix src/ tests/
20
+ pass_filenames: false
21
+ types_or: [python, pyi]
22
+ - id: ruff-format
23
+ name: ruff format (uv.lock version, same as `just fix`)
24
+ language: system
25
+ entry: uv run ruff format src/ tests/
26
+ pass_filenames: false
27
+ types_or: [python, pyi]
28
+ - id: pyright
29
+ name: pyright (same scope as `just typecheck` — strict dirs set in pyproject)
30
+ language: system
31
+ entry: uv run pyright
32
+ pass_filenames: false
33
+ types: [python]
@@ -4,6 +4,54 @@ All notable changes to this project are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
5
5
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.4.2] - 2026-07-10
8
+
9
+ A correctness release for the gate's provenance stage, plus fail-closed hardening of the provider
10
+ integrations. No API removals; two behavior changes in the gate/taint semantics (both strictly
11
+ safety-improving) and stricter model resolution.
12
+
13
+ ### Fixed
14
+
15
+ - **Gate: a tainted value with unrestricted `readers` no longer skips declassification.**
16
+ `readers=None` is reserved for purely trusted data, but `quarantine_label` preserves the source's
17
+ readers — so a Q-LLM parse of a *trusted* const yielded a value that was tainted (Q_LLM) yet
18
+ carried `readers=None`, and the gate's stage 3 auto-permitted it into a WRITE without consulting
19
+ the declassification policy. The fast-path now requires an **explicit** readers set covering the
20
+ tool's required capabilities; unscoped tainted flows go through the declassifier. (Regression
21
+ tests at gate level and end-to-end: a recipient laundered through the Q-LLM is now blocked by the
22
+ policy instead of auto-committed.)
23
+ - **Taint: `DERIVED` is no longer counted as an untrusted source.** Merging values derived solely
24
+ from the trusted query no longer manufactures taint (which caused spurious denials in policies
25
+ keyed on `is_tainted`); taint derives only from the actual presence of `TOOL_READ` / `Q_LLM` in
26
+ the joined sources. `DERIVED` remains on the label as an audit marker. The two fixes land
27
+ together deliberately: each previously masked the other.
28
+ - **`RK_LLM_MAX_TOKENS` now has an effect.** `call_structured` / `parse_with_schema` defaulted to a
29
+ private module constant, silently ignoring the `settings.llm_max_tokens` override; they now read
30
+ settings (lazily) as the single source of truth.
31
+
32
+ ### Changed
33
+
34
+ - **Transport faults fail closed with an audit record.** Provider API errors (rate limit exhausted,
35
+ 5xx, network, SDK timeout) are wrapped in a new `TransportError(ReasonerError)` by the Anthropic
36
+ and OpenAI/Deepseek providers, so an in-flight run ends with a terminal trace event
37
+ (`PlanRejected`/`RunErrored`) instead of an unrecorded traceback.
38
+ - **Model resolution is strict and construction-time.** `PLLM`/`QLLM` no longer default to
39
+ `model="fake"` (a real provider without an explicit model got a 404 deep inside a run); an
40
+ omitted model resolves via `default_model_for(provider.name)` at construction. `default_model_for`
41
+ now raises `ValueError` for unknown providers instead of silently falling back to the Anthropic
42
+ model, and maps `"fake"` to the FakeProvider's ignored sentinel. `call_structured` requires
43
+ `model=` explicitly.
44
+ - **Anthropic default model refreshed**: `claude-sonnet-4-6` → `claude-sonnet-5` (comment variant
45
+ stays `claude-opus-4-8`); `.env.example` and `docs/DEVELOPMENT.md` updated, including the
46
+ previously undocumented `RK_LLM_TIMEOUT_SECONDS` / `RK_LLM_MAX_TOKENS` overrides.
47
+ - **Branch coverage enabled** (`coverage: branch = true`): line coverage alone hid the gate's
48
+ untested readers fast-path; the new gate tests pin both directions of that branch.
49
+ - **Toolchain drift eliminated.** pre-commit now runs ruff and pyright as local hooks through
50
+ `uv run` (same versions as `uv.lock`, same scope as `just lint`/`just typecheck` and CI, instead
51
+ of a stale pinned ruff mirror and a partial pyright scope); a new aggregate `just check` runs
52
+ lint + typecheck + test exactly as CI does. `CLAUDE.md` and the README role map now include
53
+ `tools/` (the registry is part of the no-bypass argument).
54
+
7
55
  ## [0.4.1] - 2026-05-31
8
56
 
9
57
  ### Changed
@@ -95,6 +143,7 @@ Initial reference implementation of the Reasoning Kernel pattern (strong / CaMeL
95
143
  invariants, no-effect-bypasses-the-Verifier by construction, the deterministic declassification seam,
96
144
  and the worked email-exfiltration demo.
97
145
 
146
+ [0.4.2]: https://github.com/gianlucamazza/reasoning-kernel/releases/tag/v0.4.2
98
147
  [0.4.1]: https://github.com/gianlucamazza/reasoning-kernel/releases/tag/v0.4.1
99
148
  [0.4.0]: https://github.com/gianlucamazza/reasoning-kernel/releases/tag/v0.4.0
100
149
  [0.3.0]: https://github.com/gianlucamazza/reasoning-kernel/releases/tag/v0.3.0
@@ -0,0 +1,35 @@
1
+ # CLAUDE.md — reasoning-kernel
2
+
3
+ Framework-agnostic Python **reference implementation** of the Reasoning Kernel pattern (strong,
4
+ CaMeL-like form; Debenedetti et al. 2025): the LLM is treated as untrusted compute, mediated by
5
+ context on input and verification on output. Published to PyPI as `capability-reasoning-kernel`.
6
+ Remote: `gianlucamazza/reasoning-kernel`.
7
+
8
+ ## The two invariants (do not violate)
9
+ - **A** — the reasoner never sees raw reality: every model call gets a system-assembled, inspectable
10
+ context (`src/reasoning_kernel/context/`).
11
+ - **B** — the reasoner never commits reality: no model output becomes a durable effect except through
12
+ the single deterministic gate (`src/reasoning_kernel/kernel/gate.py`).
13
+
14
+ The pattern fixes a *topology*, not a policy guarantee — keep mediation/verification at those
15
+ boundaries by construction.
16
+
17
+ ## Stack & layout
18
+ - Python, `uv` (uv.lock), `pyproject.toml`, `just`. Lint Ruff, types pyright.
19
+ - `src/reasoning_kernel/`: `context/` · `kernel/` (gate) · `reasoner/` · `memory/` · `schemas/` ·
20
+ `tools/` (registry — sole holder of tool callables, part of the no-bypass argument) · `demo/`
21
+
22
+ ## Commands
23
+ ```bash
24
+ just check # lint + typecheck + test (everything CI runs)
25
+ just lint # ruff
26
+ just fix # ruff --fix
27
+ just typecheck # pyright
28
+ just test-live # tests against live model
29
+ just demo # run demo (also: demo-live, demo-subkernel, demo-limits, demo-merge, demo-reasoner-error)
30
+ uv sync # install/refresh env (--all-extras for providers + dev tools)
31
+ ```
32
+
33
+ ## Conventions
34
+ - It's a reference impl + spec, not a turn-key product — favor clarity and conformance over features.
35
+ - Changes touching invariants A/B must preserve the topology; update `docs/` and the README spec.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: capability-reasoning-kernel
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: Reference implementation of the Reasoning Kernel pattern (strong / CaMeL-like form)
5
5
  Project-URL: Homepage, https://github.com/gianlucamazza/reasoning-kernel
6
6
  Project-URL: Repository, https://github.com/gianlucamazza/reasoning-kernel
@@ -32,6 +32,15 @@ Description-Content-Type: text/markdown
32
32
 
33
33
  # Reasoning Kernel
34
34
 
35
+ [![PyPI](https://img.shields.io/pypi/v/capability-reasoning-kernel)](https://pypi.org/project/capability-reasoning-kernel/)
36
+ [![Python](https://img.shields.io/pypi/pyversions/capability-reasoning-kernel)](https://pypi.org/project/capability-reasoning-kernel/)
37
+ [![CI](https://github.com/gianlucamazza/reasoning-kernel/actions/workflows/ci.yml/badge.svg)](https://github.com/gianlucamazza/reasoning-kernel/actions/workflows/ci.yml)
38
+ [![License](https://img.shields.io/github/license/gianlucamazza/reasoning-kernel)](LICENSE)
39
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
40
+ [![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](https://microsoft.github.io/pyright/)
41
+
42
+ **[Live site →](https://gianlucamazza.github.io/reasoning-kernel/)**
43
+
35
44
  **The problem.** An LLM agent that reads untrusted data — an email, a web page, a tool result — can be
36
45
  hijacked by instructions hidden in that data and then act on them: leak your contacts, send mail, call
37
46
  tools on your behalf. This is a reference implementation of an architecture where such a hijack
@@ -82,6 +91,7 @@ The trusted, deterministic kernel is the **interpreter + capability/provenance g
82
91
  | Reasoner(s) | `reasoner/` (multi-provider) | a provider or the interface |
83
92
  | Conductor | `kernel/interpreter.py` | the execution loop |
84
93
  | Verifier | `kernel/gate.py`, `effects.py` | verification policy |
94
+ | Tool catalog | `tools/registry.py` | sole holder of tool callables |
85
95
  | Memory / Trace | `memory/` | durability / audit format |
86
96
 
87
97
  Reasoner providers: Anthropic, OpenAI, Deepseek (OpenAI-compatible, reusing the `openai` SDK via a
@@ -184,15 +194,19 @@ result = kernel.run(ctx) # RunResult(trace, committed); committed is
184
194
 
185
195
  - **Provenance is multi-dimensional**: a `ProvenanceLabel` carries *origin* (`sources`), *where it may
186
196
  flow* (`readers`), and *whose data it is* (`subjects`). Third-party data is never auto-released into a
187
- WRITE — even to the requesting user — and the Q-LLM cannot launder any of these dimensions.
197
+ WRITE — even to the requesting user — and the Q-LLM cannot launder any of these dimensions. A tainted
198
+ value whose flow was never scoped (`readers=None` is reserved for purely trusted data) is likewise
199
+ never auto-permitted into a WRITE: it is routed to the declassifier like any other tainted flow.
188
200
  - **Invariant A is typed**: the trusted channel is a `TrustedQuery` (text + label); `const`/inline
189
201
  literals DERIVE their label from it, so the trust assumption is explicit rather than by convention.
190
202
  - **Termination**: `RunLimits` bounds steps / effects / q-parses (and an optional per-call timeout); a
191
203
  run exceeding a bound aborts closed (`RunAborted`), committing nothing further. The timeout abort is
192
204
  prompt — it does not block waiting on the hung call (`kernel/interpreter.py:_call_reasoner`).
193
205
  - **Reasoner failure is fail-closed**: a provider that returns no usable output (empty / refused /
194
- malformed) raises `ReasonerError` (`reasoner/base.py`); the Conductor records it and commits
195
- nothing, rather than crashing or acting on a partial result. Treating the model as untrusted compute
206
+ malformed) raises `ReasonerError` (`reasoner/base.py`), and a provider call that fails in transport
207
+ (rate limit exhausted, 5xx, network fault) raises its `TransportError` subclass; either way the
208
+ Conductor records a terminal trace event and commits nothing, rather than crashing or acting on a
209
+ partial result. Treating the model as untrusted compute
196
210
  means a flaky reasoner can never produce a half-applied effect.
197
211
  - **Capability composition (§5.4)**: every reasoner is bound to a `CapabilitySet`; the kernel rejects a
198
212
  reasoner whose grant exceeds the dispatcher's — a child can never widen authority. A `SubKernelStep`
@@ -1,5 +1,14 @@
1
1
  # Reasoning Kernel
2
2
 
3
+ [![PyPI](https://img.shields.io/pypi/v/capability-reasoning-kernel)](https://pypi.org/project/capability-reasoning-kernel/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/capability-reasoning-kernel)](https://pypi.org/project/capability-reasoning-kernel/)
5
+ [![CI](https://github.com/gianlucamazza/reasoning-kernel/actions/workflows/ci.yml/badge.svg)](https://github.com/gianlucamazza/reasoning-kernel/actions/workflows/ci.yml)
6
+ [![License](https://img.shields.io/github/license/gianlucamazza/reasoning-kernel)](LICENSE)
7
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
8
+ [![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](https://microsoft.github.io/pyright/)
9
+
10
+ **[Live site →](https://gianlucamazza.github.io/reasoning-kernel/)**
11
+
3
12
  **The problem.** An LLM agent that reads untrusted data — an email, a web page, a tool result — can be
4
13
  hijacked by instructions hidden in that data and then act on them: leak your contacts, send mail, call
5
14
  tools on your behalf. This is a reference implementation of an architecture where such a hijack
@@ -50,6 +59,7 @@ The trusted, deterministic kernel is the **interpreter + capability/provenance g
50
59
  | Reasoner(s) | `reasoner/` (multi-provider) | a provider or the interface |
51
60
  | Conductor | `kernel/interpreter.py` | the execution loop |
52
61
  | Verifier | `kernel/gate.py`, `effects.py` | verification policy |
62
+ | Tool catalog | `tools/registry.py` | sole holder of tool callables |
53
63
  | Memory / Trace | `memory/` | durability / audit format |
54
64
 
55
65
  Reasoner providers: Anthropic, OpenAI, Deepseek (OpenAI-compatible, reusing the `openai` SDK via a
@@ -152,15 +162,19 @@ result = kernel.run(ctx) # RunResult(trace, committed); committed is
152
162
 
153
163
  - **Provenance is multi-dimensional**: a `ProvenanceLabel` carries *origin* (`sources`), *where it may
154
164
  flow* (`readers`), and *whose data it is* (`subjects`). Third-party data is never auto-released into a
155
- WRITE — even to the requesting user — and the Q-LLM cannot launder any of these dimensions.
165
+ WRITE — even to the requesting user — and the Q-LLM cannot launder any of these dimensions. A tainted
166
+ value whose flow was never scoped (`readers=None` is reserved for purely trusted data) is likewise
167
+ never auto-permitted into a WRITE: it is routed to the declassifier like any other tainted flow.
156
168
  - **Invariant A is typed**: the trusted channel is a `TrustedQuery` (text + label); `const`/inline
157
169
  literals DERIVE their label from it, so the trust assumption is explicit rather than by convention.
158
170
  - **Termination**: `RunLimits` bounds steps / effects / q-parses (and an optional per-call timeout); a
159
171
  run exceeding a bound aborts closed (`RunAborted`), committing nothing further. The timeout abort is
160
172
  prompt — it does not block waiting on the hung call (`kernel/interpreter.py:_call_reasoner`).
161
173
  - **Reasoner failure is fail-closed**: a provider that returns no usable output (empty / refused /
162
- malformed) raises `ReasonerError` (`reasoner/base.py`); the Conductor records it and commits
163
- nothing, rather than crashing or acting on a partial result. Treating the model as untrusted compute
174
+ malformed) raises `ReasonerError` (`reasoner/base.py`), and a provider call that fails in transport
175
+ (rate limit exhausted, 5xx, network fault) raises its `TransportError` subclass; either way the
176
+ Conductor records a terminal trace event and commits nothing, rather than crashing or acting on a
177
+ partial result. Treating the model as untrusted compute
164
178
  means a flaky reasoner can never produce a half-applied effect.
165
179
  - **Capability composition (§5.4)**: every reasoner is bound to a `CapabilitySet`; the kernel rejects a
166
180
  reasoner whose grant exceeds the dispatcher's — a child can never widen authority. A `SubKernelStep`
@@ -34,8 +34,8 @@ permissive policy written on top of it.
34
34
 
35
35
  | Version | Supported |
36
36
  |---------|-----------|
37
- | `0.3.x` | ✅ |
38
- | `< 0.3` | ❌ |
37
+ | `0.4.x` | ✅ |
38
+ | `< 0.4` | ❌ |
39
39
 
40
40
  ## Reporting a vulnerability
41
41
 
@@ -47,4 +47,4 @@ Please report privately — do **not** open a public issue for a suspected vulne
47
47
  Include a minimal reproduction (a plan + tool/policy setup that commits an effect that should have been
48
48
  blocked), the expected vs. actual behavior, and the affected version/commit. Expect an initial
49
49
  acknowledgement within a few days. As a single-maintainer reference project there is no formal SLA, but
50
- mechanism-level issues are taken seriously and will be addressed in a `0.3.x` patch.
50
+ mechanism-level issues are taken seriously and will be addressed in a `0.4.x` patch.
@@ -0,0 +1,246 @@
1
+ # Analisi del progetto e piano di miglioramento — luglio 2026
2
+
3
+ Analisi completa della codebase alla versione **0.4.1** (branch `main`, commit `0189752`),
4
+ condotta su tre assi: core fidato (kernel/memory/context), livello di integrazione LLM
5
+ (reasoner/config/tools), e qualità/DevEx (test, CI, packaging, documentazione).
6
+
7
+ ## Stato di avanzamento (aggiornato 2026-07-10)
8
+
9
+ **✅ Tranche 1 (0.4.2) — completata su questo branch.** Corretti e testati: A1 (gate:
10
+ `readers=None` tainted → declassificazione, con regressione end-to-end), M1 (`DERIVED` fuori
11
+ da `_UNTRUSTED`), fast-path del gate testato + branch coverage attiva, `RK_LLM_MAX_TOKENS`
12
+ cablato, `model` risolto strettamente alla costruzione dei ruoli (`default_model_for` rifiuta
13
+ provider ignoti), `TransportError` per i guasti di trasporto Anthropic/OpenAI/Deepseek
14
+ (esportato dall'API pubblica), test `AnthropicProvider`, default `claude-sonnet-5`,
15
+ CHANGELOG/README/DEVELOPMENT aggiornati. Suite: 111 test, coverage ~94% (branch).
16
+
17
+ **✅ Consolidamento drift (stesso branch):** pre-commit riallineato a justfile/CI (ruff e
18
+ pyright come hook local via `uv run`, stessa versione di uv.lock e stesso scope di
19
+ `just typecheck`), target aggregato `just check`, `tools/` aggiunto al layout di CLAUDE.md e
20
+ alla role map del README.
21
+
22
+ > Nota: il passaggio a `uv sync --all-extras --locked` in `.github/workflows/ci.yml` (entrambi
23
+ > i job) è pronto ma NON è su questo branch: il token dell'app GitHub non ha il permesso
24
+ > `workflows`, quindi va applicato manualmente insieme agli altri interventi CI aperti.
25
+
26
+ **⬜ Ancora aperti:** tranche 0.5.0 (RunLimits globali sui sub-kernel — M3/1.3,
27
+ `ToolExecutionError` + evento di trace, fallback strict-schema robusto — 1.1, trace writer
28
+ per-run — M4, export `LLMResult`/`LLMUsage` — 3.1), infra release (build unica promossa +
29
+ gate di test/versione — 2.1/2.2, dependabot — 2.3, pinning SHA — 2.4, matrice 3.14 — 2.7,
30
+ `permissions` in ci.yml — 2.5, `uv build` + `twine check` — 2.9), spec/docs (semantica limits
31
+ — 4.3, regola "argomenti che escono ⇒ WRITE" — M2, CONFORMANCE.md — 4.4, metadati pyproject
32
+ — §3), e i P2 restanti (hardening prompt, test minori, audit trace).
33
+
34
+ ## Stato di salute (baseline verificata)
35
+
36
+ | Check | Esito |
37
+ |---|---|
38
+ | `ruff check` + `ruff format --check` | pulito (67 file) |
39
+ | `pyright` (strict su schemas/kernel/memory) | 0 errori |
40
+ | `pytest` (suite non-live) | 98 passed |
41
+ | Coverage | 92.19% (soglia 85% applicata in CI) |
42
+
43
+ L'architettura di sicurezza regge: nessun bypass strutturale del gate (il registry è
44
+ l'unico detentore dei callable, il dispatcher chiama `Gate.check` incondizionatamente),
45
+ validazione del Plan solida (id unici, ref forward-only, niente cicli), clamp monotono dei
46
+ grant nei sub-kernel, join delle label monotono. I finding sotto riguardano punti specifici,
47
+ non la topologia del pattern.
48
+
49
+ ---
50
+
51
+ ## P0 — Correttezza del gate (invariante B)
52
+
53
+ ### A1. Valore tainted con `readers=None` salta la declassificazione ⚠️
54
+
55
+ `kernel/gate.py:71-77` + `kernel/taint.py:58-68` + `schemas/provenance.py:43,54-58`
56
+
57
+ `provenance.py` documenta `readers=None` = "unrestricted, **reserved for purely trusted
58
+ data**", ma `quarantine_label()` preserva i `readers` della sorgente: un `q_parse` su un
59
+ const trusted produce un valore **tainted (Q_LLM) con `readers=None`**. Nello stage 3 del
60
+ gate, `allows_reader()` restituisce `True` quando `readers is None` → `permitted=True`,
61
+ la declassificazione viene saltata.
62
+
63
+ Scenario: piano con `const → q_parse(const) → send_email(to=ref(q_parse))`. L'output del
64
+ Q-LLM (compute probabilistico non fidato, per spec "verification never depends on trusting
65
+ a probabilistic component") diventa argomento di una WRITE senza alcun vincolo
66
+ deterministico oltre lo schema.
67
+
68
+ **Fix (minimale, fail-closed):** in `gate.check`, per gli argomenti tainted trattare
69
+ `readers is None` come *non* auto-permesso e instradare a declassificazione. In alternativa
70
+ (più invasivo) non produrre mai label tainted con `readers=None` in `quarantine_label` /
71
+ `result_label`.
72
+
73
+ ### M1. `DERIVED` è considerato untrusted: il merge di soli valori trusted diventa tainted
74
+
75
+ `schemas/provenance.py:34` + `kernel/taint.py:30-31`
76
+
77
+ `_UNTRUSTED = {TOOL_READ, Q_LLM, DERIVED}` e `join_labels` aggiunge `DERIVED` a ogni join
78
+ con >1 input: `merge(const, const)` risulta tainted anche se tutti gli input derivano dalla
79
+ query trusted → denial spuri nelle policy che usano `is_tainted`.
80
+
81
+ **Fix:** togliere `DERIVED` da `_UNTRUSTED`; il taint deve derivare solo dalla presenza
82
+ effettiva di `TOOL_READ`/`Q_LLM`. `DERIVED` resta come marcatore di audit.
83
+
84
+ > **A1 e M1 vanno corretti insieme**: oggi si mascherano a vicenda (il merge over-taintato
85
+ > passa comunque grazie al buco readers=None; chiudere solo A1 bloccherebbe i merge di dati
86
+ > trusted).
87
+
88
+ ### Test mancante sul fast-path del gate
89
+
90
+ `tests/` + `gate.py:71-78` — il ramo "readers permettono → niente declassificazione" nel
91
+ caso positivo non ha alcun test, e la line coverage al 100% lo nasconde perché
92
+ `branch = true` non è abilitato in `[tool.coverage.run]`. Aggiungere il test (arg tainted
93
+ con readers che coprono le required_caps + declassificatore `DenyAll` mai consultato) e
94
+ abilitare la branch coverage.
95
+
96
+ ---
97
+
98
+ ## P1 — Robustezza e semantica da definire
99
+
100
+ ### RunLimits per-interprete, non globali (amplificazione via sub-kernel)
101
+
102
+ `kernel/interpreter.py:199-207` — ogni sub-kernel riceve gli stessi `limits` ma contatori
103
+ azzerati: con fan-out di sub-kernel il totale di effetti scala ~`E·S^D`. Il sub-planner è
104
+ per costruzione prompt-injectable, quindi un blob malevolo può moltiplicare i tentativi di
105
+ effetto entro il grant. Né README né docstring dichiarano questa semantica.
106
+ **Fix:** budget condiviso (contatori globali del run radice) passato ai sub-Interpreter;
107
+ in ogni caso documentare la semantica scelta in `limits.py` e nel README, con un test che
108
+ la pinna.
109
+
110
+ ### Errori di trasporto dei provider → trace senza evento terminale
111
+
112
+ `kernel/interpreter.py:53` — `_PLAN_ERRORS` non copre `anthropic.APIError` /
113
+ `openai.APIError` (429 esauriti, 5xx, timeout SDK): il traceback propaga e la trace
114
+ (promessa come audit log completo) termina senza `RunErrored`/`RunAborted`.
115
+ **Fix:** wrappare le eccezioni API in un `TransportError(ReasonerError)` nei provider.
116
+ Stesso tema per i tool callable (`interpreter.py:126-127`): un `TypeError` nel callable
117
+ propaga senza evento di trace — introdurre `ToolExecutionError` con evento dedicato.
118
+
119
+ ### Fallback JSON-mode basato su substring del messaggio d'errore
120
+
121
+ `reasoner/openai.py:19-21` — `_is_strict_schema_error` fa match su `"response_format"` nel
122
+ testo dell'eccezione: fragile in entrambe le direzioni (fallback che non scatta con
123
+ messaggi diversi; 400 non correlati che innescano un secondo round-trip). È il meccanismo
124
+ che regge `DeepseekProvider`.
125
+ **Fix:** ispezionare i campi strutturati di `openai.BadRequestError` (`exc.param` /
126
+ `exc.body`), o tentare il fallback una sola volta su qualunque `BadRequestError`
127
+ ri-sollevando l'errore originale se fallisce anche quello.
128
+
129
+ ### `model="fake"` come default sui ruoli con provider reali
130
+
131
+ `reasoner/roles.py:55,80` + `parse.py:23` — `PLLM(provider)` senza `model=` spedisce
132
+ `model="fake"` all'API reale → 404 a runtime lontano dal punto d'errore.
133
+ **Fix:** `model` keyword-only obbligatorio, o risoluzione automatica via
134
+ `default_model_for(provider.name)`.
135
+
136
+ ### `RK_LLM_MAX_TOKENS` non ha alcun effetto
137
+
138
+ `config.py:38` vs `reasoner/parse.py:14` — `llm_max_tokens` è definito in Settings ma
139
+ `parse.py` usa una propria costante `DEFAULT_MAX_TOKENS = 4096`: l'override in `.env` è
140
+ silenziosamente ignorato. **Fix:** far leggere `settings.llm_max_tokens` (import lazy) e
141
+ rimuovere la costante. Collegato: `PLLM.plan()` non espone `max_tokens` e nessun provider
142
+ controlla `finish_reason`/`stop_reason` == troncamento (errore criptico invece di
143
+ `ReasonerError("output truncated")`).
144
+
145
+ ### Default modello Anthropic di una generazione indietro
146
+
147
+ `config.py:32` + `.env.example:10` — `claude-sonnet-4-6` funziona ancora ma è superato:
148
+ aggiornare a `claude-sonnet-5` (o `claude-opus-4-8`), allineando `.env.example`. Il beta
149
+ header `structured-outputs-2025-11-13` in `anthropic.py:15,36` è ormai superfluo (feature
150
+ GA) e può essere rimosso al prossimo bump dell'SDK.
151
+
152
+ ### Release workflow: artefatto non promosso e nessun gate
153
+
154
+ `.github/workflows/release.yml` — i job `testpypi` e `pypi` fanno ciascuno `uv build`:
155
+ l'artefatto pubblicato su PyPI non è quello validato su TestPyPI. Inoltre il publish parte
156
+ sul tag senza rieseguire la suite né verificare tag ↔ `project.version`.
157
+ **Fix:** job `build` unico + `upload/download-artifact`; step di verifica versione;
158
+ job di test come `needs:` dei publish. (Trusted publishing e permessi sono già corretti.)
159
+
160
+ ### Stage provenance solo sulle WRITE: argomenti dei READ come canale di uscita
161
+
162
+ `kernel/gate.py:62` — un tool READ con argomenti che lasciano il sistema (es.
163
+ `web_fetch(url)`, `search(query)`) trasmetterebbe dati tainted senza controllo di
164
+ provenance. Non sfruttabile oggi (i READ della demo non hanno argomenti), ma è un gap di
165
+ spec per chi conforma il proprio sistema.
166
+ **Fix minimo:** documentare in `capability.py` e nella spec che ogni tool i cui argomenti
167
+ attraversano il confine DEVE essere registrato WRITE. Più robusto: flag
168
+ `args_leave_boundary` su `ToolSpec`.
169
+
170
+ ---
171
+
172
+ ## P2 — Qualità, API, DevEx
173
+
174
+ **API pubblica**
175
+ - `LLMProvider` è esportato ma `LLMResult`/`LLMUsage` (necessari per implementarlo) no —
176
+ aggiungerli a `__all__` (`__init__.py`).
177
+ - `supports_structured_output` dichiarato nel Protocol ma mai consultato: usarlo o rimuoverlo.
178
+ - `default_model_for` (`factory.py:39`) fa fallback silenzioso al modello Anthropic per
179
+ provider ignoti — meglio errore esplicito.
180
+ - Commento in `config.py:31` elenca `"fake"` tra i provider validi ma la factory lo rifiuta
181
+ (scelta intenzionale): allineare il commento.
182
+
183
+ **Interpreter/trace (audit)**
184
+ - `Interpreter`/`TraceWriter` non rientranti: un secondo `run()` mescola gli eventi del
185
+ primo (trace.py:13-23) — documentare one-shot o creare il writer per run.
186
+ - `TraceEvent` non frozen + `snapshot()` shallow: lo "snapshot immutabile" non è
187
+ strutturale.
188
+ - `digest()` basato su `repr()`: non deterministico per payload arbitrari — usare
189
+ serializzazione canonica.
190
+ - `RunId(f"{ctx.run_id}/{step.id}")` senza escaping: uno step id con `/` rende ambiguo il
191
+ trace — vietare `/` negli id del Plan.
192
+ - `dispatch()` etichetta `produced_by="__effect__<tool>"` invece dello step id.
193
+ - Ogni `_call_reasoner` crea un `ThreadPoolExecutor` (thread orfani cumulativi su timeout);
194
+ nessun timeout sui tool callable — riusare l'executor, valutare `tool_timeout_s`.
195
+
196
+ **Hardening prompt (mitigato dall'architettura, ma a buon mercato)**
197
+ - `context/assembler.py:61-66` e `interpreter.py:193`: il blob non fidato non è recintato —
198
+ delimitatori falsificabili (`# Extraction instruction:` contraffatto). Fix: tag sentinella
199
+ e istruzione ripetuta dopo il blob.
200
+ - Il sub-planner vede il catalogo tool completo, non filtrato per grant ridotto
201
+ (`effects.py:55-61`): filtrare per `required_caps ⊆ grant`.
202
+
203
+ **Test mancanti (oltre al fast-path P0)**
204
+ - `AnthropicProvider` a coverage 0% nonostante l'injection seam esista già: replicare i
205
+ fake client di `test_reasoner_robustness.py`.
206
+ - Timeout del Q-LLM mai esercitato (solo il planner): test con provider che si blocca su
207
+ `parse_blob` dopo un piano valido.
208
+ - Literal inline in `ToolCallStep.args` con query tainted → send bloccato (variante di
209
+ `test_const_label_derives_from_query`).
210
+ - Oracolo di `test_every_committed_effect_was_gated_first` confronta solo per nome tool:
211
+ contare le occorrenze.
212
+ - `RunLimits` accetta valori negativi: `Field(ge=0)` + test.
213
+
214
+ **CI / supply chain**
215
+ - Aggiungere `.github/dependabot.yml` (ecosystem `github-actions` + `uv`).
216
+ - Pinnare a SHA le action in `ci.yml` e soprattutto `release.yml` (pages.yml lo fa già).
217
+ - `permissions: contents: read` in `ci.yml`; `uv sync --locked`; matrice Python + 3.14
218
+ (e classifier); step `uv build` + `twine check` nel job quality; `branch = true` nella
219
+ coverage.
220
+
221
+ **DevEx / docs**
222
+ - pre-commit: l'hook pyright copre solo `schemas kernel memory` mentre `just typecheck`
223
+ copre tutto `src/` — usare `uv run pyright`; il pin ruff `v0.9.0` diverge dal venv —
224
+ hook local `uv run ruff`.
225
+ - justfile: aggiungere target aggregato `check: lint typecheck test`.
226
+ - CLAUDE.md e la tabella "Role → module map" del README omettono `tools/` (che è parte
227
+ dell'argomento no-bypass).
228
+ - `RK_LLM_MAX_TOKENS` e `RK_LLM_TIMEOUT_SECONDS` assenti da DEVELOPMENT.md/.env.example.
229
+ - Valutare `docs/CONFORMANCE.md`: checklist verificabile per il claim "conform your own
230
+ system to it" (oggi la spec vive tutta nel README).
231
+ - pyproject: URL `Changelog`/`Documentation`, migrazione licenza a PEP 639, classifier 3.14.
232
+
233
+ ---
234
+
235
+ ## Roadmap proposta
236
+
237
+ 1. **0.4.2 (patch, subito):** A1 + M1 con test dedicati (fix congiunto), branch coverage,
238
+ fix `RK_LLM_MAX_TOKENS`, `model` obbligatorio sui ruoli, `TransportError`, test
239
+ AnthropicProvider, default `claude-sonnet-5`.
240
+ 2. **0.5.0 (minor):** semantica globale dei RunLimits (breaking sulla semantica, non
241
+ sull'API), `ToolExecutionError` + evento di trace, fallback strict-schema robusto,
242
+ trace writer per-run, esportazione `LLMResult`/`LLMUsage`.
243
+ 3. **Infra (indipendente):** release workflow a build unica con gate, dependabot, pinning
244
+ SHA, matrice 3.14, allineamento pre-commit, `just check`.
245
+ 4. **Spec/docs:** semantica limits, regola "argomenti che escono ⇒ WRITE", `tools/` nel
246
+ role map, CONFORMANCE.md.
@@ -16,6 +16,7 @@ Python 3.12+ is required. The default suite needs no API keys: it runs against t
16
16
 
17
17
  | Command | What it does |
18
18
  |--------------------|--------------------------------------------------------------------------|
19
+ | `just check` | `lint` + `typecheck` + `test` — everything CI runs, in one command |
19
20
  | `just demo` | Worked demo (FakeProvider): legit send commits; injection inert; exfil blocked |
20
21
  | `just demo-subkernel` | §5.4 composition demo: untrusted content delegated at a reduced grant |
21
22
  | `just demo-limits` | Termination demo: `RunLimits` aborts the run closed before the second effect |
@@ -61,16 +62,16 @@ Keys are accepted under either their conventional bare name or an `RK_`-prefixed
61
62
 
62
63
  | Provider | Env var | Default model (more capable variant) |
63
64
  |-----------|------------------------------------------|---------------------------------------------|
64
- | Anthropic | `ANTHROPIC_API_KEY` / `RK_ANTHROPIC_API_KEY` | `claude-sonnet-4-6` (`claude-opus-4-8`) |
65
+ | Anthropic | `ANTHROPIC_API_KEY` / `RK_ANTHROPIC_API_KEY` | `claude-sonnet-5` (`claude-opus-4-8`) |
65
66
  | OpenAI | `OPENAI_API_KEY` / `RK_OPENAI_API_KEY` | `gpt-5.5` (`gpt-5.5-pro`) |
66
67
  | Deepseek | `DEEPSEEK_API_KEY` / `RK_DEEPSEEK_API_KEY` | `deepseek-v4-flash` (`deepseek-v4-pro`) |
67
68
 
68
69
  Other overrides (defaults in `config.py`): `RK_LLM_PROVIDER_DEFAULT`, `RK_LLM_MODEL_*`,
69
- `RK_DEEPSEEK_BASE_URL`, `RK_LLM_TIMEOUT_SECONDS`. A live test or demo **skips** any provider whose
70
+ `RK_DEEPSEEK_BASE_URL`, `RK_LLM_TIMEOUT_SECONDS`, `RK_LLM_MAX_TOKENS`. A live test or demo **skips** any provider whose
70
71
  key is absent, so partial configuration is fine.
71
72
 
72
73
  Each provider has a sensible default and a more capable variant (the parenthesised id above). Model
73
- ids are current as of May 2026; the defaults are the cost-effective tier, the variants the
74
+ ids are current as of July 2026; the defaults are the cost-effective tier, the variants the
74
75
  frontier tier. For Deepseek the legacy `deepseek-chat` / `deepseek-reasoner` names still resolve as
75
76
  deprecated aliases of `deepseek-v4-flash` but should not be used.
76
77
 
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Reasoning Kernel">
2
+ <rect width="64" height="64" fill="#0c0c0b"/>
3
+ <rect x="3.5" y="3.5" width="57" height="57" fill="none" stroke="#b45f43" stroke-width="2"/>
4
+ <text x="11" y="42" font-family="ui-monospace, SFMono-Regular, Menlo, monospace"
5
+ font-size="26" font-weight="500" fill="#b45f43">&gt;_</text>
6
+ </svg>