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,151 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from hashlib import sha256
5
+ from pathlib import Path
6
+ from typing import Any
7
+ import json
8
+
9
+ from pdl_taskmaster.runtime.standard_registry import StandardRegistry
10
+
11
+
12
+ _AUTO_SYMBOLS = {"OPERATION_ID", "APPLICABLE_STANDARD_CLAUSES", "HIGHER_PRIORITY_CONSTRAINTS"}
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class CompiledProjection:
17
+ operation: str
18
+ output_kind: str
19
+ output_fields: tuple[str, ...]
20
+ document: dict[str, Any]
21
+ manifest: dict[str, Any]
22
+
23
+ def render(self, bootstrap: str, *, compact: bool = False) -> str:
24
+ """Render bootstrap + projection document.
25
+
26
+ compact=True serializes the document without indentation (~23% smaller);
27
+ the parsed JSON is identical, so semantics are unchanged. Fixture replay
28
+ hashes the full rendered prompt, so recorded fixtures must keep the
29
+ default pretty rendering.
30
+ """
31
+ if compact:
32
+ body = json.dumps(self.document, ensure_ascii=False, separators=(",", ":"))
33
+ else:
34
+ body = json.dumps(self.document, ensure_ascii=False, indent=2)
35
+ return bootstrap.rstrip() + "\n\n" + body + "\n"
36
+
37
+
38
+ class ContextCompiler:
39
+ def __init__(self, repo_root: str | Path, standards_root: str | Path | None = None):
40
+ self.repo_root = Path(repo_root)
41
+ from pdl_taskmaster.runtime.normative_store import NormativeStore
42
+ self.standards_root = Path(standards_root) if standards_root else NormativeStore.resolve_standards_root(self.repo_root)
43
+ contract_path = NormativeStore.resolve_contract(self.repo_root, "EXECUTION_CONTRACT.json")
44
+ if not contract_path.is_file():
45
+ contract_path = self.repo_root / "contracts" / "EXECUTION_CONTRACT.json"
46
+ self.execution_contract = json.loads(contract_path.read_text(encoding="utf-8"))
47
+ self.registry = StandardRegistry(self.repo_root, standards_root=self.standards_root)
48
+
49
+ def compile(
50
+ self,
51
+ operation: str,
52
+ values: dict[str, Any],
53
+ *,
54
+ higher_priority_constraints: Any = None,
55
+ ) -> CompiledProjection:
56
+ operations = self.execution_contract["operations"]
57
+ if operation not in operations:
58
+ raise ValueError(f"operation:{operation}")
59
+ spec = operations[operation]
60
+ include = tuple(spec["include"])
61
+ # Optional symbols (Phase 9 S4): supplied by the engine only when the
62
+ # session provides them (e.g. PREVIOUS_DELIVERABLE on chained turns);
63
+ # absent optional symbols are omitted from the projection entirely so
64
+ # single-turn projections stay byte-identical (fixture replay safe).
65
+ optional = tuple(spec.get("optional_include", ()))
66
+ expected = set(include) - _AUTO_SYMBOLS
67
+ provided = set(values)
68
+ missing = expected - provided
69
+ extra = (provided - expected) - set(optional)
70
+ if missing:
71
+ raise ValueError(f"missing_symbols:{sorted(missing)}")
72
+ if extra:
73
+ raise ValueError(f"extra_symbols:{sorted(extra)}")
74
+
75
+ clauses = self.registry.select(spec["requirements"])
76
+ clause_values = [
77
+ {"requirement_id": clause.requirement_id, "clause": clause.text}
78
+ for clause in clauses
79
+ ]
80
+ ordered_inputs: dict[str, Any] = {}
81
+ for symbol in include:
82
+ if symbol == "OPERATION_ID":
83
+ ordered_inputs[symbol] = operation
84
+ elif symbol == "APPLICABLE_STANDARD_CLAUSES":
85
+ ordered_inputs[symbol] = clause_values
86
+ elif symbol == "HIGHER_PRIORITY_CONSTRAINTS":
87
+ ordered_inputs[symbol] = higher_priority_constraints
88
+ else:
89
+ ordered_inputs[symbol] = values[symbol]
90
+ for symbol in optional:
91
+ if symbol in provided and values[symbol] is not None:
92
+ ordered_inputs[symbol] = values[symbol]
93
+
94
+ schema_relative = spec.get("output_schema")
95
+ if not isinstance(schema_relative, str):
96
+ raise ValueError(f"output_schema:{operation}")
97
+ schema_path = Path(schema_relative)
98
+ if schema_path.is_absolute() or ".." in schema_path.parts:
99
+ raise ValueError(f"output_schema_path:{operation}")
100
+ full_schema_path = self.repo_root / schema_path
101
+ if not full_schema_path.is_file() and not schema_path.is_absolute():
102
+ rel = Path(*schema_path.parts[1:]) if schema_path.parts and schema_path.parts[0] == "scripts" else schema_path
103
+ pkg_root = Path(__file__).resolve().parents[1]
104
+ for c in (
105
+ pkg_root / rel,
106
+ self.repo_root / "src" / "pdl_taskmaster" / rel,
107
+ pkg_root / schema_path,
108
+ self.repo_root / "src" / "pdl_taskmaster" / schema_path,
109
+ self.repo_root / "scripts" / schema_path,
110
+ ):
111
+ if c.is_file():
112
+ full_schema_path = c
113
+ break
114
+ if not full_schema_path.is_file():
115
+ raise ValueError(f"output_schema_missing:{operation}")
116
+ output_schema = json.loads(full_schema_path.read_text(encoding="utf-8"))
117
+
118
+ document: dict[str, Any] = {
119
+ "operation": operation,
120
+ "output_kind": spec["output_kind"],
121
+ "output_schema": output_schema,
122
+ "operation_inputs": ordered_inputs,
123
+ }
124
+ if spec.get("artifact_kind"):
125
+ document["artifact_kind"] = spec["artifact_kind"]
126
+
127
+ clause_digests = {
128
+ clause.requirement_id: sha256(clause.text.encode("utf-8")).hexdigest()
129
+ for clause in clauses
130
+ }
131
+ manifest = {
132
+ "operation": operation,
133
+ "included_symbols": list(include),
134
+ "excluded_symbols": list(spec.get("exclude", [])),
135
+ "requirement_ids": list(spec["requirements"]),
136
+ "clause_sha256": clause_digests,
137
+ "output_kind": spec["output_kind"],
138
+ "output_fields": list(spec.get("output_fields", [])),
139
+ "output_schema": schema_relative,
140
+ "output_schema_sha256": sha256(full_schema_path.read_bytes()).hexdigest(),
141
+ }
142
+ manifest["projection_sha256"] = sha256(
143
+ json.dumps(document, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
144
+ ).hexdigest()
145
+ return CompiledProjection(
146
+ operation=operation,
147
+ output_kind=spec["output_kind"],
148
+ output_fields=tuple(spec.get("output_fields", [])),
149
+ document=document,
150
+ manifest=manifest,
151
+ )
@@ -0,0 +1,290 @@
1
+ """Model and Provider Classification Taxonomy (Track M2).
2
+
3
+ Prevents cross-tier and cross-provider false equivalences in adversarial evaluation.
4
+ Explicitly distinguishes between:
5
+ 1. Capability Tier (Frontier Flagship vs. Mid-Tier Balanced vs. Lightweight Fast)
6
+ 2. Provider Hosting Class (Direct Dedicated vs. Aggregator BYOK vs. Aggregator Shared Pool)
7
+ 3. Reasoning Architecture (Disabled vs. Native Thinking Traces)
8
+
9
+ Under this taxonomy, comparing a Mid-Tier Balanced model (e.g. GLM-4.7) to a
10
+ Frontier Flagship model (e.g. DeepSeek-V4-Pro, Claude 3.5 Sonnet, GPT-4o) without
11
+ controlling for capability tier constitutes a methodological false equivalence.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from dataclasses import asdict, dataclass
16
+ from enum import Enum
17
+ from typing import Any
18
+
19
+
20
+ class CapabilityTier(str, Enum):
21
+ FRONTIER_FLAGSHIP = "FRONTIER_FLAGSHIP" # Tier 1: DeepSeek-V4-Pro, GPT-4o, Claude 3.5 Sonnet, GLM-5.3
22
+ MID_BALANCED = "MID_BALANCED" # Tier 2: GLM-4.7, DeepSeek-Flash, Qwen-2.5-72B, Llama-3.3-70B
23
+ LIGHT_FAST = "LIGHT_FAST" # Tier 3: GLM-4.7-Flash, Gemini-2.0-Flash, Claude-3.5-Haiku
24
+
25
+
26
+ class ProviderHostingClass(str, Enum):
27
+ DIRECT_FIRST_PARTY = "DIRECT_FIRST_PARTY" # Direct vendor API with dedicated account quota (e.g. api.deepseek.com)
28
+ AGGREGATOR_BYOK = "AGGREGATOR_BYOK" # Aggregator proxy with user's own upstream account key
29
+ AGGREGATOR_SHARED = "AGGREGATOR_SHARED" # Aggregator shared public rate-limit pool (e.g. OpenRouter free/shared)
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class ModelClassification:
34
+ model_id: str
35
+ display_name: str
36
+ family: str
37
+ capability_tier: CapabilityTier
38
+ tier_rank: int # 1 = Frontier, 2 = Mid, 3 = Light
39
+ provider_class: ProviderHostingClass
40
+ upstream_provider: str
41
+ reasoning_mode: str
42
+ recommended_inter_call_delay_s: float
43
+ comparative_peers: list[str]
44
+ methodological_notes: str
45
+
46
+ def as_dict(self) -> dict[str, Any]:
47
+ d = asdict(self)
48
+ d["capability_tier"] = self.capability_tier.value
49
+ d["provider_class"] = self.provider_class.value
50
+ return d
51
+
52
+
53
+ # Known model taxonomy registry
54
+ _TAXONOMY: dict[str, dict[str, Any]] = {
55
+ "z-ai/glm-4.7": {
56
+ "display_name": "GLM-4.7",
57
+ "family": "Zhipu GLM",
58
+ "capability_tier": CapabilityTier.MID_BALANCED,
59
+ "tier_rank": 2,
60
+ "upstream_provider": "Google Vertex (via OpenRouter)",
61
+ "recommended_inter_call_delay_s": 3.0,
62
+ "comparative_peers": ["deepseek-flash", "qwen/qwen-2.5-72b-instruct", "meta-llama/llama-3.3-70b-instruct"],
63
+ "methodological_notes": (
64
+ "GLM-4.7 is a Mid-Tier Balanced workhorse. Comparing it to Frontier Flagships "
65
+ "(e.g. DeepSeek-V4-Pro, Claude-3.5-Sonnet) represents a cross-tier false equivalence. "
66
+ "Valid comparative baselines must be drawn from Tier 2 peers or paired with GLM-5.3 if available."
67
+ ),
68
+ },
69
+ "z-ai/glm-4.7-flash": {
70
+ "display_name": "GLM-4.7-Flash",
71
+ "family": "Zhipu GLM",
72
+ "capability_tier": CapabilityTier.LIGHT_FAST,
73
+ "tier_rank": 3,
74
+ "upstream_provider": "DeepInfra (via OpenRouter)",
75
+ "recommended_inter_call_delay_s": 1.5,
76
+ "comparative_peers": ["google/gemini-2.0-flash-001", "anthropic/claude-3.5-haiku"],
77
+ "methodological_notes": "High-speed Tier 3 lightweight model.",
78
+ },
79
+ "deepseek-flash": {
80
+ "display_name": "DeepSeek-V4.1-Flash",
81
+ "family": "DeepSeek",
82
+ "capability_tier": CapabilityTier.MID_BALANCED,
83
+ "tier_rank": 2,
84
+ "upstream_provider": "DeepSeek First-Party API",
85
+ "recommended_inter_call_delay_s": 1.0,
86
+ "comparative_peers": ["z-ai/glm-4.7", "qwen/qwen-2.5-72b-instruct"],
87
+ "methodological_notes": "Direct competitor to GLM-4.7 within Tier 2 (Mid-Tier Balanced).",
88
+ },
89
+ "deepseek-v4-pro": {
90
+ "display_name": "DeepSeek-V4-Pro",
91
+ "family": "DeepSeek",
92
+ "capability_tier": CapabilityTier.FRONTIER_FLAGSHIP,
93
+ "tier_rank": 1,
94
+ "upstream_provider": "DeepSeek First-Party API",
95
+ "recommended_inter_call_delay_s": 2.0,
96
+ "comparative_peers": ["claude-3-5-sonnet", "gpt-4o", "z-ai/glm-5.3"],
97
+ "methodological_notes": "Frontier Flagship (Tier 1). Symmetrically comparable only to other Frontier models.",
98
+ },
99
+ "google/gemini-2.0-flash-001": {
100
+ "display_name": "Gemini 2.0 Flash",
101
+ "family": "Google Gemini",
102
+ "capability_tier": CapabilityTier.LIGHT_FAST,
103
+ "tier_rank": 3,
104
+ "upstream_provider": "Google (via OpenRouter)",
105
+ "recommended_inter_call_delay_s": 1.0,
106
+ "comparative_peers": ["z-ai/glm-4.7-flash", "anthropic/claude-3.5-haiku"],
107
+ "methodological_notes": "Tier 3 Lightweight fast instruction-follower.",
108
+ },
109
+ "google/gemini-2.5-flash": {
110
+ "display_name": "Gemini 2.5 Flash",
111
+ "family": "Google Gemini",
112
+ "capability_tier": CapabilityTier.MID_BALANCED,
113
+ "tier_rank": 2,
114
+ "upstream_provider": "Google Vertex (via OpenRouter)",
115
+ "recommended_inter_call_delay_s": 0.5,
116
+ "comparative_peers": ["z-ai/glm-4.7", "deepseek-flash", "qwen/qwen-2.5-72b-instruct"],
117
+ "methodological_notes": (
118
+ "Google Vertex Tier 2 Mid-Tier Balanced workhorse model with high throughput and native structured outputs. "
119
+ "Directly comparable to GLM-4.7 and DeepSeek-Flash."
120
+ ),
121
+ },
122
+ "qwen/qwen3-coder-30b-a3b-instruct": {
123
+ "display_name": "Qwen3 Coder 30B",
124
+ "family": "Qwen / Alibaba",
125
+ "capability_tier": CapabilityTier.MID_BALANCED,
126
+ "tier_rank": 2,
127
+ "upstream_provider": "Alibaba (via OpenRouter)",
128
+ "recommended_inter_call_delay_s": 1.0,
129
+ "comparative_peers": ["z-ai/glm-4.7", "meta-llama/llama-3.3-70b-instruct", "deepseek/deepseek-chat"],
130
+ "methodological_notes": "Tier 2 Mid-Tier Balanced coding MoE with 256k context. Primary open-weights distillation baseline.",
131
+ },
132
+ "meta-llama/llama-3.3-70b-instruct": {
133
+ "display_name": "Llama 3.3 70B Instruct",
134
+ "family": "Meta Llama",
135
+ "capability_tier": CapabilityTier.MID_BALANCED,
136
+ "tier_rank": 2,
137
+ "upstream_provider": "Meta (via OpenRouter)",
138
+ "recommended_inter_call_delay_s": 1.0,
139
+ "comparative_peers": ["z-ai/glm-4.7", "qwen/qwen3-coder-30b-a3b-instruct", "deepseek/deepseek-chat"],
140
+ "methodological_notes": "Tier 2 Mid-Tier Balanced dense 70B standard enterprise baseline.",
141
+ },
142
+ "deepseek/deepseek-chat": {
143
+ "display_name": "DeepSeek-V3",
144
+ "family": "DeepSeek",
145
+ "capability_tier": CapabilityTier.MID_BALANCED,
146
+ "tier_rank": 2,
147
+ "upstream_provider": "DeepSeek (via OpenRouter)",
148
+ "recommended_inter_call_delay_s": 1.0,
149
+ "comparative_peers": ["z-ai/glm-4.7", "qwen/qwen3-coder-30b-a3b-instruct", "meta-llama/llama-3.3-70b-instruct"],
150
+ "methodological_notes": "Tier 2 Mid-Tier Balanced MoE (671B total, 37B active). Direct architectural peer to GLM-4.7's 32B active MoE.",
151
+ },
152
+ "z-ai/glm-5.3-flash": {
153
+ "display_name": "GLM-5.3-Flash",
154
+ "family": "Zhipu GLM",
155
+ "capability_tier": CapabilityTier.LIGHT_FAST,
156
+ "tier_rank": 3,
157
+ "upstream_provider": "Zhipu AI (via OpenRouter)",
158
+ "recommended_inter_call_delay_s": 0.5,
159
+ "comparative_peers": ["openai/gpt-4o-mini", "z-ai/glm-4.7-flash"],
160
+ "methodological_notes": "Tier 3 Lightweight Fast high-throughput MoE (320B total, 18B active) with 1.3M context.",
161
+ },
162
+ "openai/gpt-4o-mini": {
163
+ "display_name": "GPT-4o-mini",
164
+ "family": "OpenAI",
165
+ "capability_tier": CapabilityTier.LIGHT_FAST,
166
+ "tier_rank": 3,
167
+ "upstream_provider": "OpenAI (via OpenRouter)",
168
+ "recommended_inter_call_delay_s": 0.5,
169
+ "comparative_peers": ["z-ai/glm-5.3-flash", "google/gemini-2.0-flash-001"],
170
+ "methodological_notes": "Tier 3 Lightweight Fast commercial baseline.",
171
+ },
172
+ "anthropic/claude-haiku-4.5": {
173
+ "display_name": "Claude Haiku 4.5",
174
+ "family": "Anthropic",
175
+ "capability_tier": CapabilityTier.FRONTIER_FLAGSHIP,
176
+ "tier_rank": 1,
177
+ "upstream_provider": "Anthropic (via OpenRouter)",
178
+ "recommended_inter_call_delay_s": 1.5,
179
+ "comparative_peers": ["deepseek-v4-pro", "openai/gpt-4o"],
180
+ "methodological_notes": "Tier 1 Frontier Flagship lightweight anchor. Frontier upper-bound audit model.",
181
+ },
182
+ }
183
+
184
+
185
+ def classify_model(
186
+ model_id: str,
187
+ base_url: str = "https://openrouter.ai/api/v1",
188
+ reasoning_effort: str = "none",
189
+ ) -> ModelClassification:
190
+ """Classify model capability tier, provider class, and peers."""
191
+ if "api.deepseek.com" in base_url:
192
+ provider_class = ProviderHostingClass.DIRECT_FIRST_PARTY
193
+ elif "openrouter.ai" in base_url:
194
+ provider_class = ProviderHostingClass.AGGREGATOR_SHARED
195
+ else:
196
+ provider_class = ProviderHostingClass.DIRECT_FIRST_PARTY
197
+
198
+ reasoning_mode = "disabled" if reasoning_effort in ("none", "", None) else f"enabled({reasoning_effort})"
199
+
200
+ meta = _TAXONOMY.get(model_id)
201
+ if meta:
202
+ return ModelClassification(
203
+ model_id=model_id,
204
+ display_name=meta["display_name"],
205
+ family=meta["family"],
206
+ capability_tier=meta["capability_tier"],
207
+ tier_rank=meta["tier_rank"],
208
+ provider_class=provider_class,
209
+ upstream_provider=meta["upstream_provider"],
210
+ reasoning_mode=reasoning_mode,
211
+ recommended_inter_call_delay_s=meta["recommended_inter_call_delay_s"],
212
+ comparative_peers=list(meta["comparative_peers"]),
213
+ methodological_notes=meta["methodological_notes"],
214
+ )
215
+
216
+ return ModelClassification(
217
+ model_id=model_id,
218
+ display_name=model_id,
219
+ family="Unknown",
220
+ capability_tier=CapabilityTier.MID_BALANCED,
221
+ tier_rank=2,
222
+ provider_class=provider_class,
223
+ upstream_provider="Generic / Aggregator",
224
+ reasoning_mode=reasoning_mode,
225
+ recommended_inter_call_delay_s=3.0,
226
+ comparative_peers=[],
227
+ methodological_notes="Uncataloged model evaluated as generic Tier 2.",
228
+ )
229
+
230
+
231
+ def get_proportional_reasoning_mapping(model_id: str) -> dict[str, str | int]:
232
+ """Return the normative operational reasoning mapping per model class (ADR-0006).
233
+
234
+ Operational intent:
235
+ - BOOTSTRAP_ANALYSIS: Defensive reasoning (untangle injections from tasks)
236
+ - DRAFT_PROMPT / REVISE_PROMPT: Bounded pre-execution reasoning (entity preservation)
237
+ - DRAFT_PLAN / REVISE_PLAN / EXECUTE: Zero reasoning (mechanical translation)
238
+ """
239
+ mid = model_id.lower()
240
+ if "glm-4.7" in mid:
241
+ # Class A: Native effort tiers (Zhipu GLM-4.7)
242
+ # ADR-0006 as amended by the ADR-0009 benchmark (2026-09-18): EXECUTE
243
+ # is now the primary semantic generation step (deliverable + Result IR
244
+ # emission), so it is priced HIGH; DRAFT_EXECUTE (entity-dense brief
245
+ # drafting) is likewise semantic -> HIGH; EMIT_RESULT_IR is mechanical
246
+ # correction against explicit host-side errors -> LOW. Translation ops
247
+ # keep the ratified LOW floor (D25: load-bearing); plan drafting stays
248
+ # NONE (measured: 144-token plans, zero reasoning, no quality loss).
249
+ return {
250
+ "BOOTSTRAP_ANALYSIS": "high",
251
+ "DRAFT_PROMPT": "low",
252
+ "REVISE_PROMPT": "low",
253
+ "DRAFT_PLAN": "none",
254
+ "REVISE_PLAN": "none",
255
+ "DRAFT_EXECUTE": "high",
256
+ "EXECUTE": "high",
257
+ "EMIT_RESULT_IR": "low",
258
+ }
259
+ elif "claude" in mid or "anthropic" in mid:
260
+ # Class B: Explicit thinking budget (Anthropic Claude 3.5/3.7 Sonnet, Haiku 4.5)
261
+ return {
262
+ "BOOTSTRAP_ANALYSIS": 4096,
263
+ "DRAFT_PROMPT": 1024,
264
+ "REVISE_PROMPT": 1024,
265
+ "DRAFT_PLAN": "none",
266
+ "REVISE_PLAN": "none",
267
+ "EXECUTE": "none",
268
+ }
269
+ elif "thinking" in mid or "r1" in mid or "o1" in mid or "o3" in mid or "o4" in mid:
270
+ # Class C / Native thinking open-weights / reasoning
271
+ return {
272
+ "BOOTSTRAP_ANALYSIS": "high",
273
+ "DRAFT_PROMPT": "low",
274
+ "REVISE_PROMPT": "low",
275
+ "DRAFT_PLAN": "none",
276
+ "REVISE_PLAN": "none",
277
+ "EXECUTE": "none",
278
+ }
279
+ else:
280
+ # Class D: Pure instruct / quantized models (Llama 3.3, Qwen Instruct, GPT-4o-mini)
281
+ # Native API reasoning disabled; deliberative compute provided in-band via schema.
282
+ return {
283
+ "BOOTSTRAP_ANALYSIS": "none",
284
+ "DRAFT_PROMPT": "none",
285
+ "REVISE_PROMPT": "none",
286
+ "DRAFT_PLAN": "none",
287
+ "REVISE_PLAN": "none",
288
+ "EXECUTE": "none",
289
+ }
290
+
@@ -0,0 +1,157 @@
1
+ """Normative Store & Protocol Version Resolver (ADR-0008).
2
+
3
+ Formal 4-Tier Precedence Order for Normative Standards:
4
+ 1. Priority 1 (Explicit Environment Override): `PDLT_STANDARDS_PATH` environment variable.
5
+ 2. Priority 2 (Local Repository Override): `<repo_root>/contracts/` (if containing `CONTRACT_MANIFEST.json`).
6
+ 3. Priority 3 (User Normative Store): `~/.pdlt/versions/<version>/contracts/` (ADR-0008 content-addressed store).
7
+ 4. Priority 4 (Bundled Canonical Fallback): `pdl_taskmaster.contracts` package data (via `importlib.resources`).
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import importlib.resources
12
+ import json
13
+ import os
14
+ from pathlib import Path
15
+ import shutil
16
+
17
+
18
+ class NormativeStoreError(RuntimeError):
19
+ pass
20
+
21
+
22
+ class NormativeStore:
23
+ DEFAULT_VERSION = "v2"
24
+
25
+ @classmethod
26
+ def get_default_store_root(cls) -> Path:
27
+ env_root = os.environ.get("PDLT_STORE_ROOT")
28
+ if env_root:
29
+ return Path(env_root).resolve()
30
+ return Path.home() / ".pdlt"
31
+
32
+ @classmethod
33
+ def resolve_version(cls, repo_root: str | Path | None = None) -> str:
34
+ """Resolve pinned protocol version from .pdlt-version or pdlt.json."""
35
+ if repo_root is not None:
36
+ root = Path(repo_root).resolve()
37
+ pin_file = root / ".pdlt-version"
38
+ if pin_file.is_file():
39
+ v = pin_file.read_text(encoding="utf-8").strip()
40
+ if v:
41
+ return v
42
+ cfg_file = root / "pdlt.json"
43
+ if cfg_file.is_file():
44
+ try:
45
+ data = json.loads(cfg_file.read_text(encoding="utf-8"))
46
+ v = data.get("version")
47
+ if isinstance(v, str) and v.strip():
48
+ return v.strip()
49
+ except Exception:
50
+ pass
51
+ return cls.DEFAULT_VERSION
52
+
53
+ @classmethod
54
+ def get_bundled_contracts_path(cls) -> Path | None:
55
+ """Resolve path to package-bundled contracts data directory."""
56
+ try:
57
+ bundled = importlib.resources.files("pdl_taskmaster").joinpath("contracts")
58
+ p = Path(str(bundled))
59
+ if p.is_dir() and (p / "CONTRACT_MANIFEST.json").is_file():
60
+ return p
61
+ except Exception:
62
+ pass
63
+ return None
64
+
65
+ @classmethod
66
+ def resolve_standards_root(
67
+ cls,
68
+ repo_root: str | Path,
69
+ version: str | None = None,
70
+ store_root: str | Path | None = None,
71
+ ) -> Path:
72
+ """Resolve directory containing CONTRACT_MANIFEST.json and standards.
73
+
74
+ Strict 4-Tier Precedence:
75
+ 1. PDLT_STANDARDS_PATH env var
76
+ 2. Local <repo_root>/contracts/ (if present and valid)
77
+ 3. ~/.pdlt/versions/<version>/contracts/ (if seeded)
78
+ 4. Bundled package data in pdl_taskmaster.contracts
79
+ """
80
+ # Tier 1: Explicit environment variable override
81
+ env_standards = os.environ.get("PDLT_STANDARDS_PATH")
82
+ if env_standards:
83
+ p = Path(env_standards).resolve()
84
+ if p.is_dir() and (p / "CONTRACT_MANIFEST.json").is_file():
85
+ return p
86
+
87
+ # Tier 2: Local repository contracts directory
88
+ repo_path = Path(repo_root).resolve()
89
+ local_candidate = repo_path / "contracts"
90
+ if local_candidate.is_dir() and (local_candidate / "CONTRACT_MANIFEST.json").is_file():
91
+ return local_candidate
92
+
93
+ # Tier 3: Centralized ~/.pdlt store
94
+ v = version or cls.resolve_version(repo_path)
95
+ base = Path(store_root).resolve() if store_root else cls.get_default_store_root()
96
+ user_candidate = base / "versions" / v / "contracts"
97
+ if user_candidate.is_dir() and (user_candidate / "CONTRACT_MANIFEST.json").is_file():
98
+ return user_candidate
99
+
100
+ # Tier 4: Bundled package data
101
+ bundled = cls.get_bundled_contracts_path()
102
+ if bundled is not None:
103
+ return bundled
104
+
105
+ return local_candidate
106
+
107
+ @classmethod
108
+ def resolve_contract(
109
+ cls,
110
+ repo_root: str | Path,
111
+ contract_name: str,
112
+ version: str | None = None,
113
+ store_root: str | Path | None = None,
114
+ ) -> Path:
115
+ """Resolve a specific contract file (e.g. EXECUTION_CONTRACT.json)."""
116
+ standards_root = cls.resolve_standards_root(repo_root, version, store_root)
117
+ target = standards_root / contract_name
118
+ if target.is_file():
119
+ return target
120
+ fallback = Path(repo_root).resolve() / "contracts" / contract_name
121
+ return fallback
122
+
123
+ @classmethod
124
+ def seed_directory(cls, target_dir: Path, source_dir: Path | None = None) -> Path:
125
+ """Copy contract files into target directory from local source or bundled data."""
126
+ target_dir = Path(target_dir).resolve()
127
+ target_dir.mkdir(parents=True, exist_ok=True)
128
+
129
+ src = source_dir
130
+ if src is None or not (src / "CONTRACT_MANIFEST.json").is_file():
131
+ src = cls.get_bundled_contracts_path()
132
+ if src is None or not src.is_dir():
133
+ raise NormativeStoreError("cannot seed contracts: no valid local or bundled source found")
134
+
135
+ for item in src.iterdir():
136
+ dest = target_dir / item.name
137
+ if item.is_dir():
138
+ if dest.exists():
139
+ shutil.rmtree(dest)
140
+ shutil.copytree(item, dest)
141
+ else:
142
+ shutil.copy2(item, dest)
143
+ return target_dir
144
+
145
+ @classmethod
146
+ def seed_store(
147
+ cls,
148
+ repo_root: str | Path,
149
+ version: str = "v2",
150
+ store_root: str | Path | None = None,
151
+ ) -> Path:
152
+ """Seed the centralized ~/.pdlt store from the current repository contracts or bundled data."""
153
+ repo_path = Path(repo_root).resolve()
154
+ base = Path(store_root).resolve() if store_root else cls.get_default_store_root()
155
+ target_dir = base / "versions" / version / "contracts"
156
+ local_src = repo_path / "contracts" if (repo_path / "contracts").is_dir() else None
157
+ return cls.seed_directory(target_dir, source_dir=local_src)