cortex-loop 0.1.0a1__tar.gz → 0.1.0a2__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.
- {cortex_loop-0.1.0a1/cortex_loop.egg-info → cortex_loop-0.1.0a2}/PKG-INFO +36 -13
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/README.md +35 -12
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/__init__.py +1 -1
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/adapters.py +60 -57
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/challenges.py +49 -5
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/core.py +579 -9
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/hooks/_shared.py +129 -2
- cortex_loop-0.1.0a2/cortex/hooks/instructions_loaded.py +40 -0
- cortex_loop-0.1.0a2/cortex/hooks/post_tool_use_failure.py +13 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/requirements.py +42 -1
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/stop_contract.py +32 -5
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/stop_policy.py +30 -3
- cortex_loop-0.1.0a2/cortex/stop_runtime.py +1433 -0
- cortex_loop-0.1.0a2/cortex/stop_signals.py +333 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2/cortex_loop.egg-info}/PKG-INFO +36 -13
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_loop.egg-info/SOURCES.txt +18 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_ops_cli/_adapter_validation.py +20 -1
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_ops_cli/_check_report.py +15 -8
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_ops_cli/_openai_bridge_protocol.py +179 -133
- cortex_loop-0.1.0a2/cortex_ops_cli/_runtime_profile_templates.py +447 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_ops_cli/_runtime_profiles.py +144 -38
- cortex_loop-0.1.0a2/cortex_ops_cli/gemini_hooks.py +582 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_ops_cli/main.py +10 -3
- cortex_loop-0.1.0a2/cortex_ops_cli/openai_app_server_bridge.py +963 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/pyproject.toml +1 -1
- cortex_loop-0.1.0a2/tests/test_adapter_implementation_dossier.py +69 -0
- cortex_loop-0.1.0a2/tests/test_adapter_validation_contract.py +258 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_adapters.py +79 -14
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_challenges.py +3 -0
- cortex_loop-0.1.0a2/tests/test_claude_boundedness_postmortem.py +57 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_cli.py +366 -7
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_context_budget_regression.py +34 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_core.py +950 -7
- cortex_loop-0.1.0a2/tests/test_cortex_state_analysis.py +75 -0
- cortex_loop-0.1.0a2/tests/test_docs_hygiene.py +760 -0
- cortex_loop-0.1.0a2/tests/test_dossier_snapshots.py +129 -0
- cortex_loop-0.1.0a2/tests/test_gemini_hooks_bridge.py +1518 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_hooks.py +286 -1
- cortex_loop-0.1.0a2/tests/test_model_kernel_adapter_audit.py +60 -0
- cortex_loop-0.1.0a2/tests/test_net_positive_benchmark_result_table.py +133 -0
- cortex_loop-0.1.0a2/tests/test_net_positive_benchmark_scorecard.py +137 -0
- cortex_loop-0.1.0a2/tests/test_net_positive_benchmark_task_manifest.py +116 -0
- cortex_loop-0.1.0a2/tests/test_net_positive_claude_post_split_baseline.py +126 -0
- cortex_loop-0.1.0a2/tests/test_net_positive_gemini_post_split_baseline.py +165 -0
- cortex_loop-0.1.0a2/tests/test_net_positive_openai_post_split_baseline.py +235 -0
- cortex_loop-0.1.0a2/tests/test_net_positive_phase1_baseline_blocker.py +113 -0
- cortex_loop-0.1.0a2/tests/test_net_positive_phase9_current_packet.py +251 -0
- cortex_loop-0.1.0a2/tests/test_net_positive_phase9_openai_assisted_current_pair.py +106 -0
- cortex_loop-0.1.0a2/tests/test_net_positive_phase9_rerun_readiness.py +183 -0
- cortex_loop-0.1.0a2/tests/test_openai_app_server_bridge.py +2297 -0
- cortex_loop-0.1.0a2/tests/test_release_notes.py +37 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_repo_hygiene.py +3 -0
- cortex_loop-0.1.0a2/tests/test_repo_hygiene_sessions.py +398 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_requirements.py +22 -1
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_runtime_latency_regression.py +3 -1
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_stop_contract.py +29 -4
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_stop_policy.py +37 -2
- cortex_loop-0.1.0a2/tests/test_stop_runtime.py +1476 -0
- cortex_loop-0.1.0a2/tests/test_stop_signals.py +412 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_workflow_contract.py +53 -1
- cortex_loop-0.1.0a1/cortex/stop_runtime.py +0 -400
- cortex_loop-0.1.0a1/cortex/stop_signals.py +0 -75
- cortex_loop-0.1.0a1/cortex_ops_cli/_runtime_profile_templates.py +0 -341
- cortex_loop-0.1.0a1/cortex_ops_cli/gemini_hooks.py +0 -301
- cortex_loop-0.1.0a1/cortex_ops_cli/openai_app_server_bridge.py +0 -375
- cortex_loop-0.1.0a1/tests/test_adapter_validation_contract.py +0 -127
- cortex_loop-0.1.0a1/tests/test_docs_hygiene.py +0 -443
- cortex_loop-0.1.0a1/tests/test_gemini_hooks_bridge.py +0 -570
- cortex_loop-0.1.0a1/tests/test_openai_app_server_bridge.py +0 -850
- cortex_loop-0.1.0a1/tests/test_repo_hygiene_sessions.py +0 -174
- cortex_loop-0.1.0a1/tests/test_stop_runtime.py +0 -145
- cortex_loop-0.1.0a1/tests/test_stop_signals.py +0 -44
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/LICENSE +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/MANIFEST.in +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/blocklist.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/cli.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/core_helpers.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/data/identity_preamble.md +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/data/layer1_part_a.md +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/data/layer1_part_b.md +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/executive.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/foundation.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/genome.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/graveyard.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/hooks/__init__.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/hooks/post_tool_use.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/hooks/pre_tool_use.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/hooks/session_start.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/hooks/stop.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/invariants.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/packs.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/repomap.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/retry.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/stop_payload.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/store.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/templates/__init__.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex/utils.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_loop.egg-info/dependency_links.txt +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_loop.egg-info/entry_points.txt +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_loop.egg-info/requires.txt +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_loop.egg-info/top_level.txt +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_ops_cli/__init__.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_ops_cli/_check_report_output.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_ops_cli/_openai_bridge_probe.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_repomap/__init__.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/cortex_repomap/engine.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/setup.cfg +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_adapter_validation_fixture_hygiene.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_adapter_validation_fixture_provenance.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_blocklist.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_core_helpers.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_executive.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_executive_layer2.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_foundation.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_gemini_fixture_hygiene.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_gemini_fixture_provenance.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_genome.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_graveyard.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_install_surfaces.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_integration.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_invariants.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_kernel_density_targets.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_kernel_runtime_agnostic_guard.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_mini_openai_ab.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_packs.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_preflight_openai_probe_v121.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_repomap.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_retry.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_shims.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_stop_payload.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_store.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_structure_boundaries.py +0 -0
- {cortex_loop-0.1.0a1 → cortex_loop-0.1.0a2}/tests/test_study_scripts.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cortex-loop
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.0a2
|
|
4
4
|
Summary: Runtime-agnostic enforcement layer for AI coding agents
|
|
5
5
|
Author: Cortex contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -46,7 +46,8 @@ The stop path is the product. Cortex is not a planning framework, generic memory
|
|
|
46
46
|
| --- | --- | --- | --- |
|
|
47
47
|
| Claude Code | `cortex runtime install --profile claude` | Shipped | Strongest current runtime. The truthful boundary is live-proven; remaining caveats are minor. |
|
|
48
48
|
| Gemini CLI | `cortex runtime install --profile gemini` | Shipped with watchlist | The truthful boundary is live-proven. One operational watchlist remains: blocked malformed stops can leave Gemini CLI resident until operator termination. |
|
|
49
|
-
| OpenAI Codex App Server | `cortex runtime install --profile openai` | Experimental | Several critical paths are proven
|
|
49
|
+
| OpenAI Codex App Server (native) | `cortex runtime install --profile openai` | Experimental | Honest native App Server surface: one turn, then stop audit. Several critical paths are proven, but positive strict close is still not dependable enough for broader support. |
|
|
50
|
+
| OpenAI Codex App Server (assisted) | `cortex runtime install --profile openai-assisted` | Experimental | Explicit bounded assisted mode with a current committed shared-harness pair: Cortex provides session-start context with a short kernel-owned completion preview, one short evidence-expectation line, and one kernel-derived corrective turn. The lane remains supplemental-only, not native-substitutive proof. |
|
|
50
51
|
|
|
51
52
|
Status labels in this table:
|
|
52
53
|
- `Shipped`: live-proven on the shared harness; remaining caveats are non-critical.
|
|
@@ -55,23 +56,36 @@ Status labels in this table:
|
|
|
55
56
|
|
|
56
57
|
Detailed runtime release evidence lives in [docs/ADAPTER_VALIDATION.md](docs/ADAPTER_VALIDATION.md) plus committed provenance under [tests/fixtures/adapter_validation/claude/PROVENANCE.json](tests/fixtures/adapter_validation/claude/PROVENANCE.json), [tests/fixtures/adapter_validation/gemini/PROVENANCE.json](tests/fixtures/adapter_validation/gemini/PROVENANCE.json), and [tests/fixtures/adapter_validation/openai/PROVENANCE.json](tests/fixtures/adapter_validation/openai/PROVENANCE.json).
|
|
57
58
|
|
|
59
|
+
Current product-proof truth: Cortex is strong on truthful completion boundaries,
|
|
60
|
+
but the repo still does not earn a net-positive product claim over the raw
|
|
61
|
+
model. Phase 9 is landed at a truthful-withheld endpoint rather than an earned
|
|
62
|
+
product-proof win.
|
|
63
|
+
|
|
64
|
+
This repo state is also the final archival v1 reference point for the
|
|
65
|
+
enforcement-layer line. The archival package target is `cortex-loop 0.1.0a2`.
|
|
66
|
+
|
|
58
67
|
## Quickstart
|
|
59
68
|
|
|
60
|
-
|
|
61
|
-
package publication is proven, the truthful first-run path is from a repo
|
|
62
|
-
checkout:
|
|
69
|
+
Default public install:
|
|
63
70
|
|
|
64
71
|
```bash
|
|
65
|
-
|
|
66
|
-
. .venv/bin/activate
|
|
67
|
-
python -m pip install . pytest
|
|
72
|
+
pipx install cortex-loop
|
|
68
73
|
cortex init
|
|
69
74
|
cortex runtime install --profile claude
|
|
70
75
|
```
|
|
71
76
|
|
|
72
|
-
|
|
77
|
+
Supported advanced alternative:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
uv tool install cortex-loop
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Before `cortex check`, choose the baseline that matches the repository or the
|
|
84
|
+
machine you are using:
|
|
73
85
|
|
|
74
|
-
- trusted local repo:
|
|
86
|
+
- trusted local repo without Docker: add `pytest` to the promoted tool surface
|
|
87
|
+
(`pipx inject cortex-loop pytest` or `uv tool install --with pytest
|
|
88
|
+
cortex-loop`) and switch `cortex.toml` to the trusted-host baseline in
|
|
75
89
|
[docs/SECURE_DEFAULTS.md](docs/SECURE_DEFAULTS.md)
|
|
76
90
|
- untrusted or container baseline: keep the defaults and ensure Docker is on
|
|
77
91
|
`PATH`
|
|
@@ -82,12 +96,21 @@ Then run:
|
|
|
82
96
|
cortex check
|
|
83
97
|
```
|
|
84
98
|
|
|
85
|
-
|
|
86
|
-
|
|
99
|
+
Install, update, uninstall, and live proof details live in
|
|
100
|
+
[docs/INSTALL.md](docs/INSTALL.md).
|
|
101
|
+
Guided evaluator onboarding lives in
|
|
102
|
+
[docs/ALPHA_TESTER_ONBOARDING.md](docs/ALPHA_TESTER_ONBOARDING.md).
|
|
103
|
+
Trust boundary and pack review posture live in
|
|
104
|
+
[docs/TRUST_BOUNDARY.md](docs/TRUST_BOUNDARY.md) and
|
|
105
|
+
[docs/PACK_POLICY.md](docs/PACK_POLICY.md).
|
|
87
106
|
|
|
88
107
|
For a first evaluation, keep `claude`. Use `gemini` when you want the shipped
|
|
89
108
|
watchlist surface. Use `openai` only when you are explicitly evaluating the
|
|
90
|
-
experimental surface.
|
|
109
|
+
experimental native surface. Use `openai-assisted` when you want the current
|
|
110
|
+
bounded assisted OpenAI lane: session-start context with a short kernel-owned
|
|
111
|
+
completion preview, one short evidence-expectation line, one kernel-derived
|
|
112
|
+
corrective turn aimed at the unresolved completion gap, and a current
|
|
113
|
+
shared-harness pair that still remains supplemental-only.
|
|
91
114
|
|
|
92
115
|
## What Cortex Enforces
|
|
93
116
|
|
|
@@ -14,7 +14,8 @@ The stop path is the product. Cortex is not a planning framework, generic memory
|
|
|
14
14
|
| --- | --- | --- | --- |
|
|
15
15
|
| Claude Code | `cortex runtime install --profile claude` | Shipped | Strongest current runtime. The truthful boundary is live-proven; remaining caveats are minor. |
|
|
16
16
|
| Gemini CLI | `cortex runtime install --profile gemini` | Shipped with watchlist | The truthful boundary is live-proven. One operational watchlist remains: blocked malformed stops can leave Gemini CLI resident until operator termination. |
|
|
17
|
-
| OpenAI Codex App Server | `cortex runtime install --profile openai` | Experimental | Several critical paths are proven
|
|
17
|
+
| OpenAI Codex App Server (native) | `cortex runtime install --profile openai` | Experimental | Honest native App Server surface: one turn, then stop audit. Several critical paths are proven, but positive strict close is still not dependable enough for broader support. |
|
|
18
|
+
| OpenAI Codex App Server (assisted) | `cortex runtime install --profile openai-assisted` | Experimental | Explicit bounded assisted mode with a current committed shared-harness pair: Cortex provides session-start context with a short kernel-owned completion preview, one short evidence-expectation line, and one kernel-derived corrective turn. The lane remains supplemental-only, not native-substitutive proof. |
|
|
18
19
|
|
|
19
20
|
Status labels in this table:
|
|
20
21
|
- `Shipped`: live-proven on the shared harness; remaining caveats are non-critical.
|
|
@@ -23,23 +24,36 @@ Status labels in this table:
|
|
|
23
24
|
|
|
24
25
|
Detailed runtime release evidence lives in [docs/ADAPTER_VALIDATION.md](docs/ADAPTER_VALIDATION.md) plus committed provenance under [tests/fixtures/adapter_validation/claude/PROVENANCE.json](tests/fixtures/adapter_validation/claude/PROVENANCE.json), [tests/fixtures/adapter_validation/gemini/PROVENANCE.json](tests/fixtures/adapter_validation/gemini/PROVENANCE.json), and [tests/fixtures/adapter_validation/openai/PROVENANCE.json](tests/fixtures/adapter_validation/openai/PROVENANCE.json).
|
|
25
26
|
|
|
27
|
+
Current product-proof truth: Cortex is strong on truthful completion boundaries,
|
|
28
|
+
but the repo still does not earn a net-positive product claim over the raw
|
|
29
|
+
model. Phase 9 is landed at a truthful-withheld endpoint rather than an earned
|
|
30
|
+
product-proof win.
|
|
31
|
+
|
|
32
|
+
This repo state is also the final archival v1 reference point for the
|
|
33
|
+
enforcement-layer line. The archival package target is `cortex-loop 0.1.0a2`.
|
|
34
|
+
|
|
26
35
|
## Quickstart
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
package publication is proven, the truthful first-run path is from a repo
|
|
30
|
-
checkout:
|
|
37
|
+
Default public install:
|
|
31
38
|
|
|
32
39
|
```bash
|
|
33
|
-
|
|
34
|
-
. .venv/bin/activate
|
|
35
|
-
python -m pip install . pytest
|
|
40
|
+
pipx install cortex-loop
|
|
36
41
|
cortex init
|
|
37
42
|
cortex runtime install --profile claude
|
|
38
43
|
```
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
Supported advanced alternative:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
uv tool install cortex-loop
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Before `cortex check`, choose the baseline that matches the repository or the
|
|
52
|
+
machine you are using:
|
|
41
53
|
|
|
42
|
-
- trusted local repo:
|
|
54
|
+
- trusted local repo without Docker: add `pytest` to the promoted tool surface
|
|
55
|
+
(`pipx inject cortex-loop pytest` or `uv tool install --with pytest
|
|
56
|
+
cortex-loop`) and switch `cortex.toml` to the trusted-host baseline in
|
|
43
57
|
[docs/SECURE_DEFAULTS.md](docs/SECURE_DEFAULTS.md)
|
|
44
58
|
- untrusted or container baseline: keep the defaults and ensure Docker is on
|
|
45
59
|
`PATH`
|
|
@@ -50,12 +64,21 @@ Then run:
|
|
|
50
64
|
cortex check
|
|
51
65
|
```
|
|
52
66
|
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
Install, update, uninstall, and live proof details live in
|
|
68
|
+
[docs/INSTALL.md](docs/INSTALL.md).
|
|
69
|
+
Guided evaluator onboarding lives in
|
|
70
|
+
[docs/ALPHA_TESTER_ONBOARDING.md](docs/ALPHA_TESTER_ONBOARDING.md).
|
|
71
|
+
Trust boundary and pack review posture live in
|
|
72
|
+
[docs/TRUST_BOUNDARY.md](docs/TRUST_BOUNDARY.md) and
|
|
73
|
+
[docs/PACK_POLICY.md](docs/PACK_POLICY.md).
|
|
55
74
|
|
|
56
75
|
For a first evaluation, keep `claude`. Use `gemini` when you want the shipped
|
|
57
76
|
watchlist surface. Use `openai` only when you are explicitly evaluating the
|
|
58
|
-
experimental surface.
|
|
77
|
+
experimental native surface. Use `openai-assisted` when you want the current
|
|
78
|
+
bounded assisted OpenAI lane: session-start context with a short kernel-owned
|
|
79
|
+
completion preview, one short evidence-expectation line, one kernel-derived
|
|
80
|
+
corrective turn aimed at the unresolved completion gap, and a current
|
|
81
|
+
shared-harness pair that still remains supplemental-only.
|
|
59
82
|
|
|
60
83
|
## What Cortex Enforces
|
|
61
84
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import importlib
|
|
4
|
-
import json
|
|
5
4
|
import logging
|
|
6
5
|
import re
|
|
7
6
|
from collections.abc import Mapping
|
|
@@ -32,6 +31,8 @@ CANONICAL_EVENT_ALIASES = {
|
|
|
32
31
|
"pretooluse": "pre_tool_use",
|
|
33
32
|
"post_tool_use": "post_tool_use",
|
|
34
33
|
"posttooluse": "post_tool_use",
|
|
34
|
+
"post_tool_use_failure": "post_tool_use",
|
|
35
|
+
"posttoolusefailure": "post_tool_use",
|
|
35
36
|
"stop": "stop",
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -51,7 +52,7 @@ class ClaudeAdapter:
|
|
|
51
52
|
def normalize(self, event_name: str, payload: Mapping[str, Any] | None = None) -> NormalizedEvent:
|
|
52
53
|
name = _normalize_event_name(event_name, self.EVENT_ALIASES)
|
|
53
54
|
data = dict(payload) if isinstance(payload, Mapping) else {}
|
|
54
|
-
data = _normalize_claude_payload(data)
|
|
55
|
+
data = _normalize_claude_payload(data, normalized_event_name=name)
|
|
55
56
|
message = data.get("last_assistant_message")
|
|
56
57
|
if isinstance(message, str):
|
|
57
58
|
rewritten = _rewrite_legacy_trailer_markers(message)
|
|
@@ -94,6 +95,7 @@ class GeminiAdapter:
|
|
|
94
95
|
_normalize_session_id(data)
|
|
95
96
|
if name in {"pre_tool_use", "post_tool_use"}:
|
|
96
97
|
_normalize_tool_name(data, candidate_keys=("tool_name",))
|
|
98
|
+
_normalize_gemini_file_targets(data)
|
|
97
99
|
if name == "post_tool_use":
|
|
98
100
|
_normalize_gemini_status(data)
|
|
99
101
|
if name == "stop":
|
|
@@ -107,8 +109,13 @@ class GeminiAdapter:
|
|
|
107
109
|
type(prompt_response).__name__,
|
|
108
110
|
)
|
|
109
111
|
prompt_response = ""
|
|
110
|
-
stop_fields, passthrough = _normalize_gemini_stop_fields(prompt_response)
|
|
111
|
-
|
|
112
|
+
stop_fields, passthrough, parse_error = _normalize_gemini_stop_fields(prompt_response)
|
|
113
|
+
if isinstance(stop_fields, dict):
|
|
114
|
+
data["stop_fields"] = stop_fields
|
|
115
|
+
else:
|
|
116
|
+
data.pop("stop_fields", None)
|
|
117
|
+
if parse_error:
|
|
118
|
+
data["stop_fields_parse_error"] = parse_error
|
|
112
119
|
data["last_assistant_message"] = passthrough
|
|
113
120
|
return NormalizedEvent(name=name, payload=data)
|
|
114
121
|
|
|
@@ -193,14 +200,46 @@ def _normalize_event_name(event_name: str, aliases: dict[str, str]) -> str:
|
|
|
193
200
|
return aliases.get(token) or aliases.get(token.replace("_", "")) or token
|
|
194
201
|
|
|
195
202
|
|
|
196
|
-
def _normalize_claude_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
203
|
+
def _normalize_claude_payload(payload: dict[str, Any], *, normalized_event_name: str) -> dict[str, Any]:
|
|
197
204
|
_normalize_tool_name(payload, candidate_keys=("tool_name", "tool", "toolName", "action"))
|
|
198
205
|
_normalize_session_id(payload)
|
|
206
|
+
_normalize_claude_file_targets(payload)
|
|
207
|
+
if normalized_event_name == "session_start":
|
|
208
|
+
payload.setdefault("runtime_mode", "native_preview")
|
|
209
|
+
payload.setdefault("stop_trailer_marker", "STOP_FIELDS_JSON")
|
|
210
|
+
if normalized_event_name == "post_tool_use":
|
|
211
|
+
_normalize_claude_post_tool_status(payload)
|
|
199
212
|
if "stop_fields" not in payload and "cortex_stop" in payload:
|
|
200
213
|
payload["stop_fields"] = payload.get("cortex_stop")
|
|
201
214
|
return payload
|
|
202
215
|
|
|
203
216
|
|
|
217
|
+
def _normalize_claude_post_tool_status(payload: dict[str, Any]) -> None:
|
|
218
|
+
raw_hook = str(payload.get("hook_event_name") or "").strip()
|
|
219
|
+
if raw_hook == "PostToolUseFailure":
|
|
220
|
+
payload.setdefault("status", "error")
|
|
221
|
+
tool_response = payload.get("tool_response")
|
|
222
|
+
if isinstance(tool_response, Mapping):
|
|
223
|
+
response_error = tool_response.get("error")
|
|
224
|
+
if payload.get("status") is None and response_error:
|
|
225
|
+
payload["status"] = "error"
|
|
226
|
+
if payload.get("error") is None and isinstance(response_error, str) and response_error.strip():
|
|
227
|
+
payload["error"] = response_error.strip()
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _normalize_claude_file_targets(payload: dict[str, Any]) -> None:
|
|
231
|
+
tool_input = payload.get("tool_input")
|
|
232
|
+
if not isinstance(tool_input, Mapping):
|
|
233
|
+
return
|
|
234
|
+
file_path = str(tool_input.get("file_path") or tool_input.get("path") or "").strip()
|
|
235
|
+
if not file_path:
|
|
236
|
+
return
|
|
237
|
+
payload.setdefault("target_files", [file_path])
|
|
238
|
+
tool_name = str(payload.get("tool_name") or "").strip().lower()
|
|
239
|
+
if tool_name in {"edit", "multiedit", "write", "notebookedit"}:
|
|
240
|
+
payload.setdefault("planned_files", [file_path])
|
|
241
|
+
|
|
242
|
+
|
|
204
243
|
def _normalize_claude_stop_fields(message: str) -> tuple[dict[str, Any] | None, str]:
|
|
205
244
|
parsed, _, _ = parse_stop_fields_json(message)
|
|
206
245
|
passthrough = _strip_gemini_stop_markers(message)
|
|
@@ -255,7 +294,20 @@ def _normalize_gemini_status(payload: dict[str, Any]) -> None:
|
|
|
255
294
|
payload["status"] = "ok"
|
|
256
295
|
|
|
257
296
|
|
|
258
|
-
def
|
|
297
|
+
def _normalize_gemini_file_targets(payload: dict[str, Any]) -> None:
|
|
298
|
+
tool_input = payload.get("tool_input")
|
|
299
|
+
if not isinstance(tool_input, Mapping):
|
|
300
|
+
return
|
|
301
|
+
file_path = str(tool_input.get("file_path") or tool_input.get("path") or "").strip()
|
|
302
|
+
if not file_path:
|
|
303
|
+
return
|
|
304
|
+
payload.setdefault("target_files", [file_path])
|
|
305
|
+
tool_name = str(payload.get("tool_name") or "").strip().lower()
|
|
306
|
+
if tool_name in {"replace", "write_file", "edit", "write"}:
|
|
307
|
+
payload.setdefault("planned_files", [file_path])
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def _normalize_gemini_stop_fields(prompt_response: str) -> tuple[dict[str, Any] | None, str, str]:
|
|
259
311
|
parsed, marker_found, error = parse_stop_fields_json(prompt_response)
|
|
260
312
|
passthrough = _strip_gemini_stop_markers(prompt_response)
|
|
261
313
|
|
|
@@ -263,16 +315,9 @@ def _normalize_gemini_stop_fields(prompt_response: str) -> tuple[dict[str, Any],
|
|
|
263
315
|
stop_fields = {str(k): v for k, v in parsed.items()}
|
|
264
316
|
if passthrough and not stop_fields.get("summary"):
|
|
265
317
|
stop_fields["summary"] = passthrough
|
|
266
|
-
return stop_fields, passthrough
|
|
318
|
+
return stop_fields, passthrough, ""
|
|
267
319
|
|
|
268
|
-
|
|
269
|
-
if marker_found and error:
|
|
270
|
-
stop_fields["marker_parse_error"] = error
|
|
271
|
-
if passthrough:
|
|
272
|
-
stop_fields.setdefault("summary", passthrough)
|
|
273
|
-
elif prompt_response.strip():
|
|
274
|
-
stop_fields.setdefault("summary", prompt_response.strip())
|
|
275
|
-
return stop_fields, passthrough
|
|
320
|
+
return None, passthrough or prompt_response.strip(), str(error or "") if marker_found else ""
|
|
276
321
|
|
|
277
322
|
|
|
278
323
|
def _strip_gemini_stop_markers(text: str) -> str:
|
|
@@ -289,48 +334,6 @@ def _strip_gemini_stop_markers(text: str) -> str:
|
|
|
289
334
|
cleaned = cleaned[:marker_idx]
|
|
290
335
|
return cleaned.strip()
|
|
291
336
|
|
|
292
|
-
|
|
293
|
-
def _recover_partial_stop_fields(text: str) -> dict[str, Any]:
|
|
294
|
-
recovered: dict[str, Any] = {}
|
|
295
|
-
|
|
296
|
-
coverage: dict[str, bool] = {}
|
|
297
|
-
for key in ("null_inputs", "boundary_values", "error_handling", "graveyard_regression"):
|
|
298
|
-
match = re.search(rf'"{key}"\s*:\s*(true|false)', text, flags=re.IGNORECASE)
|
|
299
|
-
if match:
|
|
300
|
-
coverage[key] = match.group(1).lower() == "true"
|
|
301
|
-
if coverage:
|
|
302
|
-
recovered["challenge_coverage"] = coverage
|
|
303
|
-
|
|
304
|
-
truth_claims: dict[str, list[str]] = {}
|
|
305
|
-
for key in ("modified_files", "tests_ran"):
|
|
306
|
-
values = _recover_string_list(text, key)
|
|
307
|
-
if values:
|
|
308
|
-
truth_claims[key] = values
|
|
309
|
-
if truth_claims:
|
|
310
|
-
recovered["truth_claims"] = truth_claims
|
|
311
|
-
|
|
312
|
-
return recovered
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
def _recover_string_list(text: str, key: str) -> list[str]:
|
|
316
|
-
match = re.search(rf'"{key}"\s*:\s*\[(.*?)\]', text, flags=re.DOTALL | re.IGNORECASE)
|
|
317
|
-
if not match:
|
|
318
|
-
return []
|
|
319
|
-
values: list[str] = []
|
|
320
|
-
seen: set[str] = set()
|
|
321
|
-
for token in re.findall(r'"((?:\\.|[^"\\])*)"', match.group(1)):
|
|
322
|
-
try:
|
|
323
|
-
value = str(json.loads(f'"{token}"'))
|
|
324
|
-
except json.JSONDecodeError:
|
|
325
|
-
value = token
|
|
326
|
-
cleaned = value.strip()
|
|
327
|
-
if not cleaned or cleaned in seen:
|
|
328
|
-
continue
|
|
329
|
-
seen.add(cleaned)
|
|
330
|
-
values.append(cleaned)
|
|
331
|
-
return values
|
|
332
|
-
|
|
333
|
-
|
|
334
337
|
def _rewrite_legacy_trailer_markers(message: str) -> str:
|
|
335
338
|
return (
|
|
336
339
|
message.replace("CORTEX_STOP_JSON:", "STOP_FIELDS_JSON:")
|
|
@@ -33,6 +33,7 @@ class ChallengeReport:
|
|
|
33
33
|
diagnostics: list[dict[str, Any]]
|
|
34
34
|
config_warnings: list[str]
|
|
35
35
|
ok: bool
|
|
36
|
+
gap_entries: list[str] = field(default_factory=list)
|
|
36
37
|
|
|
37
38
|
def to_dict(self) -> dict[str, Any]:
|
|
38
39
|
return {
|
|
@@ -45,6 +46,7 @@ class ChallengeReport:
|
|
|
45
46
|
"diagnostics": self.diagnostics,
|
|
46
47
|
"config_warnings": self.config_warnings,
|
|
47
48
|
"ok": self.ok,
|
|
49
|
+
"gap_entries": self.gap_entries,
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
|
|
@@ -67,6 +69,7 @@ class ChallengeEnforcer:
|
|
|
67
69
|
missing: list[str] = []
|
|
68
70
|
unverified: list[str] = []
|
|
69
71
|
uncheckable: list[str] = []
|
|
72
|
+
gap_entries: list[str] = []
|
|
70
73
|
diagnostics: list[dict[str, Any]] = []
|
|
71
74
|
config_warnings: list[str] = []
|
|
72
75
|
resolved_root = root.resolve() if root is not None else None
|
|
@@ -82,6 +85,7 @@ class ChallengeEnforcer:
|
|
|
82
85
|
for category in self.config.active_categories:
|
|
83
86
|
raw = coverage_payload.get(category)
|
|
84
87
|
covered, evidence = self._coerce_coverage(raw)
|
|
88
|
+
gap_kind = "missing" if raw is None else "uncovered"
|
|
85
89
|
if require_verifiable_coverage and covered:
|
|
86
90
|
verification = self._verify_covered_category(
|
|
87
91
|
evidence=evidence,
|
|
@@ -95,13 +99,16 @@ class ChallengeEnforcer:
|
|
|
95
99
|
reason = str(verification.get("reason") or "missing verifiable evidence")
|
|
96
100
|
if verification_status == "uncheckable":
|
|
97
101
|
uncheckable.append(category)
|
|
102
|
+
gap_kind = "uncheckable"
|
|
98
103
|
else:
|
|
99
104
|
unverified.append(category)
|
|
105
|
+
gap_kind = "unverified"
|
|
100
106
|
config_warnings.append(
|
|
101
107
|
f"Challenge coverage '{category}' marked covered but evidence is {verification_status}: {reason}"
|
|
102
108
|
)
|
|
103
109
|
if not covered:
|
|
104
110
|
missing.append(category)
|
|
111
|
+
gap_entries.append(f"{category}:{gap_kind}")
|
|
105
112
|
diagnostics.append(
|
|
106
113
|
self._coverage_diagnostic(
|
|
107
114
|
category=category,
|
|
@@ -125,16 +132,31 @@ class ChallengeEnforcer:
|
|
|
125
132
|
diagnostics=diagnostics,
|
|
126
133
|
config_warnings=config_warnings,
|
|
127
134
|
ok=ok,
|
|
135
|
+
gap_entries=sorted(set(gap_entries)),
|
|
128
136
|
)
|
|
129
137
|
|
|
130
138
|
def missing_coverage_diagnostics(self) -> list[dict[str, Any]]:
|
|
131
139
|
return [
|
|
132
|
-
{
|
|
140
|
+
{
|
|
141
|
+
"evidence_found": [],
|
|
142
|
+
"evidence_expected": [f"challenge_coverage for: {', '.join(self.config.active_categories)}"],
|
|
143
|
+
"gap_description": "No challenge_coverage was provided for the stop attempt.",
|
|
144
|
+
"gap_characterization": "comprehension_gap",
|
|
145
|
+
"distance_signal": "far",
|
|
146
|
+
"gap_entries": ["__all__:missing"],
|
|
147
|
+
}
|
|
133
148
|
]
|
|
134
149
|
|
|
135
150
|
def invalid_coverage_diagnostics(self, raw: Any) -> list[dict[str, Any]]:
|
|
136
151
|
return [
|
|
137
|
-
{
|
|
152
|
+
{
|
|
153
|
+
"evidence_found": [f"challenge_coverage={type(raw).__name__}"],
|
|
154
|
+
"evidence_expected": ["challenge_coverage object keyed by active challenge categories"],
|
|
155
|
+
"gap_description": "Challenge coverage used an invalid payload shape.",
|
|
156
|
+
"gap_characterization": "comprehension_gap",
|
|
157
|
+
"distance_signal": "far",
|
|
158
|
+
"gap_entries": ["__all__:invalid_shape"],
|
|
159
|
+
}
|
|
138
160
|
]
|
|
139
161
|
|
|
140
162
|
@staticmethod
|
|
@@ -200,11 +222,33 @@ class ChallengeEnforcer:
|
|
|
200
222
|
) -> dict[str, Any]:
|
|
201
223
|
verification = evidence.get("verification") if isinstance(evidence, Mapping) else None
|
|
202
224
|
if raw is None:
|
|
203
|
-
return {
|
|
225
|
+
return {
|
|
226
|
+
"evidence_found": [],
|
|
227
|
+
"evidence_expected": [f"challenge_coverage.{category}=true"],
|
|
228
|
+
"gap_description": f"Challenge category '{category}' was not addressed in stop coverage.",
|
|
229
|
+
"gap_characterization": "comprehension_gap",
|
|
230
|
+
"distance_signal": "far",
|
|
231
|
+
"gap_entries": [f"{category}:missing"],
|
|
232
|
+
}
|
|
204
233
|
|
|
205
234
|
evidence_refs = _as_string_list(evidence.get("evidence")) if isinstance(evidence, Mapping) else []
|
|
206
235
|
if require_verifiable_coverage and isinstance(verification, Mapping):
|
|
207
236
|
status = str(verification.get("status") or "unverified")
|
|
208
|
-
|
|
237
|
+
gap_kind = "uncheckable" if status == "uncheckable" else "unverified"
|
|
238
|
+
return {
|
|
239
|
+
"evidence_found": evidence_refs or [f"verification_status={status}"],
|
|
240
|
+
"evidence_expected": [f"verifiable evidence for challenge '{category}'"],
|
|
241
|
+
"gap_description": f"Challenge category '{category}' was claimed but not verifiably supported.",
|
|
242
|
+
"gap_characterization": "execution_gap",
|
|
243
|
+
"distance_signal": "moderate" if evidence_refs else "far",
|
|
244
|
+
"gap_entries": [f"{category}:{gap_kind}"],
|
|
245
|
+
}
|
|
209
246
|
|
|
210
|
-
return {
|
|
247
|
+
return {
|
|
248
|
+
"evidence_found": ["covered=false"],
|
|
249
|
+
"evidence_expected": [f"challenge_coverage.{category}=true"],
|
|
250
|
+
"gap_description": f"Challenge category '{category}' remains uncovered.",
|
|
251
|
+
"gap_characterization": "comprehension_gap",
|
|
252
|
+
"distance_signal": "moderate",
|
|
253
|
+
"gap_entries": [f"{category}:uncovered"],
|
|
254
|
+
}
|