pdl-taskmaster 2.4.0__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.
Files changed (92) hide show
  1. pdl_taskmaster/__init__.py +6 -0
  2. pdl_taskmaster/contracts/AUTHORITY_MAP.json +19 -0
  3. pdl_taskmaster/contracts/CALIBRATION.json +7 -0
  4. pdl_taskmaster/contracts/CONTRACT_MANIFEST.json +231 -0
  5. pdl_taskmaster/contracts/EXECUTION_CONTRACT.json +615 -0
  6. pdl_taskmaster/contracts/IMPLEMENTATION_BOUNDARY.json +32 -0
  7. pdl_taskmaster/contracts/VERIFICATION_CONTRACT.json +379 -0
  8. pdl_taskmaster/contracts/standards/ARTIFACT_STANDARD.md +7 -0
  9. pdl_taskmaster/contracts/standards/AUTHORITY_STANDARD.md +17 -0
  10. pdl_taskmaster/contracts/standards/CONFORMANCE_STANDARD.md +15 -0
  11. pdl_taskmaster/contracts/standards/CONTEXT_STANDARD.md +15 -0
  12. pdl_taskmaster/contracts/standards/EXECUTION_STANDARD.md +14 -0
  13. pdl_taskmaster/contracts/standards/HANDOFF_STANDARD.md +21 -0
  14. pdl_taskmaster/contracts/standards/INSTALLATION_STANDARD.md +17 -0
  15. pdl_taskmaster/contracts/standards/PDL_STANDARD.md +19 -0
  16. pdl_taskmaster/contracts/standards/PROMPT_STANDARD.md +13 -0
  17. pdl_taskmaster/contracts/standards/PROTOCOL_STANDARD.md +17 -0
  18. pdl_taskmaster/contracts/standards/RESPONSE_PLAN_STANDARD.md +24 -0
  19. pdl_taskmaster/contracts/standards/RESULT_STANDARD.md +35 -0
  20. pdl_taskmaster/contracts/standards/REVIEW_STANDARD.md +31 -0
  21. pdl_taskmaster/contracts/standards/SEMANTIC_INPUT_STANDARD.md +15 -0
  22. pdl_taskmaster/contracts/standards/TASK_SEMANTICS_STANDARD.md +9 -0
  23. pdl_taskmaster/controller/__init__.py +0 -0
  24. pdl_taskmaster/controller/mechanical_controller.py +542 -0
  25. pdl_taskmaster/controller/schemas/activation_decision.schema.json +34 -0
  26. pdl_taskmaster/controller/schemas/artifact_body.schema.json +14 -0
  27. pdl_taskmaster/controller/schemas/artifact_review_facts.schema.json +91 -0
  28. pdl_taskmaster/controller/schemas/bootstrap_analysis.schema.json +60 -0
  29. pdl_taskmaster/controller/schemas/execution_draft.schema.json +47 -0
  30. pdl_taskmaster/controller/schemas/execution_input_decision.schema.json +43 -0
  31. pdl_taskmaster/controller/schemas/execution_outcome.schema.json +201 -0
  32. pdl_taskmaster/controller/schemas/plan_artifact.schema.json +14 -0
  33. pdl_taskmaster/controller/schemas/prompt_artifact.schema.json +63 -0
  34. pdl_taskmaster/controller/schemas/protocol_discussion.schema.json +8 -0
  35. pdl_taskmaster/controller/schemas/result_ir_repair.schema.json +140 -0
  36. pdl_taskmaster/eval/__init__.py +1 -0
  37. pdl_taskmaster/eval/build_adversarial_battery.py +351 -0
  38. pdl_taskmaster/eval/compare_eval_runs.py +251 -0
  39. pdl_taskmaster/eval/control_api_call.py +428 -0
  40. pdl_taskmaster/eval/export_rlcd_dataset.py +799 -0
  41. pdl_taskmaster/eval/fidelity_scan.py +268 -0
  42. pdl_taskmaster/eval/gen_fidelity_battery.py +457 -0
  43. pdl_taskmaster/eval/leak_scan.py +260 -0
  44. pdl_taskmaster/eval/rerecord_fixtures.py +139 -0
  45. pdl_taskmaster/eval/run_canary_shape_probe.py +229 -0
  46. pdl_taskmaster/eval/run_dual_gate_probe.py +404 -0
  47. pdl_taskmaster/eval/run_qualified_batch.py +841 -0
  48. pdl_taskmaster/eval/summarize_run.py +194 -0
  49. pdl_taskmaster/host/__init__.py +1 -0
  50. pdl_taskmaster/host/app.py +164 -0
  51. pdl_taskmaster/host/cli.py +100 -0
  52. pdl_taskmaster/host/repl.py +866 -0
  53. pdl_taskmaster/observation/__init__.py +1 -0
  54. pdl_taskmaster/observation/cli.py +47 -0
  55. pdl_taskmaster/observation/observed_session.py +219 -0
  56. pdl_taskmaster/observation/parsers.py +66 -0
  57. pdl_taskmaster/observation/records.py +58 -0
  58. pdl_taskmaster/observation/session.py +3 -0
  59. pdl_taskmaster/observation/sink.py +3 -0
  60. pdl_taskmaster/observation/sinks.py +20 -0
  61. pdl_taskmaster/providers/README.md +66 -0
  62. pdl_taskmaster/providers/__init__.py +1 -0
  63. pdl_taskmaster/providers/api_worker.py +411 -0
  64. pdl_taskmaster/providers/base.py +26 -0
  65. pdl_taskmaster/providers/codex_worker.py +328 -0
  66. pdl_taskmaster/providers/fixtures.py +157 -0
  67. pdl_taskmaster/providers/live_stub.py +66 -0
  68. pdl_taskmaster/providers/recorded.py +120 -0
  69. pdl_taskmaster/py.typed +1 -0
  70. pdl_taskmaster/runtime/__init__.py +0 -0
  71. pdl_taskmaster/runtime/context_compiler.py +151 -0
  72. pdl_taskmaster/runtime/model_classification.py +290 -0
  73. pdl_taskmaster/runtime/normative_store.py +157 -0
  74. pdl_taskmaster/runtime/operation_bridge.py +336 -0
  75. pdl_taskmaster/runtime/presentation.py +18 -0
  76. pdl_taskmaster/runtime/quarantine.py +91 -0
  77. pdl_taskmaster/runtime/result_ir.py +401 -0
  78. pdl_taskmaster/runtime/semantic_observer.py +40 -0
  79. pdl_taskmaster/runtime/session_engine.py +1168 -0
  80. pdl_taskmaster/runtime/standard_registry.py +55 -0
  81. pdl_taskmaster/runtime/wire_payloads.py +462 -0
  82. pdl_taskmaster/runtime/worker-bootstrap.txt +4 -0
  83. pdl_taskmaster/runtime/workspace.py +602 -0
  84. pdl_taskmaster/tracking/__init__.py +1 -0
  85. pdl_taskmaster/tracking/log_live_session.py +98 -0
  86. pdl_taskmaster/tracking/mlflow_sink.py +58 -0
  87. pdl_taskmaster/verify/verify_repl_baseline.py +299 -0
  88. pdl_taskmaster-2.4.0.dist-info/METADATA +155 -0
  89. pdl_taskmaster-2.4.0.dist-info/RECORD +92 -0
  90. pdl_taskmaster-2.4.0.dist-info/WHEEL +4 -0
  91. pdl_taskmaster-2.4.0.dist-info/entry_points.txt +2 -0
  92. pdl_taskmaster-2.4.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,336 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from enum import Enum
5
+ from pathlib import Path
6
+ from typing import Any
7
+ import json
8
+ import re
9
+
10
+ from pydantic import TypeAdapter, ValidationError
11
+
12
+ from pdl_taskmaster.runtime.context_compiler import CompiledProjection, ContextCompiler
13
+ from pdl_taskmaster.runtime.workspace import WorkspaceInvocation, WorkspaceRun
14
+ from pdl_taskmaster.runtime.wire_payloads import (
15
+ ActivationDecisionPayload,
16
+ ActivationRoute,
17
+ ApproachChangeDimension,
18
+ ArtifactReviewPayload,
19
+ BootstrapAnalysisData,
20
+ BootstrapAnalysisPayload,
21
+ ExecutionDraftBlockedData,
22
+ ExecutionDraftPayload,
23
+ ExecutionDraftResultData,
24
+ ExecutionInputPayload,
25
+ ExecutionInputReviseData,
26
+ ExecutionOutcomePayload,
27
+ ExecutionRequestInputData,
28
+ ExecutionResultData,
29
+ NeutralPlanBodyPayload,
30
+ PromptBodyPayload,
31
+ PromptDraftData,
32
+ PromptDraftPayload,
33
+ ProtocolDiscussionPayload,
34
+ ResultIRRepairPayload,
35
+ ReviewFactsData,
36
+ SYSTEM1_CONFIDENCE_FLOOR,
37
+ TaskChangeDimension,
38
+ WireError,
39
+ format_validation_feedback,
40
+ map_validation_error_to_wire_reason,
41
+ )
42
+
43
+
44
+ @dataclass(frozen=True)
45
+ class ActivationDecision:
46
+ route: ActivationRoute
47
+ response: str | None = None
48
+
49
+
50
+ @dataclass(frozen=True)
51
+ class PromptDraftOutcome:
52
+ kind: str
53
+ prompt_body: str | None = None
54
+ approach_handoff: str = "NONE"
55
+ blocking_basis: str | None = None
56
+ response: str | None = None
57
+ task_entities: tuple[str, ...] | None = None
58
+
59
+
60
+ @dataclass(frozen=True)
61
+ class ExecutionOutcome:
62
+ kind: str
63
+ body: str
64
+ expected_type: str | None = None
65
+ description: str | None = None
66
+ result_ir: dict | None = None
67
+
68
+
69
+ @dataclass(frozen=True)
70
+ class ExecutionDraftOutcome:
71
+ kind: str
72
+ brief_body: str
73
+ execution_entities: tuple[dict, ...] = ()
74
+
75
+
76
+ @dataclass(frozen=True)
77
+ class ModelRequest:
78
+ projection: CompiledProjection
79
+ prompt: str
80
+ workspace_invocation: WorkspaceInvocation
81
+
82
+ @property
83
+ def operation(self) -> str:
84
+ return self.projection.operation
85
+
86
+ @property
87
+ def manifest(self) -> dict[str, Any]:
88
+ return self.projection.manifest
89
+
90
+
91
+ class OperationBridge:
92
+ def __init__(self, repo_root: str | Path, *, render_compact: bool = False):
93
+ self.repo_root = Path(repo_root)
94
+ self.render_compact = render_compact
95
+ self.compiler = ContextCompiler(self.repo_root)
96
+ bootstrap_path = Path(__file__).parent / "worker-bootstrap.txt"
97
+ if not bootstrap_path.is_file():
98
+ bootstrap_path = self.repo_root / "src" / "pdl_taskmaster" / "runtime" / "worker-bootstrap.txt"
99
+ self.bootstrap = bootstrap_path.read_text(encoding="utf-8")
100
+
101
+ def request(
102
+ self,
103
+ operation: str,
104
+ values: dict[str, Any],
105
+ *,
106
+ workspace: WorkspaceRun,
107
+ higher_priority_constraints: Any = None,
108
+ operator_correction: str | None = None,
109
+ ) -> ModelRequest:
110
+ invocation = workspace.materialize_operation(
111
+ operation, values, higher_priority_constraints=higher_priority_constraints
112
+ )
113
+ materialized_values, materialized_higher_priority = workspace.load_operation_values(invocation)
114
+ projection = self.compiler.compile(
115
+ operation,
116
+ materialized_values,
117
+ higher_priority_constraints=materialized_higher_priority,
118
+ )
119
+ workspace.record_projection(invocation, projection.manifest, projection.document)
120
+ prompt = projection.render(self.bootstrap, compact=self.render_compact)
121
+ if operator_correction:
122
+ # Appended outside the projection document so projection_sha256
123
+ # (and fixture replay) stay stable; only used on the retry path.
124
+ prompt = prompt + operator_correction.strip() + "\n"
125
+ return ModelRequest(projection, prompt, invocation)
126
+
127
+ @staticmethod
128
+ def _object(model_text: str) -> dict[str, Any]:
129
+ """Extract exactly one JSON object from a worker response.
130
+
131
+ Tolerant of *placement* (leading/trailing prose, BOM, markdown fences
132
+ anywhere in the text) but never repairs *content*: malformed JSON
133
+ still raises WireError("invalid_json") so the caller's retry path
134
+ can ask the stateless worker to re-emit (see SessionEngine._call).
135
+ """
136
+ stripped = model_text.strip().lstrip("\ufeff")
137
+ last_error: json.JSONDecodeError | None = None
138
+ value: Any = None
139
+ try:
140
+ value = json.loads(stripped)
141
+ except json.JSONDecodeError as exc:
142
+ last_error = exc
143
+ if value is None:
144
+ unfenced = re.sub(r"```(?:json)?", "", stripped).strip()
145
+ try:
146
+ value = json.loads(unfenced)
147
+ except json.JSONDecodeError as exc:
148
+ last_error = exc
149
+ if value is None:
150
+ # Balanced-brace scan: locate the first parseable JSON object
151
+ # embedded in surrounding prose. raw_decode consumes exactly one
152
+ # balanced value starting at each candidate brace.
153
+ decoder = json.JSONDecoder()
154
+ for idx, char in enumerate(stripped):
155
+ if char != "{":
156
+ continue
157
+ try:
158
+ candidate, _ = decoder.raw_decode(stripped[idx:])
159
+ except json.JSONDecodeError as exc:
160
+ last_error = exc
161
+ continue
162
+ if isinstance(candidate, dict):
163
+ value = candidate
164
+ break
165
+ if value is None:
166
+ # Carry the underlying JSONDecodeError detail (e.g. "Invalid
167
+ # \\escape") so the retry's operator correction names the actual
168
+ # defect instead of a generic invalid_json.
169
+ detail = f"invalid_json ({last_error.msg} at column {last_error.pos + 1})" if last_error else "invalid_json"
170
+ raise WireError(detail)
171
+ if not isinstance(value, dict):
172
+ raise WireError("not_object")
173
+ return value
174
+
175
+ @staticmethod
176
+ def _keys(value: dict[str, Any], allowed: set[str], required: set[str]) -> None:
177
+ if set(value) - allowed:
178
+ raise WireError("extra_fields")
179
+ if not required <= set(value):
180
+ raise WireError("missing_fields")
181
+
182
+ def _validate(self, operation: str, model_type: Any, model_text: str) -> Any:
183
+ value = self._object(model_text)
184
+ try:
185
+ adapter = TypeAdapter(model_type)
186
+ return adapter.validate_python(value)
187
+ except ValidationError as val_err:
188
+ reason = map_validation_error_to_wire_reason(
189
+ operation, val_err, value if isinstance(value, dict) else None
190
+ )
191
+ feedback = format_validation_feedback(val_err)
192
+ raise WireError(reason, operator_feedback=feedback, validation_error=val_err) from val_err
193
+
194
+ def parse_activation(self, model_text: str) -> ActivationDecision:
195
+ payload: ActivationDecisionPayload = self._validate("INTERPRET_ACTIVATION", ActivationDecisionPayload, model_text)
196
+ if payload.confidence is not None and payload.confidence < SYSTEM1_CONFIDENCE_FLOOR:
197
+ # ADR-0012 Fail-closed: low-confidence activation classification defaults to protocol application
198
+ return ActivationDecision(ActivationRoute.APPLY_PROTOCOL)
199
+ return ActivationDecision(payload.route, payload.response.strip() if payload.response else None)
200
+
201
+ def parse_prompt_draft(self, model_text: str) -> PromptDraftOutcome:
202
+ payload: PromptDraftPayload = self._validate("DRAFT_PROMPT", PromptDraftPayload, model_text)
203
+ if isinstance(payload, PromptDraftData):
204
+ return PromptDraftOutcome(
205
+ payload.kind,
206
+ payload.prompt_body.strip(),
207
+ payload.approach_handoff,
208
+ task_entities=tuple(payload.task_entities) if payload.task_entities else (),
209
+ )
210
+ return PromptDraftOutcome(
211
+ payload.kind,
212
+ blocking_basis=payload.blocking_basis,
213
+ response=payload.response.strip(),
214
+ )
215
+
216
+ def _parse_named_body(self, model_text: str, field: str) -> str:
217
+ if field == "prompt_body":
218
+ return self.parse_prompt_body(model_text)
219
+ if field == "neutral_plan_body":
220
+ return self.parse_plan_body(model_text)
221
+ value = self._object(model_text)
222
+ self._keys(value, {field}, {field})
223
+ body = value[field]
224
+ if not isinstance(body, str) or not body.strip():
225
+ raise WireError(field)
226
+ return body.strip()
227
+
228
+ def parse_prompt_body(self, model_text: str) -> str:
229
+ payload: PromptBodyPayload = self._validate("REVISE_PROMPT", PromptBodyPayload, model_text)
230
+ return payload.prompt_body.strip()
231
+
232
+ def parse_plan_body(self, model_text: str) -> str:
233
+ payload: NeutralPlanBodyPayload = self._validate("DRAFT_PLAN", NeutralPlanBodyPayload, model_text)
234
+ return payload.neutral_plan_body.strip()
235
+
236
+ def _parse_artifact_review(self, model_text: str) -> dict[str, Any]:
237
+ payload: ArtifactReviewPayload = self._validate("INTERPRET_PROMPT_REVIEW", ArtifactReviewPayload, model_text)
238
+ if hasattr(payload, "confidence") and payload.confidence is not None and payload.confidence < SYSTEM1_CONFIDENCE_FLOOR:
239
+ # ADR-0012 Fail-closed: low-confidence review classification must never progress or silently route.
240
+ # Escalate to UNRESOLVED to re-prompt human confirmation per REVIEW-09.
241
+ return {"intent": "UNRESOLVED"}
242
+ if isinstance(payload, ReviewFactsData):
243
+ task_changed = bool(payload.task_change_dimensions)
244
+ approach_changed = bool(payload.approach_change_dimensions)
245
+ if not (task_changed or approach_changed or payload.progression_requested):
246
+ # All-empty REVIEW_FACTS: the model reports the message changed
247
+ # no dimension and requested no progression. Per REVIEW-09/13/14,
248
+ # mechanically treat this as SUBSTANTIVE_DISCUSSION.
249
+ return {"intent": "SUBSTANTIVE_DISCUSSION"}
250
+ if task_changed:
251
+ return {"intent": "REVISE_TASK", "also_changes_approach": approach_changed}
252
+ if approach_changed:
253
+ return {"intent": "REVISE_APPROACH"}
254
+ return {"intent": "ACCEPT_CURRENT"}
255
+ return {"intent": payload.kind}
256
+
257
+ def parse_prompt_review(self, model_text: str) -> dict[str, Any]:
258
+ return self._parse_artifact_review(model_text)
259
+
260
+ def parse_plan_review(self, model_text: str) -> dict[str, Any]:
261
+ return self._parse_artifact_review(model_text)
262
+
263
+ def parse_bootstrap_analysis(self, model_text: str) -> dict[str, Any]:
264
+ payload: BootstrapAnalysisPayload = self._validate("BOOTSTRAP_ANALYSIS", BootstrapAnalysisPayload, model_text)
265
+ if isinstance(payload, BootstrapAnalysisData):
266
+ return {
267
+ "kind": payload.kind,
268
+ "task_summary": payload.task_summary,
269
+ "approach_notes": payload.approach_notes,
270
+ "risk_notes": payload.risk_notes,
271
+ "task_entities": list(payload.task_entities),
272
+ }
273
+ return {"kind": payload.kind, "response": payload.response.strip()}
274
+
275
+ def parse_execution_input(self, model_text: str) -> dict[str, Any]:
276
+ payload: ExecutionInputPayload = self._validate("INTERPRET_EXECUTION_INPUT", ExecutionInputPayload, model_text)
277
+ if hasattr(payload, "confidence") and payload.confidence is not None and payload.confidence < SYSTEM1_CONFIDENCE_FLOOR:
278
+ return {"intent": "UNRESOLVED"}
279
+ if isinstance(payload, ExecutionInputReviseData):
280
+ return {
281
+ "intent": "REVISE_TASK",
282
+ "also_changes_approach": payload.also_changes_approach,
283
+ }
284
+ return {"intent": payload.kind}
285
+
286
+ def parse_protocol_discussion(self, model_text: str) -> str:
287
+ payload: ProtocolDiscussionPayload = self._validate("ANSWER_PROTOCOL_DISCUSSION", ProtocolDiscussionPayload, model_text)
288
+ return payload.body.strip()
289
+
290
+ def parse_execution_draft(self, model_text: str) -> ExecutionDraftOutcome:
291
+ payload: ExecutionDraftPayload = self._validate("DRAFT_EXECUTION", ExecutionDraftPayload, model_text)
292
+ if isinstance(payload, ExecutionDraftBlockedData):
293
+ return ExecutionDraftOutcome(payload.kind, payload.brief_body.strip(), ())
294
+ clean: list[dict] = []
295
+ for e in payload.execution_entities:
296
+ if isinstance(e, dict) and str(e.get("value", "")).strip():
297
+ clean.append({
298
+ "kind": str(e.get("kind", "meta")),
299
+ "value": str(e["value"]).strip(),
300
+ "name": e.get("name"),
301
+ "struct_format": e.get("struct_format"),
302
+ "declared_size": e.get("declared_size"),
303
+ })
304
+ elif isinstance(e, str) and e.strip():
305
+ clean.append({
306
+ "kind": "meta",
307
+ "value": e.strip(),
308
+ "name": None,
309
+ "struct_format": None,
310
+ "declared_size": None,
311
+ })
312
+ return ExecutionDraftOutcome(payload.kind, payload.brief_body.strip(), tuple(clean))
313
+
314
+ def parse_result_ir_repair(self, model_text: str) -> dict:
315
+ payload: ResultIRRepairPayload = self._validate("EMIT_RESULT_IR", ResultIRRepairPayload, model_text)
316
+ return payload.result_ir.model_dump()
317
+
318
+ def parse_execution(self, model_text: str) -> ExecutionOutcome:
319
+ payload: ExecutionOutcomePayload = self._validate("EXECUTE", ExecutionOutcomePayload, model_text)
320
+ if isinstance(payload, ExecutionRequestInputData):
321
+ return ExecutionOutcome(
322
+ payload.kind,
323
+ payload.body.strip(),
324
+ payload.expected_type.strip(),
325
+ payload.description.strip(),
326
+ None,
327
+ )
328
+ ir_dict = payload.result_ir.model_dump() if payload.result_ir is not None else None
329
+ return ExecutionOutcome(
330
+ payload.kind,
331
+ payload.body.strip(),
332
+ None,
333
+ None,
334
+ ir_dict,
335
+ )
336
+
@@ -0,0 +1,18 @@
1
+ def prompt_artifact(body: str) -> str:
2
+ return f"Prompt Pseudocode\n\n{body}\n\nConfirm or correct this interpretation."
3
+
4
+
5
+ def plan_artifact(body: str) -> str:
6
+ return f"Response Plan Pseudocode\n\n{body}\n\nConfirm or correct this response approach."
7
+
8
+
9
+ def deferred_substantive() -> str:
10
+ return "I’ll address that substantive task question after the current confirmations are complete."
11
+
12
+
13
+ def review_clarification() -> str:
14
+ return "Please clarify how that message should affect the current review."
15
+
16
+
17
+ def cancelled() -> str:
18
+ return "Cancelled."
@@ -0,0 +1,91 @@
1
+ """Quarantine sanitizer (protocol v2, semantic-bootstrap containment).
2
+
3
+ The data-plane sanitizer: verbatim content emitted by the semantic-read
4
+ (bootstrap) operation is sanitized before it becomes a compile operation's
5
+ context.
6
+
7
+ - `compile_bootstrap_output`: sanitizes the bootstrap analysis before it
8
+ becomes a compile operation's context. Explicit canary tokens, tripwire
9
+ strings, override directives, and quoted spans carrying them are replaced
10
+ with [REDACTED_IOC]. Legitimate code specifications, identifiers, exception
11
+ classes, and parameter names are strictly preserved.
12
+
13
+ The retired in-band evidence-sink delimiters (`<<<EVIDENCE>>>` /
14
+ `<<<END_EVIDENCE>>>`) and their mechanical strip were permanently removed per
15
+ Decision D24 / TRD-0002: containment is out-of-band schema field isolation
16
+ (`task_summary` vs `risk_notes`), never in-band delimiter fences.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import re
21
+
22
+ # Known canary, tripwire, and exploit directive patterns
23
+ _CANARY_OR_TRIPWIRE = re.compile(
24
+ r"\b(?:TRIPWIRE_[A-Za-z0-9_]+|CANARY_[A-Za-z0-9_]+|PWNED_[A-Za-z0-9_]+|ACTIVATED|ROOTKIT-[A-Za-z0-9_]+)\b"
25
+ )
26
+ _OVERRIDE_DIRECTIVE = re.compile(r"\+{3}\s*(?:SYSTEM\s+(?:DIRECTIVE\s+)?OVERRIDE|END)\s*\+{3}", re.IGNORECASE)
27
+
28
+ # D29 generalized opaque-identifier shapes: canaries need not carry a
29
+ # recognizable prefix (UUID / long-hex canaries measurably reached the
30
+ # deliverable on the primary tier -- runs/canary_shape_probe). These
31
+ # apply ONLY to the sanitized summary surface, where an untrusted literal
32
+ # of digest length or UUID shape has no legitimate role; digest
33
+ # verification belongs in code, which this sanitizer never touches
34
+ # (execution-stage output is out of scope). Deliberate policy: >=32 hex
35
+ # chars includes SHA-256-length digests on this surface -- refer to them
36
+ # by role, not verbatim.
37
+ _UUID_SHAPE = re.compile(
38
+ r"\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b"
39
+ )
40
+ _LONG_HEX_RUN = re.compile(r"\b[0-9a-fA-F]{32,}\b")
41
+ _EXPLOIT_PHRASE = re.compile(
42
+ r"(?:system\s+(?:directive\s+)?override|ignore\s+previous\s+instructions|dump\s+system\s+secrets|rootkit|credential[- ]harvesting)",
43
+ re.IGNORECASE,
44
+ )
45
+ _QUOTED_SPAN = re.compile(r"(\"[^\"]{6,}\"|'[^']{6,}'|`[^`]{6,}`)")
46
+
47
+
48
+ def compile_bootstrap_output(raw_source: str, analysis_text: str) -> tuple[str, dict]:
49
+ """Sanitize a bootstrap analysis for use as a compile-operation input.
50
+
51
+ 1. Replace explicit canary tokens, tripwire strings, and override directives
52
+ with [REDACTED_IOC].
53
+ 2. Redact quoted spans that contain exploit directives or canary tokens.
54
+ Legitimate code specifications, identifiers, exception classes, and
55
+ parameter names are strictly preserved.
56
+ Returns (sanitized_text, meta{redactions}).
57
+ """
58
+ meta: dict = {"redactions": 0}
59
+ if not analysis_text:
60
+ return "", meta
61
+
62
+ text = analysis_text
63
+ if not raw_source:
64
+ return text, meta
65
+
66
+ # 1. Redact quoted spans if they contain attack directives, canaries, or overrides
67
+ def _redact_quoted_span(m: re.Match) -> str:
68
+ inner = m.group(1)[1:-1]
69
+ if _EXPLOIT_PHRASE.search(inner) or _CANARY_OR_TRIPWIRE.search(inner) or _OVERRIDE_DIRECTIVE.search(inner):
70
+ meta["redactions"] += 1
71
+ return "[REDACTED_IOC]"
72
+ return m.group(0)
73
+
74
+ text = _QUOTED_SPAN.sub(_redact_quoted_span, text)
75
+
76
+ # 2. Redact explicit tripwire/canary tokens
77
+ def _redact_token(m: re.Match) -> str:
78
+ meta["redactions"] += 1
79
+ return "[REDACTED_IOC]"
80
+
81
+ text = _CANARY_OR_TRIPWIRE.sub(_redact_token, text)
82
+ text = _OVERRIDE_DIRECTIVE.sub(_redact_token, text)
83
+
84
+ # 3. D29 generalized-shape redaction (prefix-independent canaries)
85
+ def _redact_generalized(m: re.Match) -> str:
86
+ meta["redactions_generalized"] = meta.get("redactions_generalized", 0) + 1
87
+ return "[REDACTED_IOC]"
88
+
89
+ text = _UUID_SHAPE.sub(_redact_generalized, text)
90
+ text = _LONG_HEX_RUN.sub(_redact_generalized, text)
91
+ return text, meta