cortex-loop 0.1.0a1__py3-none-any.whl
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/__init__.py +7 -0
- cortex/adapters.py +339 -0
- cortex/blocklist.py +51 -0
- cortex/challenges.py +210 -0
- cortex/cli.py +7 -0
- cortex/core.py +601 -0
- cortex/core_helpers.py +190 -0
- cortex/data/identity_preamble.md +5 -0
- cortex/data/layer1_part_a.md +65 -0
- cortex/data/layer1_part_b.md +17 -0
- cortex/executive.py +295 -0
- cortex/foundation.py +185 -0
- cortex/genome.py +348 -0
- cortex/graveyard.py +226 -0
- cortex/hooks/__init__.py +27 -0
- cortex/hooks/_shared.py +167 -0
- cortex/hooks/post_tool_use.py +13 -0
- cortex/hooks/pre_tool_use.py +13 -0
- cortex/hooks/session_start.py +13 -0
- cortex/hooks/stop.py +13 -0
- cortex/invariants.py +258 -0
- cortex/packs.py +118 -0
- cortex/repomap.py +6 -0
- cortex/requirements.py +497 -0
- cortex/retry.py +312 -0
- cortex/stop_contract.py +217 -0
- cortex/stop_payload.py +122 -0
- cortex/stop_policy.py +100 -0
- cortex/stop_runtime.py +400 -0
- cortex/stop_signals.py +75 -0
- cortex/store.py +793 -0
- cortex/templates/__init__.py +10 -0
- cortex/utils.py +58 -0
- cortex_loop-0.1.0a1.dist-info/METADATA +121 -0
- cortex_loop-0.1.0a1.dist-info/RECORD +52 -0
- cortex_loop-0.1.0a1.dist-info/WHEEL +5 -0
- cortex_loop-0.1.0a1.dist-info/entry_points.txt +3 -0
- cortex_loop-0.1.0a1.dist-info/licenses/LICENSE +21 -0
- cortex_loop-0.1.0a1.dist-info/top_level.txt +3 -0
- cortex_ops_cli/__init__.py +3 -0
- cortex_ops_cli/_adapter_validation.py +119 -0
- cortex_ops_cli/_check_report.py +454 -0
- cortex_ops_cli/_check_report_output.py +270 -0
- cortex_ops_cli/_openai_bridge_probe.py +241 -0
- cortex_ops_cli/_openai_bridge_protocol.py +469 -0
- cortex_ops_cli/_runtime_profile_templates.py +341 -0
- cortex_ops_cli/_runtime_profiles.py +445 -0
- cortex_ops_cli/gemini_hooks.py +301 -0
- cortex_ops_cli/main.py +911 -0
- cortex_ops_cli/openai_app_server_bridge.py +375 -0
- cortex_repomap/__init__.py +1 -0
- cortex_repomap/engine.py +1201 -0
cortex/stop_policy.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(slots=True)
|
|
7
|
+
class StopVerdict:
|
|
8
|
+
session_status: str
|
|
9
|
+
recommend_revert: bool
|
|
10
|
+
proceed: bool
|
|
11
|
+
feedback_mode: str
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def compute_stop_outcome(
|
|
15
|
+
*,
|
|
16
|
+
mode: str,
|
|
17
|
+
fail_on_missing_challenge_coverage: bool,
|
|
18
|
+
fail_on_requirement_audit_gap: bool,
|
|
19
|
+
require_requirement_audit: bool,
|
|
20
|
+
challenge_ok: bool | None,
|
|
21
|
+
invariant_ok: bool | None,
|
|
22
|
+
invariant_recommend_revert: bool,
|
|
23
|
+
missing_challenge_coverage: bool,
|
|
24
|
+
requirements_gate_gap: bool,
|
|
25
|
+
requirement_audit_missing: bool,
|
|
26
|
+
structured_stop_violation: bool,
|
|
27
|
+
loop_detected: bool = False,
|
|
28
|
+
stuck_declared: bool = False,
|
|
29
|
+
) -> StopVerdict:
|
|
30
|
+
strict_mode = mode == "strict"
|
|
31
|
+
strict_challenge_gate = strict_mode and fail_on_missing_challenge_coverage
|
|
32
|
+
challenge_gate_violation = _challenge_gate_violation(
|
|
33
|
+
strict_challenge_gate=strict_challenge_gate,
|
|
34
|
+
missing_challenge_coverage=missing_challenge_coverage,
|
|
35
|
+
challenge_ok=challenge_ok,
|
|
36
|
+
)
|
|
37
|
+
requirement_audit_violation = _requirement_audit_violation(
|
|
38
|
+
strict_mode=strict_mode,
|
|
39
|
+
fail_on_requirement_audit_gap=fail_on_requirement_audit_gap,
|
|
40
|
+
requirements_gate_gap=requirements_gate_gap,
|
|
41
|
+
requirement_audit_missing=requirement_audit_missing,
|
|
42
|
+
require_requirement_audit=require_requirement_audit,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
base_status = "completed"
|
|
46
|
+
if invariant_ok is False:
|
|
47
|
+
base_status = "failed_invariants"
|
|
48
|
+
elif structured_stop_violation:
|
|
49
|
+
base_status = "failed_stop_contract"
|
|
50
|
+
elif requirement_audit_violation:
|
|
51
|
+
base_status = "failed_requirements"
|
|
52
|
+
elif challenge_ok is False:
|
|
53
|
+
base_status = "failed_challenges"
|
|
54
|
+
elif missing_challenge_coverage and strict_challenge_gate:
|
|
55
|
+
base_status = "missing_challenge_coverage"
|
|
56
|
+
|
|
57
|
+
if base_status != "completed" and stuck_declared:
|
|
58
|
+
return StopVerdict(
|
|
59
|
+
session_status="stuck",
|
|
60
|
+
recommend_revert=False,
|
|
61
|
+
proceed=False,
|
|
62
|
+
feedback_mode="stuck",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
recommend_revert = bool(
|
|
66
|
+
invariant_recommend_revert
|
|
67
|
+
or challenge_gate_violation
|
|
68
|
+
or requirement_audit_violation
|
|
69
|
+
or (strict_mode and structured_stop_violation)
|
|
70
|
+
)
|
|
71
|
+
return StopVerdict(
|
|
72
|
+
session_status=base_status,
|
|
73
|
+
recommend_revert=recommend_revert,
|
|
74
|
+
proceed=not recommend_revert,
|
|
75
|
+
feedback_mode="reconsider_approach" if base_status != "completed" and loop_detected else "normal",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _challenge_gate_violation(
|
|
80
|
+
*,
|
|
81
|
+
strict_challenge_gate: bool,
|
|
82
|
+
missing_challenge_coverage: bool,
|
|
83
|
+
challenge_ok: bool | None,
|
|
84
|
+
) -> bool:
|
|
85
|
+
return strict_challenge_gate and (missing_challenge_coverage or challenge_ok is False)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _requirement_audit_violation(
|
|
89
|
+
*,
|
|
90
|
+
strict_mode: bool,
|
|
91
|
+
fail_on_requirement_audit_gap: bool,
|
|
92
|
+
requirements_gate_gap: bool,
|
|
93
|
+
requirement_audit_missing: bool,
|
|
94
|
+
require_requirement_audit: bool,
|
|
95
|
+
) -> bool:
|
|
96
|
+
return bool(
|
|
97
|
+
strict_mode
|
|
98
|
+
and fail_on_requirement_audit_gap
|
|
99
|
+
and (requirements_gate_gap or (requirement_audit_missing and require_requirement_audit))
|
|
100
|
+
)
|
cortex/stop_runtime.py
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable, Mapping
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from .challenges import ChallengeEnforcer, ChallengeReport
|
|
9
|
+
from .core_helpers import (
|
|
10
|
+
session_changed_files_since_baseline,
|
|
11
|
+
session_git_snapshot,
|
|
12
|
+
session_metadata,
|
|
13
|
+
session_required_requirement_ids,
|
|
14
|
+
session_witness_context,
|
|
15
|
+
)
|
|
16
|
+
from .genome import CortexGenome
|
|
17
|
+
from .graveyard import Graveyard
|
|
18
|
+
from .invariants import InvariantReport, InvariantRunner
|
|
19
|
+
from .requirements import (
|
|
20
|
+
RequirementAuditEvaluation,
|
|
21
|
+
TruthClaimsEvaluation,
|
|
22
|
+
evaluate_requirement_audit_payload,
|
|
23
|
+
evaluate_truth_claims_payload,
|
|
24
|
+
)
|
|
25
|
+
from .stop_contract import (
|
|
26
|
+
StopContract,
|
|
27
|
+
reconcile_required_requirement_ids,
|
|
28
|
+
structured_stop_contract_diagnostic,
|
|
29
|
+
)
|
|
30
|
+
from .stop_policy import compute_stop_outcome
|
|
31
|
+
from .stop_signals import build_stop_attempt_signature, stop_attempt_similarity
|
|
32
|
+
from .store import SQLiteStore
|
|
33
|
+
from .utils import _as_bool, _as_string_list
|
|
34
|
+
|
|
35
|
+
LOOP_WARNING = (
|
|
36
|
+
"Stop attempt is highly similar to the previous failed Stop; reconsider the approach "
|
|
37
|
+
"instead of refining the same attempt."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(slots=True)
|
|
42
|
+
class StopPathOutcome:
|
|
43
|
+
warnings: list[str]
|
|
44
|
+
challenge_report: ChallengeReport | None
|
|
45
|
+
challenge_diagnostics: list[dict[str, Any]]
|
|
46
|
+
missing_challenge_coverage: bool
|
|
47
|
+
invariant_report: InvariantReport | None
|
|
48
|
+
requirement_audit: RequirementAuditEvaluation
|
|
49
|
+
truth_claims: TruthClaimsEvaluation
|
|
50
|
+
required_requirement_ids: list[str]
|
|
51
|
+
required_requirement_ids_source: str
|
|
52
|
+
contract_diagnostic: dict[str, Any] | None
|
|
53
|
+
git_snapshot: dict[str, Any] | None
|
|
54
|
+
stuck_declaration: dict[str, Any] | None
|
|
55
|
+
structured_stop_violation: bool
|
|
56
|
+
strict_message_fallback_violation: bool
|
|
57
|
+
enforcement_pass: bool
|
|
58
|
+
session_status: str
|
|
59
|
+
recommend_revert: bool
|
|
60
|
+
proceed: bool
|
|
61
|
+
feedback_mode: str
|
|
62
|
+
stop_attempt_signature: dict[str, Any]
|
|
63
|
+
loop_detected: bool
|
|
64
|
+
loop_similarity: float | None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class StopPathRunner:
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
*,
|
|
71
|
+
root: Path,
|
|
72
|
+
store: SQLiteStore,
|
|
73
|
+
genome: CortexGenome,
|
|
74
|
+
challenges: ChallengeEnforcer,
|
|
75
|
+
invariants: InvariantRunner,
|
|
76
|
+
graveyard: Graveyard,
|
|
77
|
+
session_metadata_loader: Callable[[SQLiteStore, str], dict[str, Any]] = session_metadata,
|
|
78
|
+
session_git_snapshotter: Callable[[Path], dict[str, Any]] = session_git_snapshot,
|
|
79
|
+
session_changed_files_since_baseline_fn: Callable[
|
|
80
|
+
..., tuple[list[str] | None, str | None]
|
|
81
|
+
] = session_changed_files_since_baseline,
|
|
82
|
+
session_required_requirement_ids_loader: Callable[
|
|
83
|
+
[SQLiteStore, str], list[str]
|
|
84
|
+
] = session_required_requirement_ids,
|
|
85
|
+
session_witness_context_loader: Callable[
|
|
86
|
+
[SQLiteStore, str], Mapping[str, list[str]]
|
|
87
|
+
] = session_witness_context,
|
|
88
|
+
) -> None:
|
|
89
|
+
self.root = root
|
|
90
|
+
self.store = store
|
|
91
|
+
self.genome = genome
|
|
92
|
+
self.challenges = challenges
|
|
93
|
+
self.invariants = invariants
|
|
94
|
+
self.graveyard = graveyard
|
|
95
|
+
self._session_metadata = session_metadata_loader
|
|
96
|
+
self._session_git_snapshot = session_git_snapshotter
|
|
97
|
+
self._session_changed_files_since_baseline = session_changed_files_since_baseline_fn
|
|
98
|
+
self._session_required_requirement_ids = session_required_requirement_ids_loader
|
|
99
|
+
self._session_witness_context = session_witness_context_loader
|
|
100
|
+
|
|
101
|
+
def run(
|
|
102
|
+
self,
|
|
103
|
+
*,
|
|
104
|
+
session_id: str,
|
|
105
|
+
payload: Mapping[str, Any],
|
|
106
|
+
stop_contract: StopContract,
|
|
107
|
+
) -> StopPathOutcome:
|
|
108
|
+
session_meta = self._session_metadata(self.store, session_id)
|
|
109
|
+
warnings = list(stop_contract.warnings)
|
|
110
|
+
baseline_git_snapshot = (
|
|
111
|
+
dict(session_meta.get("git_snapshot"))
|
|
112
|
+
if isinstance(session_meta.get("git_snapshot"), Mapping)
|
|
113
|
+
else None
|
|
114
|
+
)
|
|
115
|
+
strict_message_fallback_violation = (
|
|
116
|
+
self.genome.hooks.mode == "strict" and stop_contract.stop_source == "message_fallback"
|
|
117
|
+
)
|
|
118
|
+
contract_diagnostic = stop_contract.contract_diagnostic
|
|
119
|
+
if strict_message_fallback_violation:
|
|
120
|
+
warnings.append(
|
|
121
|
+
"Strict mode rejects Stop message-fallback payloads; send stop fields natively or via payload.stop_fields."
|
|
122
|
+
)
|
|
123
|
+
if contract_diagnostic is None:
|
|
124
|
+
contract_diagnostic = structured_stop_contract_diagnostic(
|
|
125
|
+
"strict_message_fallback_rejected"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
coverage_payload = stop_contract.challenge_coverage
|
|
129
|
+
challenge_report: ChallengeReport | None = None
|
|
130
|
+
challenge_diagnostics: list[dict[str, Any]] = []
|
|
131
|
+
missing_challenge_coverage = False
|
|
132
|
+
require_verifiable_challenge_coverage = (
|
|
133
|
+
self.genome.hooks.mode == "strict"
|
|
134
|
+
and self.genome.hooks.fail_on_missing_challenge_coverage
|
|
135
|
+
)
|
|
136
|
+
needs_witness = bool(
|
|
137
|
+
(require_verifiable_challenge_coverage and isinstance(coverage_payload, Mapping))
|
|
138
|
+
or stop_contract.requirement_audit is not None
|
|
139
|
+
or stop_contract.truth_claims is not None
|
|
140
|
+
)
|
|
141
|
+
witness = (
|
|
142
|
+
self._session_witness_context(self.store, session_id)
|
|
143
|
+
if needs_witness
|
|
144
|
+
else {"commands": [], "tools": []}
|
|
145
|
+
)
|
|
146
|
+
if isinstance(coverage_payload, Mapping):
|
|
147
|
+
challenge_report = self.challenges.evaluate(
|
|
148
|
+
session_id=session_id,
|
|
149
|
+
coverage_payload=coverage_payload,
|
|
150
|
+
require_verifiable_coverage=require_verifiable_challenge_coverage,
|
|
151
|
+
root=self.root,
|
|
152
|
+
witness=witness,
|
|
153
|
+
)
|
|
154
|
+
if not challenge_report.ok:
|
|
155
|
+
warnings.append(
|
|
156
|
+
"Missing challenge coverage for categories: "
|
|
157
|
+
+ ", ".join(challenge_report.missing_categories)
|
|
158
|
+
)
|
|
159
|
+
warnings.extend(challenge_report.config_warnings)
|
|
160
|
+
challenge_diagnostics = list(challenge_report.diagnostics)
|
|
161
|
+
elif coverage_payload is not None:
|
|
162
|
+
missing_challenge_coverage = self.genome.challenges.require_coverage
|
|
163
|
+
warnings.append(
|
|
164
|
+
"Invalid challenge_coverage format; expected an object mapping category names to values."
|
|
165
|
+
)
|
|
166
|
+
challenge_diagnostics = self.challenges.invalid_coverage_diagnostics(coverage_payload)
|
|
167
|
+
elif self.genome.challenges.require_coverage:
|
|
168
|
+
missing_challenge_coverage = True
|
|
169
|
+
message = (
|
|
170
|
+
"No challenge_coverage provided in Stop payload; skipping challenge gate recording. "
|
|
171
|
+
"Include challenge_coverage directly or via payload.stop_fields"
|
|
172
|
+
)
|
|
173
|
+
if self.genome.hooks.allow_message_stop_fallback:
|
|
174
|
+
message += ", or as a STOP_FIELDS_JSON trailer"
|
|
175
|
+
warnings.append(message + ".")
|
|
176
|
+
challenge_diagnostics = self.challenges.missing_coverage_diagnostics()
|
|
177
|
+
|
|
178
|
+
required_requirement_ids, requirement_ids_source, required_ids_warning = (
|
|
179
|
+
reconcile_required_requirement_ids(
|
|
180
|
+
self._session_required_requirement_ids(self.store, session_id),
|
|
181
|
+
list(stop_contract.required_requirement_ids),
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
if required_ids_warning:
|
|
185
|
+
warnings.append(required_ids_warning)
|
|
186
|
+
|
|
187
|
+
truth_claims_payload = (
|
|
188
|
+
stop_contract.truth_claims if isinstance(stop_contract.truth_claims, Mapping) else None
|
|
189
|
+
)
|
|
190
|
+
has_modified_files_claim = bool(
|
|
191
|
+
truth_claims_payload and _as_string_list(truth_claims_payload.get("modified_files"))
|
|
192
|
+
)
|
|
193
|
+
observed_modified_files: list[str] | None = None
|
|
194
|
+
modified_files_error: str | None = None
|
|
195
|
+
if has_modified_files_claim:
|
|
196
|
+
current_git_snapshot = self._session_git_snapshot(self.root)
|
|
197
|
+
observed_modified_files, modified_files_error = self._session_changed_files_since_baseline(
|
|
198
|
+
baseline_snapshot=baseline_git_snapshot,
|
|
199
|
+
current_snapshot=current_git_snapshot,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
requirement_audit = evaluate_requirement_audit_payload(
|
|
203
|
+
stop_contract.requirement_audit,
|
|
204
|
+
require_requirement_audit=self.genome.hooks.require_requirement_audit,
|
|
205
|
+
require_evidence_for_passed_requirement=self.genome.hooks.require_evidence_for_passed_requirement,
|
|
206
|
+
required_requirement_ids=required_requirement_ids,
|
|
207
|
+
root=self.root,
|
|
208
|
+
witness=witness,
|
|
209
|
+
)
|
|
210
|
+
warnings.extend(requirement_audit.warnings)
|
|
211
|
+
truth_claims = evaluate_truth_claims_payload(
|
|
212
|
+
stop_contract.truth_claims,
|
|
213
|
+
root=self.root,
|
|
214
|
+
witness=witness,
|
|
215
|
+
observed_modified_files=observed_modified_files,
|
|
216
|
+
modified_files_error=modified_files_error,
|
|
217
|
+
)
|
|
218
|
+
warnings.extend(truth_claims.warnings)
|
|
219
|
+
|
|
220
|
+
invariant_report = None
|
|
221
|
+
if self.genome.invariants.run_on_stop and _as_bool(payload.get("run_invariants"), True):
|
|
222
|
+
invariant_report = self.invariants.run(
|
|
223
|
+
session_id=session_id,
|
|
224
|
+
extra_pytest_args=_as_string_list(payload.get("pytest_args")),
|
|
225
|
+
)
|
|
226
|
+
if not invariant_report.ok:
|
|
227
|
+
warnings.append("Invariant suite reported failures.")
|
|
228
|
+
|
|
229
|
+
if stop_contract.failed_approach:
|
|
230
|
+
self.graveyard.record_failure(
|
|
231
|
+
session_id=session_id,
|
|
232
|
+
summary=str(stop_contract.failed_approach["summary"]),
|
|
233
|
+
reason=str(stop_contract.failed_approach["reason"]),
|
|
234
|
+
files=_as_string_list(stop_contract.failed_approach.get("files")),
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
structured_stop_violation = (
|
|
238
|
+
stop_contract.structured_stop_violation or strict_message_fallback_violation
|
|
239
|
+
)
|
|
240
|
+
challenge_ok = None if challenge_report is None else challenge_report.ok
|
|
241
|
+
invariant_ok = None if invariant_report is None else invariant_report.ok
|
|
242
|
+
requirements_gate_gap = requirement_audit.gap or truth_claims.gap
|
|
243
|
+
stop_attempt_signature = build_stop_attempt_signature(
|
|
244
|
+
challenge_coverage=coverage_payload if isinstance(coverage_payload, Mapping) else None,
|
|
245
|
+
witness=witness,
|
|
246
|
+
truth_claims_payload=truth_claims_payload,
|
|
247
|
+
failed_approach=stop_contract.failed_approach,
|
|
248
|
+
observed_modified_files=observed_modified_files,
|
|
249
|
+
)
|
|
250
|
+
previous_signature = session_meta.get("stop_attempt_signature")
|
|
251
|
+
loop_similarity = stop_attempt_similarity(
|
|
252
|
+
previous_signature if isinstance(previous_signature, Mapping) else None,
|
|
253
|
+
stop_attempt_signature,
|
|
254
|
+
)
|
|
255
|
+
loop_detected = bool(loop_similarity is not None and loop_similarity >= 0.85)
|
|
256
|
+
verdict = compute_stop_outcome(
|
|
257
|
+
mode=self.genome.hooks.mode,
|
|
258
|
+
fail_on_missing_challenge_coverage=self.genome.hooks.fail_on_missing_challenge_coverage,
|
|
259
|
+
fail_on_requirement_audit_gap=self.genome.hooks.fail_on_requirement_audit_gap,
|
|
260
|
+
require_requirement_audit=self.genome.hooks.require_requirement_audit,
|
|
261
|
+
challenge_ok=challenge_ok,
|
|
262
|
+
invariant_ok=invariant_ok,
|
|
263
|
+
invariant_recommend_revert=bool(invariant_report and invariant_report.recommend_revert),
|
|
264
|
+
missing_challenge_coverage=missing_challenge_coverage,
|
|
265
|
+
requirements_gate_gap=requirements_gate_gap,
|
|
266
|
+
requirement_audit_missing=requirement_audit.missing,
|
|
267
|
+
structured_stop_violation=structured_stop_violation,
|
|
268
|
+
loop_detected=loop_detected,
|
|
269
|
+
stuck_declared=stop_contract.stuck_declaration is not None,
|
|
270
|
+
)
|
|
271
|
+
if loop_detected and verdict.session_status != "completed":
|
|
272
|
+
warnings.append(LOOP_WARNING)
|
|
273
|
+
enforcement_pass = verdict.session_status == "completed"
|
|
274
|
+
|
|
275
|
+
return StopPathOutcome(
|
|
276
|
+
warnings=warnings,
|
|
277
|
+
challenge_report=challenge_report,
|
|
278
|
+
challenge_diagnostics=challenge_diagnostics,
|
|
279
|
+
missing_challenge_coverage=missing_challenge_coverage,
|
|
280
|
+
invariant_report=invariant_report,
|
|
281
|
+
requirement_audit=requirement_audit,
|
|
282
|
+
truth_claims=truth_claims,
|
|
283
|
+
required_requirement_ids=required_requirement_ids,
|
|
284
|
+
required_requirement_ids_source=requirement_ids_source,
|
|
285
|
+
contract_diagnostic=contract_diagnostic,
|
|
286
|
+
git_snapshot=baseline_git_snapshot,
|
|
287
|
+
stuck_declaration=stop_contract.stuck_declaration,
|
|
288
|
+
structured_stop_violation=structured_stop_violation,
|
|
289
|
+
strict_message_fallback_violation=strict_message_fallback_violation,
|
|
290
|
+
enforcement_pass=enforcement_pass,
|
|
291
|
+
session_status=verdict.session_status,
|
|
292
|
+
recommend_revert=verdict.recommend_revert,
|
|
293
|
+
proceed=verdict.proceed,
|
|
294
|
+
feedback_mode=verdict.feedback_mode,
|
|
295
|
+
stop_attempt_signature=stop_attempt_signature,
|
|
296
|
+
loop_detected=loop_detected,
|
|
297
|
+
loop_similarity=loop_similarity,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
@staticmethod
|
|
301
|
+
def close_session_metadata(
|
|
302
|
+
*,
|
|
303
|
+
outcome: StopPathOutcome,
|
|
304
|
+
stop_contract: StopContract,
|
|
305
|
+
executive_signature: str | None,
|
|
306
|
+
executive_record: dict[str, Any] | None,
|
|
307
|
+
) -> dict[str, Any]:
|
|
308
|
+
challenge_report = outcome.challenge_report
|
|
309
|
+
invariant_report = outcome.invariant_report
|
|
310
|
+
requirement_audit = outcome.requirement_audit
|
|
311
|
+
truth_claims = outcome.truth_claims
|
|
312
|
+
requirement_audit_gap = requirement_audit.gap
|
|
313
|
+
truth_claims_gap = truth_claims.gap
|
|
314
|
+
requirements_gate_gap = requirement_audit_gap or truth_claims_gap
|
|
315
|
+
challenge_ok = None if challenge_report is None else challenge_report.ok
|
|
316
|
+
invariant_ok = None if invariant_report is None else invariant_report.ok
|
|
317
|
+
return {
|
|
318
|
+
"hook": "Stop",
|
|
319
|
+
"enforcement_pass": outcome.enforcement_pass,
|
|
320
|
+
"challenge_ok": challenge_ok,
|
|
321
|
+
"challenge_diagnostics": outcome.challenge_diagnostics,
|
|
322
|
+
"challenge_coverage_missing": outcome.missing_challenge_coverage,
|
|
323
|
+
"challenge_unverified_categories": (
|
|
324
|
+
[] if challenge_report is None else challenge_report.unverified_categories
|
|
325
|
+
),
|
|
326
|
+
"challenge_uncheckable_categories": (
|
|
327
|
+
[] if challenge_report is None else challenge_report.uncheckable_categories
|
|
328
|
+
),
|
|
329
|
+
"invariant_ok": invariant_ok,
|
|
330
|
+
"requirement_audit_ok": (
|
|
331
|
+
None if requirement_audit.details is None else requirement_audit.details["ok"]
|
|
332
|
+
),
|
|
333
|
+
"requirement_audit_diagnostics": requirement_audit.diagnostics,
|
|
334
|
+
"requirement_audit_missing": requirement_audit.missing,
|
|
335
|
+
"requirement_audit_gap": requirement_audit_gap,
|
|
336
|
+
"truth_claims_ok": None if truth_claims.report is None else truth_claims.report["ok"],
|
|
337
|
+
"truth_claims_diagnostics": truth_claims.diagnostics,
|
|
338
|
+
"truth_claims_gap": truth_claims_gap,
|
|
339
|
+
"requirements_gate_gap": requirements_gate_gap,
|
|
340
|
+
"required_requirement_ids": outcome.required_requirement_ids,
|
|
341
|
+
"required_requirement_ids_source": outcome.required_requirement_ids_source,
|
|
342
|
+
"contract_diagnostic": outcome.contract_diagnostic,
|
|
343
|
+
"git_snapshot": outcome.git_snapshot,
|
|
344
|
+
"stuck_declared": outcome.stuck_declaration is not None,
|
|
345
|
+
"stuck_declaration": outcome.stuck_declaration,
|
|
346
|
+
"structured_stop_violation": outcome.structured_stop_violation,
|
|
347
|
+
"stop_source": stop_contract.stop_source,
|
|
348
|
+
"stop_fields_source": stop_contract.stop_fields_source,
|
|
349
|
+
"stop_fields_fallback_used": stop_contract.stop_fields_fallback_used,
|
|
350
|
+
"stop_key_normalization_count": stop_contract.stop_key_normalization_count,
|
|
351
|
+
"strict_message_fallback_violation": outcome.strict_message_fallback_violation,
|
|
352
|
+
"feedback_mode": outcome.feedback_mode,
|
|
353
|
+
"stop_attempt_signature": outcome.stop_attempt_signature,
|
|
354
|
+
"loop_detected": outcome.loop_detected,
|
|
355
|
+
"loop_similarity": outcome.loop_similarity,
|
|
356
|
+
"executive_last_stop_signature": executive_signature,
|
|
357
|
+
"executive_memory_recorded": bool(executive_record),
|
|
358
|
+
"executive_memory_record_id": None if executive_record is None else executive_record["id"],
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
@staticmethod
|
|
362
|
+
def response_payload(
|
|
363
|
+
*,
|
|
364
|
+
outcome: StopPathOutcome,
|
|
365
|
+
stop_contract: StopContract,
|
|
366
|
+
) -> dict[str, Any]:
|
|
367
|
+
challenge_report = outcome.challenge_report
|
|
368
|
+
requirement_audit = outcome.requirement_audit
|
|
369
|
+
truth_claims = outcome.truth_claims
|
|
370
|
+
return {
|
|
371
|
+
"session_status": outcome.session_status,
|
|
372
|
+
"challenge_report": None if challenge_report is None else challenge_report.to_dict(),
|
|
373
|
+
"challenge_diagnostics": outcome.challenge_diagnostics,
|
|
374
|
+
"challenge_coverage_missing": outcome.missing_challenge_coverage,
|
|
375
|
+
"invariant_report": None if outcome.invariant_report is None else outcome.invariant_report.to_dict(),
|
|
376
|
+
"requirement_audit_report": requirement_audit.report,
|
|
377
|
+
"requirement_audit_diagnostics": requirement_audit.diagnostics,
|
|
378
|
+
"truth_claims_report": truth_claims.report,
|
|
379
|
+
"truth_claims_diagnostics": truth_claims.diagnostics,
|
|
380
|
+
"required_requirement_ids": outcome.required_requirement_ids,
|
|
381
|
+
"requirement_audit_missing": requirement_audit.missing,
|
|
382
|
+
"requirement_audit_gap": requirement_audit.gap,
|
|
383
|
+
"truth_claims_gap": truth_claims.gap,
|
|
384
|
+
"requirements_gate_gap": requirement_audit.gap or truth_claims.gap,
|
|
385
|
+
"enforcement_pass": outcome.enforcement_pass,
|
|
386
|
+
"contract_diagnostic": outcome.contract_diagnostic,
|
|
387
|
+
"stuck_declared": outcome.stuck_declaration is not None,
|
|
388
|
+
"stuck_declaration": outcome.stuck_declaration,
|
|
389
|
+
"structured_stop_violation": outcome.structured_stop_violation,
|
|
390
|
+
"stop_source": stop_contract.stop_source,
|
|
391
|
+
"stop_fields_source": stop_contract.stop_fields_source,
|
|
392
|
+
"stop_fields_fallback_used": stop_contract.stop_fields_fallback_used,
|
|
393
|
+
"stop_key_normalization_count": stop_contract.stop_key_normalization_count,
|
|
394
|
+
"feedback_mode": outcome.feedback_mode,
|
|
395
|
+
"stop_attempt_signature": outcome.stop_attempt_signature,
|
|
396
|
+
"loop_detected": outcome.loop_detected,
|
|
397
|
+
"loop_similarity": outcome.loop_similarity,
|
|
398
|
+
"recommend_revert": outcome.recommend_revert,
|
|
399
|
+
"proceed": outcome.proceed,
|
|
400
|
+
}
|
cortex/stop_signals.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .utils import _as_string_list
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def build_stop_attempt_signature(
|
|
10
|
+
*,
|
|
11
|
+
challenge_coverage: Mapping[str, Any] | None,
|
|
12
|
+
witness: Mapping[str, list[str]] | None,
|
|
13
|
+
truth_claims_payload: Mapping[str, Any] | None,
|
|
14
|
+
failed_approach: Mapping[str, Any] | None,
|
|
15
|
+
observed_modified_files: list[str] | None,
|
|
16
|
+
) -> dict[str, Any]:
|
|
17
|
+
challenge_shape: list[str] = []
|
|
18
|
+
if isinstance(challenge_coverage, Mapping):
|
|
19
|
+
for category in sorted(str(key).strip() for key in challenge_coverage.keys()):
|
|
20
|
+
raw = challenge_coverage.get(category)
|
|
21
|
+
if isinstance(raw, Mapping):
|
|
22
|
+
challenge_shape.append(f"{category}:covered={bool(raw.get('covered', False))}")
|
|
23
|
+
else:
|
|
24
|
+
challenge_shape.append(f"{category}:covered={bool(raw)}")
|
|
25
|
+
|
|
26
|
+
witnessed_commands = sorted(
|
|
27
|
+
{
|
|
28
|
+
normalize_stop_command_signal(command)
|
|
29
|
+
for command in _as_string_list((witness or {}).get("commands"))
|
|
30
|
+
if normalize_stop_command_signal(command)
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
file_signal = sorted(
|
|
34
|
+
{
|
|
35
|
+
path
|
|
36
|
+
for path in (
|
|
37
|
+
_as_string_list((truth_claims_payload or {}).get("modified_files"))
|
|
38
|
+
+ _as_string_list((failed_approach or {}).get("files"))
|
|
39
|
+
+ _as_string_list(observed_modified_files)
|
|
40
|
+
)
|
|
41
|
+
if path
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
return {
|
|
45
|
+
"challenge_shape": challenge_shape,
|
|
46
|
+
"witnessed_commands": witnessed_commands,
|
|
47
|
+
"file_signal": file_signal,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def stop_attempt_similarity(previous: Mapping[str, Any] | None, current: Mapping[str, Any]) -> float | None:
|
|
52
|
+
if not isinstance(previous, Mapping):
|
|
53
|
+
return None
|
|
54
|
+
scores = [
|
|
55
|
+
_signal_jaccard(previous.get("challenge_shape"), current.get("challenge_shape")),
|
|
56
|
+
_signal_jaccard(previous.get("witnessed_commands"), current.get("witnessed_commands")),
|
|
57
|
+
_signal_jaccard(previous.get("file_signal"), current.get("file_signal")),
|
|
58
|
+
]
|
|
59
|
+
present_scores = [score for score in scores if score is not None]
|
|
60
|
+
return round(sum(present_scores) / len(present_scores), 3) if present_scores else None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def normalize_stop_command_signal(command: str) -> str:
|
|
64
|
+
return " ".join(str(command).strip().lower().split())
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _signal_jaccard(left: Any, right: Any) -> float | None:
|
|
68
|
+
left_set = set(_as_string_list(left))
|
|
69
|
+
right_set = set(_as_string_list(right))
|
|
70
|
+
if not left_set and not right_set:
|
|
71
|
+
return None
|
|
72
|
+
union = left_set | right_set
|
|
73
|
+
if not union:
|
|
74
|
+
return None
|
|
75
|
+
return len(left_set & right_set) / len(union)
|