@adamchanadam/cer-workflow 0.3.17
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.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/bin/cer-workflow.mjs +292 -0
- package/package.json +56 -0
- package/skills/cer-workflow/SKILL.md +63 -0
- package/skills/cer-workflow/VERSION +1 -0
- package/skills/cer-workflow/agents/openai.yaml +6 -0
- package/skills/cer-workflow/references/core-runtime.md +479 -0
- package/skills/cer-workflow/references/parallel-producers.md +159 -0
- package/skills/cer-workflow/references/roadmap.md +182 -0
- package/skills/cer-workflow/references/uat.md +605 -0
- package/skills/cer-workflow/scripts/validate_cer_skill.py +1887 -0
- package/skills/cer-workflow-en/SKILL.md +71 -0
- package/skills/cer-workflow-en/VERSION +1 -0
- package/skills/cer-workflow-en/agents/openai.yaml +6 -0
- package/skills/cer-workflow-en/references/core-runtime.md +602 -0
- package/skills/cer-workflow-en/references/parallel-producers.md +174 -0
- package/skills/cer-workflow-en/references/roadmap.md +198 -0
- package/skills/cer-workflow-en/references/uat.md +718 -0
- package/skills/cer-workflow-en/scripts/validate_cer_skill.py +1889 -0
|
@@ -0,0 +1,1889 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Validate the installed CER skill package using only the standard library."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import copy
|
|
8
|
+
import re
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path, PurePosixPath
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
EXPECTED_FILES = {
|
|
14
|
+
"SKILL.md",
|
|
15
|
+
"VERSION",
|
|
16
|
+
"agents/openai.yaml",
|
|
17
|
+
"references/core-runtime.md",
|
|
18
|
+
"references/roadmap.md",
|
|
19
|
+
"references/uat.md",
|
|
20
|
+
"references/parallel-producers.md",
|
|
21
|
+
"scripts/validate_cer_skill.py",
|
|
22
|
+
}
|
|
23
|
+
TEXT_FILES = EXPECTED_FILES - {"VERSION"}
|
|
24
|
+
SEMVER_RE = re.compile(r"(?<![0-9])\d+\.\d+\.\d+(?![0-9])")
|
|
25
|
+
OWNER_MARKER = "<!-- cer-parallel-producers-owner -->"
|
|
26
|
+
UNEXPECTED_FAILURE_OWNER_MARKER = "<!-- cer-unexpected-failure-gate-owner -->"
|
|
27
|
+
TRUTH_SOURCE_INTAKE_OWNER_MARKER = "<!-- cer-truth-source-intake-gate-owner -->"
|
|
28
|
+
DRIFT_CHECKPOINT_OWNER_MARKER = "<!-- cer-controller-drift-checkpoint-owner -->"
|
|
29
|
+
RESULT_DISPOSITION_OWNER_MARKER = "<!-- cer-result-disposition-gate-owner -->"
|
|
30
|
+
EXECUTION_PROFILE_OWNER_MARKER = "<!-- cer-execution-profile-gate-owner -->"
|
|
31
|
+
PUBLIC_RUNTIME_LANGUAGE_OWNER_MARKER = "<!-- cer-public-runtime-language-boundary-owner -->"
|
|
32
|
+
EXPECTED_DEFAULT_PROMPT = (
|
|
33
|
+
"Use $cer-workflow-en with one writer for this work; create a fresh Reviewer in "
|
|
34
|
+
"proportion to risk, and accelerate internally when useful without extra setup."
|
|
35
|
+
)
|
|
36
|
+
FORMAL_COMMANDS = {
|
|
37
|
+
"/CER-auto",
|
|
38
|
+
"/CER-start",
|
|
39
|
+
"/CER-stop",
|
|
40
|
+
"/CER-close",
|
|
41
|
+
"/CER-status",
|
|
42
|
+
"/CER-help",
|
|
43
|
+
}
|
|
44
|
+
MAX_ROUTER_BYTES = 6500
|
|
45
|
+
|
|
46
|
+
EN_TRIGGER_MATRIX_EXPECTATIONS = {
|
|
47
|
+
"frontmatter": (
|
|
48
|
+
"Use only for explicit CER-qualified commands or equivalent meaning",
|
|
49
|
+
"/CER-auto",
|
|
50
|
+
"Plain start/work or close/finish messages are not CER triggers",
|
|
51
|
+
),
|
|
52
|
+
"auto_row": ("no C exists before the route decision", "CER Workflow enters full C/E/R, with R only when risk requires it", "Remote is unsupported in this first version"),
|
|
53
|
+
"start_row": ("Plain start/work messages do not start CER",),
|
|
54
|
+
"close_row": ("Plain close/finish messages do not close CER",),
|
|
55
|
+
"auto_help_template": (
|
|
56
|
+
"Show this table and the `/CER-auto` task shape",
|
|
57
|
+
"goal + constraints/do-not-do + acceptance + authority/source/authorization boundary",
|
|
58
|
+
"adapt it to the user's context rather than a fixed domain",
|
|
59
|
+
"before a formal decision, payment, publication, or external commitment, stop and use CER Workflow",
|
|
60
|
+
),
|
|
61
|
+
"startup_owner": ("Plain start/work messages belong to the target workspace's existing governance and are not CER triggers",),
|
|
62
|
+
"stop_owner": ("Plain close/finish messages belong to the target workspace's existing governance and do not map to CER stop or close",),
|
|
63
|
+
"uat_install_start": (
|
|
64
|
+
"`/CER-start` and `Start CER` trigger CER",
|
|
65
|
+
"a plain start/work message does not",
|
|
66
|
+
),
|
|
67
|
+
"uat_install_auto": (
|
|
68
|
+
"`/CER-auto` and `Run CER adaptively` trigger the local execution profile gate",
|
|
69
|
+
"no C exists before the route decision",
|
|
70
|
+
),
|
|
71
|
+
"uat_install_close": (
|
|
72
|
+
"`/CER-close` and `Close CER` trigger CER close",
|
|
73
|
+
"a plain close/finish message does not close CER and does not map to `/CER-stop`",
|
|
74
|
+
),
|
|
75
|
+
"uat_failure": ("A plain start/work message starts CER, or a plain close/finish message triggers CER close/stop",),
|
|
76
|
+
"uat_failure_auto": ("`/CER-auto` claims C before the route decision",),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
EXECUTION_PROFILE_REQUIREMENTS = {
|
|
80
|
+
"sole_owner": "This section is the sole runtime owner for `/CER-auto`",
|
|
81
|
+
"local_only": "The first version supports only a local user task. Remote `/CER-auto` is unsupported",
|
|
82
|
+
"pre_identity": "The entry task is not C before the route decision",
|
|
83
|
+
"start_unchanged": "Explicit `/CER-start` keeps its existing meaning",
|
|
84
|
+
"selective_read": "first read only this section plus the user request and target-project sources needed for the decision",
|
|
85
|
+
"single_read_bundle": "obtain them in one bounded read instead of adding a selector-only read roundtrip",
|
|
86
|
+
"minimum_strength": "Select the minimum sufficient collaboration strength between ordinary execution, Goal, CER Workflow, or blocked",
|
|
87
|
+
"route_lines": "Route: CER Workflow — <why CER is needed and where to stop>",
|
|
88
|
+
"blocked_route_line": "Route: blocked — <missing authority/safety/acceptance condition>",
|
|
89
|
+
"ordinary_boundary": "Ordinary execution does not start CER, claim C/E/R identity, show a bear card, or load other CER references",
|
|
90
|
+
"goal_boundary": "Goal does not provide CER's sole writer, C/E/R identity, or authority owner",
|
|
91
|
+
"cer_boundary": "Only after selecting CER Workflow, and only at the point that needs CER, does the task read this file and `roadmap.md` in full",
|
|
92
|
+
"decision_basis": "Judge the route by the next step's consequence, uncertainty, reversibility, and owner clarity",
|
|
93
|
+
"source_evidence_boundary": "source count, schema, hash, or receipt cannot replace authority evidence",
|
|
94
|
+
"goal_route": "the endpoint, verification loop, stop condition, and known authority sources are clear",
|
|
95
|
+
"cer_gated_promotion": "formal data, model input, a report paragraph, a decision gate, handoff truth, a release/readiness claim, a public/external claim",
|
|
96
|
+
"blocked_boundary": "Goal capability with no safe fallback",
|
|
97
|
+
"bounded_reconciliation": "touching persistent state does not by itself select CER Workflow",
|
|
98
|
+
"cost_boundary": "Cost never bypasses safety, authority, persistence, external authorization, the Reviewer owner, or the target release owner",
|
|
99
|
+
"recheck_boundaries": "Recheck only at four material boundaries: a user-requirement, authority, or consequence change; a phase boundary; a result disposition that changes carry-forward, progress, or authority effect; and immediately before an external, public, irreversible, or other high-consequence operation",
|
|
100
|
+
"no_step_recheck": "Do not recheck every small step; token pressure by itself is not an upgrade or downgrade reason",
|
|
101
|
+
"existing_owners": "Existing Reviewer ownership still decides whether R is required, and the target project's existing release owner still decides release assurance",
|
|
102
|
+
"safe_step_down": "there is no active batch, E1 has stopped writing, results have been read back and dispositioned, required persistence has been written and read back, and no truth conflict remains",
|
|
103
|
+
"not_stop_close": "This is a route transition, not `/CER-stop` or `/CER-close`",
|
|
104
|
+
"safe_step_up": "Its drafts, diagnostics, Goal outputs, and ordinary-subagent outputs default to working material",
|
|
105
|
+
"baseline_readback": "E1 rereads the workspace baseline before its first write",
|
|
106
|
+
"conditional_checkpoint": "Persist one short, non-authoritative route-transition checkpoint only when a transition crosses a task, session, or context, or carries a material artifact, adjudication, or risk",
|
|
107
|
+
"no_new_structure": "Do not create a new file, schema, YAML object, or registry",
|
|
108
|
+
"checkpoint_block": "Missing or contradictory required readback keeps the next write or dispatch blocked",
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
EXECUTION_PROFILE_UAT_REQUIREMENTS = {
|
|
112
|
+
"ordinary_route": "clear authority, one writer, reversible changes, no external side effect, and sufficient existing acceptance",
|
|
113
|
+
"single_read_bundle": "obtain them in one bounded read with no selector-only read roundtrip",
|
|
114
|
+
"ordinary_subagent": "that subagent receives no formal E/R identity, ready/result lifecycle, or Reviewer effect",
|
|
115
|
+
"bounded_reconciliation": "only local, reversible metadata reconciliation by one writer remains in the same workspace",
|
|
116
|
+
"bounded_reconciliation_limit": "An unresolved truth conflict must not be relabeled as a mechanical correction to step down",
|
|
117
|
+
"goal_route": "endpoint, verification loop, stop condition, and known authority sources are clear",
|
|
118
|
+
"goal_no_promotion": "does not yet accept the result as formal data, model input, a report paragraph, a decision gate, handoff truth, a release/readiness claim, or a public/external claim",
|
|
119
|
+
"goal_vague": "instead of entering Goal directly",
|
|
120
|
+
"cer_route": "output one `Route: CER Workflow — <why CER is needed and where to stop>` line only at that point that needs CER",
|
|
121
|
+
"blocked_route": "output one `Route: blocked — <missing authority/safety/acceptance condition>` line",
|
|
122
|
+
"small_high_consequence": "A one-line task involving deletion, release, official acceptance, or a high-consequence decision selects CER Workflow or blocks",
|
|
123
|
+
"false_evidence": "source count, schema, hash, or receipt as authority evidence",
|
|
124
|
+
"no_09_runtime": "Citing `CER_docs/09` as runtime routing authority",
|
|
125
|
+
"mixed_promotion": "only the later point that needs CER selects CER Workflow",
|
|
126
|
+
"goal_unavailable_fallback": "If Goal is unavailable but bounded ordinary execution can safely finish, do not automatically block",
|
|
127
|
+
"external_background": "If an external claim is only background context and not a formal claim, do not automatically select CER Workflow",
|
|
128
|
+
"start_unchanged": "Explicit `/CER-start` is never adaptively downgraded",
|
|
129
|
+
"remote_unsupported": "Remote `/CER-auto` must stop as unsupported in the first version",
|
|
130
|
+
"bounded_recheck": "Ordinary small steps and token pressure do not trigger a recheck",
|
|
131
|
+
"owner_boundary": "The adaptive route decision cannot force, skip, or replace either owner",
|
|
132
|
+
"safe_step_down": "there is no active batch, E1 has stopped writing, results and result disposition are read back, required persistence is read back, and no truth conflict remains",
|
|
133
|
+
"safe_step_up": "Ordinary drafts, diagnostics, Goal output, and subagent output remain working material",
|
|
134
|
+
"startup_order": "before a valid zero-write E1 `ready` is direct-pushed and read back, it shows no successful startup card and dispatches no formal batch",
|
|
135
|
+
"conditional_checkpoint": "A transition in the same task with no material artifact, adjudication, or risk carry-forward creates no checkpoint",
|
|
136
|
+
"checkpoint_block": "Missing or conflicting required readback keeps the next write or dispatch blocked",
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
EXECUTION_PROFILE_FORBIDDEN = {
|
|
140
|
+
"start_downgrade": "`/CER-start` may automatically downgrade to ordinary execution",
|
|
141
|
+
"pre_identity": "`/CER-auto` is C before the route decision",
|
|
142
|
+
"remote_supported": "Remote `/CER-auto` is supported",
|
|
143
|
+
"file_count": "Many files always require CER Workflow",
|
|
144
|
+
"token_bypass": "Saving tokens may bypass safety or an authority owner",
|
|
145
|
+
"fixed_reviewer": "`/CER-auto` always creates a Reviewer",
|
|
146
|
+
"unsafe_step_down": "An active batch may step down to ordinary execution",
|
|
147
|
+
"draft_authority": "An ordinary draft automatically becomes authoritative_input",
|
|
148
|
+
"goal_authority": "Goal automatically becomes the CER authority owner",
|
|
149
|
+
"false_evidence": "Source count, schema, hash, or receipt is enough to prove authority",
|
|
150
|
+
"cite_09_runtime": "`CER_docs/09` may be `/CER-auto` runtime routing authority",
|
|
151
|
+
"whole_phase_cer": "A later point that needs CER means the whole task must be CER",
|
|
152
|
+
"goal_unavailable_block": "Goal unavailable means blocked even when bounded ordinary execution can safely finish",
|
|
153
|
+
"background_claim_gate": "An external claim used only as background must still select CER Workflow",
|
|
154
|
+
"fixed_checkpoint": "Every route transition creates a fixed YAML checkpoint",
|
|
155
|
+
"persistent_file_always_cer": "Every persistent-state file creates C/E/R",
|
|
156
|
+
"unsafe_read_bundle": "Even different permissions or scope must be combined into one read",
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
PUBLIC_RUNTIME_LANGUAGE_REQUIREMENTS = {
|
|
160
|
+
"boundary_id": "`PUBLIC_SKILL_BOUNDARY_V1`",
|
|
161
|
+
"canonical_source": "English `cer-workflow-en` package as the canonical source",
|
|
162
|
+
"compatibility_mirror": "compatibility entry surface and user-language mirror",
|
|
163
|
+
"no_divergent_behavior": "must not define or override CER behavior",
|
|
164
|
+
"not_english_only": "An English canonical runtime is not English-only operation",
|
|
165
|
+
"stable_commands": "`/CER-auto`, `/CER-start`, `/CER-stop`, `/CER-close`, `/CER-status`, and `/CER-help`",
|
|
166
|
+
"chinese_triggers": "Chinese natural-language triggers must still work",
|
|
167
|
+
"reply_language": "user-facing replies follow the user's or target project's language",
|
|
168
|
+
"readme_boundary": "`README.md` / `README.en.md` are only user presentation and installation surfaces",
|
|
169
|
+
"release_notes_order": "Traditional Chinese first, then English",
|
|
170
|
+
"maintainer_qa": "maintainer release-QA, not an ordinary runtime step for ordinary execution, Goal, CER Workflow, or `/CER-help`",
|
|
171
|
+
"external_auth": "separate authorization, migration acceptance, and readback",
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
PUBLIC_RUNTIME_LANGUAGE_UAT_REQUIREMENTS = {
|
|
175
|
+
"no_divergent_zh": "must not define or override CER behavior",
|
|
176
|
+
"chinese_triggers": "Chinese `/CER-auto`, `/CER-start`, `CER 自適應`, `CER 啟動`, and equivalent triggers still work",
|
|
177
|
+
"reply_language": "user-facing replies follow Chinese or the target project's language",
|
|
178
|
+
"release_notes_order": "Release Notes keep Traditional Chinese first and English second",
|
|
179
|
+
"readme_not_owner": "Neither surface can override the `core-runtime.md` runtime owner",
|
|
180
|
+
"maintainer_qa": "maintainer release-QA only",
|
|
181
|
+
"not_user_step": "must not list them as ordinary user runtime steps",
|
|
182
|
+
"not_retired_by_source_only": "A source-only rule change must not claim the retirement is complete",
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
PUBLIC_RUNTIME_LANGUAGE_FORBIDDEN = {
|
|
186
|
+
"english_reply_default": "English canonical runtime means Chinese input defaults to English",
|
|
187
|
+
"readme_owner": "README is the runtime owner",
|
|
188
|
+
"full_audit_user_step": "Full Audit is an ordinary user runtime step",
|
|
189
|
+
"delete_zh_without_validation": "The Traditional Chinese package may be deleted without validation",
|
|
190
|
+
"zh_diverges": "The Traditional Chinese package may define separate CER behavior",
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
REVIEWER_PROPORTIONALITY_COUNTEREXAMPLES = {
|
|
194
|
+
"simple_fixed_fresh_reviewer": "Every simple task always creates a fresh Reviewer",
|
|
195
|
+
"every_simple_task_reviewer": "A Reviewer is assigned by default to every simple task",
|
|
196
|
+
"simple_task_fixed_r": "Simple work always gets R",
|
|
197
|
+
"reverse_low_risk_fresh_reviewer": "A fresh Reviewer is used by default for all low-risk tasks",
|
|
198
|
+
"passive_simple_task_reviewer": "An independent reviewer is always assigned to simple work",
|
|
199
|
+
"low_risk_independent_reviewer": "Low-risk tasks always receive an independent reviewer",
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
OWNER_REQUIREMENTS = {
|
|
203
|
+
"formal_roles": "CER has only the formal roles C, E1, R, and E2",
|
|
204
|
+
"not_fifth_role": "not a fifth role",
|
|
205
|
+
"no_new_lifecycle": "receives no formal title, cycle, ready, result, batch lifecycle, or Reviewer identity",
|
|
206
|
+
"no_new_commands": "adds no slash command",
|
|
207
|
+
"two_independent_lanes": "At least two work lanes are independent and need no result from each other, shared mutable state, or fixed execution order",
|
|
208
|
+
"frozen_input_version": "Each lane's input and source identity is frozen",
|
|
209
|
+
"concurrent_controller_work": "C has non-duplicative critical analysis, gating, or adjudication work to do concurrently",
|
|
210
|
+
"independently_verifiable_candidates": "C can independently verify each candidate against authoritative sources",
|
|
211
|
+
"material_time_saving": "Expected net time savings materially exceed startup, readback, hashing, deduplication, and adjudication costs",
|
|
212
|
+
"available_execution_slots": "Required parallel execution slots are available without reducing capacity needed by formal E1 or a fresh R",
|
|
213
|
+
"read_only": "`read_only`",
|
|
214
|
+
"isolated_artifact": "`isolated_artifact`",
|
|
215
|
+
"read_only_zero_write": "zero writes everywhere",
|
|
216
|
+
"project_noncontainment": "`scratch_root` and the target project do not contain each other",
|
|
217
|
+
"dangerous_roots": "not a drive root, user root, system root",
|
|
218
|
+
"link_boundary": "symlink, junction, Windows reparse point, mount",
|
|
219
|
+
"lane_nonoverlap": "Lane roots are distinct, are not ancestors of one another",
|
|
220
|
+
"actual_tool_permission_boundary": "Actual tool permissions allow only that lane's explicit root. Relative paths, wildcards, environment fallback, or a producer-selected location cannot expand the boundary",
|
|
221
|
+
"lane_contract_label": "`lane_label`",
|
|
222
|
+
"lane_contract_goal": "One objective",
|
|
223
|
+
"lane_contract_input": "Input identity and version, source identity, and verifiable source coordinates",
|
|
224
|
+
"lane_contract_scope": "Allowed and forbidden scope",
|
|
225
|
+
"lane_contract_output": "Expected candidate output",
|
|
226
|
+
"lane_contract_acceptance": "Acceptance method",
|
|
227
|
+
"lane_contract_stop": "Stop condition",
|
|
228
|
+
"scratch_root": "`scratch_root`",
|
|
229
|
+
"candidate_claims": "`claims`",
|
|
230
|
+
"candidate_unknowns": "`unknowns`",
|
|
231
|
+
"verifiable_source_coordinates": "Actual source coordinates",
|
|
232
|
+
"candidate_hash": "actual absolute path and SHA-256",
|
|
233
|
+
"controller_readback": "C personally reads back",
|
|
234
|
+
"rehash": "recomputes SHA-256",
|
|
235
|
+
"no_vote": "must not accept by vote",
|
|
236
|
+
"merged_batch": "E1 receives only that C-converged batch",
|
|
237
|
+
"no_direct_e1": "must not use raw producer communication",
|
|
238
|
+
"no_wait_poll": "C does not wait, poll, or background-monitor producers",
|
|
239
|
+
"late": "A late candidate",
|
|
240
|
+
"input_drift": "Input or source drift",
|
|
241
|
+
"tamper": "hash drift, tamper",
|
|
242
|
+
"out_of_bounds": "out-of-bounds",
|
|
243
|
+
"producer_failure": "producer creation fails",
|
|
244
|
+
"stop_close": "`/CER-stop` and `/CER-close` do not wait for producers",
|
|
245
|
+
"serial_fallback": "`producer_count=0`",
|
|
246
|
+
"user_simplicity": "The user does not configure producers",
|
|
247
|
+
"material_only_report": "Report only results, unknowns, blockers, or risks that materially affect the user",
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
UAT_REQUIREMENTS = {
|
|
251
|
+
"default_prompt_risk_proportionate": "create a fresh Reviewer in proportion to risk",
|
|
252
|
+
"default_prompt_simple_task": "does not force a Reviewer for simple work",
|
|
253
|
+
"normal_two_lanes": "two lanes are independent",
|
|
254
|
+
"auto_idle": "`producer_count=0`",
|
|
255
|
+
"no_subagent": "no subagent capability",
|
|
256
|
+
"cost_fallback": "uneconomic parallel cost",
|
|
257
|
+
"read_only_write": "A `read_only` lane that attempts any write",
|
|
258
|
+
"root_boundary": "inside the project or one of its ancestors, at a drive root, user root, system root",
|
|
259
|
+
"link_boundary": "symlink, junction, reparse point, mount",
|
|
260
|
+
"lane_overlap": "equal to or ancestral to another lane",
|
|
261
|
+
"partial_drift": "discard only the dependent candidate",
|
|
262
|
+
"source_conflict": "not by vote",
|
|
263
|
+
"late_candidate": "A late candidate",
|
|
264
|
+
"producer_failure": "producer failure",
|
|
265
|
+
"artifact_tamper": "artifact hash tamper",
|
|
266
|
+
"role_impersonation": "producer impersonating E/R",
|
|
267
|
+
"direct_to_e1": "sending directly to E1",
|
|
268
|
+
"unmerged_scratch": "E1 using unconverged scratch",
|
|
269
|
+
"project_write": "C/R/producer writing the target project",
|
|
270
|
+
"stop_close": "`/CER-stop` or `/CER-close` does not wait",
|
|
271
|
+
"no_lifecycle_identity": "no formal title, cycle, ready, result, slash, lock, registry, or run id",
|
|
272
|
+
"roadmap_boundary": "Roadmap role columns and lifecycle cards still contain formal roles only",
|
|
273
|
+
"auto_wait_threads_forbidden": "automatically uses `wait_threads` or `read_thread` after dispatch as the receiving mechanism",
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
UNEXPECTED_FAILURE_REQUIREMENTS = {
|
|
277
|
+
"test_not_authority": "tests produce evidence but do not grant more modification authority",
|
|
278
|
+
"allowlist_not_semantics": "does not authorize E1 to change another owner, authoritative source, or protected meaning inside that file",
|
|
279
|
+
"gate_off": "The gate stays inactive",
|
|
280
|
+
"caused": "E1 may repair it in the current batch",
|
|
281
|
+
"preexisting": "report it without repairing it",
|
|
282
|
+
"unknown_or_boundary": "stop further writes",
|
|
283
|
+
"regression_boundary": "does not expand E1's repair authority",
|
|
284
|
+
"controller_only": "Only C may refreeze the contract and expand scope by dispatching a new batch with a new `batchId` and `payloadDigest`",
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
DELIVERY_REQUIREMENTS = {
|
|
288
|
+
"post_dispatch_parked": "After dispatch, task creation, or send, C immediately enters `POST_DISPATCH_PARKED`",
|
|
289
|
+
"no_auto_wait": "must not automatically use `wait_threads`, `read_thread`, or a platform-equivalent tool to wait, wake itself, track progress, read commentary, read finals, probe status, or discover results",
|
|
290
|
+
"no_auto_progress_read": "read finals, probe status, or discover results",
|
|
291
|
+
"read_exceptions": "has only two read exceptions: a one-time thread check explicitly requested by the user in the same turn, or one bounded readback for verification/adjudication after C has received a direct-push",
|
|
292
|
+
"no_push_no_progress": "Without a direct-push, a wait snapshot, completion state, commentary, summary, child final, task title, user relay, or passive read cannot advance `pending` / `delivery_incomplete`",
|
|
293
|
+
"no_push_no_advance": "cannot advance `pending` / `delivery_incomplete`",
|
|
294
|
+
"no_automatic_waiting": "forbids automatic waiting, repeated waiting, polling, background listening",
|
|
295
|
+
"delivery_state_values": "`delivery_state` using only `confirmed_delivered`, `not_delivered`, or `delivery_unknown`",
|
|
296
|
+
"confirmed_delivered_evidence": "`confirmed_delivered` requires target direct-push ack",
|
|
297
|
+
"send_success_not_delivery": "Send success, title changes, thread id existence, or the sender saying it sent the message are insufficient",
|
|
298
|
+
"delivery_unknown_retry": "`delivery_unknown` may use only this section's one bounded readback and same-`messageId` controlled resend",
|
|
299
|
+
"before_next_batch_delivery": "Before dispatching the next batch, the prior batch must have necessary control messages at `confirmed_delivered`",
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
DELIVERY_UAT_REQUIREMENTS = {
|
|
303
|
+
"post_dispatch_parked_uat": "after dispatch it stays `POST_DISPATCH_PARKED`",
|
|
304
|
+
"bounded_wakeup_wrapper_bad": "wraps waiting as a bounded wakeup",
|
|
305
|
+
"no_push_next_batch_bad": "advances state or dispatches the next batch without direct-push",
|
|
306
|
+
"send_success_no_target": "send tool returns success but the target has no direct-push ack",
|
|
307
|
+
"wrong_batch_digest": "target acknowledges the wrong `batchId`/`payloadDigest`",
|
|
308
|
+
"delivery_unknown_bad": "`delivery_unknown` is still treated as received after one bounded check / controlled resend",
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
TRUTH_SOURCE_INTAKE_REQUIREMENTS = {
|
|
312
|
+
"sole_owner": "The truth-source intake gate is the sole owner inside Controller preflight",
|
|
313
|
+
"four_questions": "who owns it; who actually uses it; how it takes effect; and what counterexample can disprove it",
|
|
314
|
+
"owner_definition": "`Who owns it` means the source anchor in a user decision, project source of truth, rule, file, or external authority",
|
|
315
|
+
"consumer_definition": "`Who actually uses it` means how E1, R, the deliverable, install surface, public surface, later batch, or user flow consumes that condition",
|
|
316
|
+
"effect_definition": "`How it takes effect` means how it changes this batch's dispatch, deliverable content, permissions, acceptance, or outcome judgment",
|
|
317
|
+
"disproof_definition": "`What counterexample can disprove it` means the readback, test, Reviewer question, or counterexample that would make this batch unable to count as successful",
|
|
318
|
+
"missing_is_critical": "If any item cannot be answered, or if the answer depends on a required source C has not read, the condition is `critical missing`",
|
|
319
|
+
"no_dispatch": "C must not dispatch a formal implementation batch and may only perform necessary read-only diagnosis, narrow the acceptance scope, or use a `🟡 User decision` stop",
|
|
320
|
+
"not_full_audit": "Do not expand this gate into default full-text ingestion, whole-repo review, or fixed Full Audit",
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
TRUTH_SOURCE_INTAKE_UAT_REQUIREMENTS = {
|
|
324
|
+
"four_questions_pass": "Before a non-simple formal implementation batch, C can answer each truth-source intake question",
|
|
325
|
+
"missing_blocks": "If C cannot answer any truth-source intake question",
|
|
326
|
+
"missing_still_dispatches": "A non-simple formal implementation batch has not answered who owns it, who actually uses it, how it takes effect, and what counterexample can disprove it, but C still creates/reuses E1 or dispatches the implementation batch",
|
|
327
|
+
"overwide_gate": "C expands the truth-source intake gate into default full-text ingestion, whole-repo review, fixed Full Audit, a second rule owner, or a fixed form workflow",
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
CONTROLLER_CHALLENGE_UAT_REQUIREMENTS = {
|
|
331
|
+
"section": "## Controller Long-Task Challenge Scenarios",
|
|
332
|
+
"measurable_endpoint": "lacks a measurable or readable endpoint",
|
|
333
|
+
"authority_boundary": "required authority, allowed boundaries, or counterexample evidence is insufficient",
|
|
334
|
+
"adjacent_mainline": "plausible adjacent request, process improvement, or substitute deliverable",
|
|
335
|
+
"defensive_expansion": "is not a reason for defensive expansion",
|
|
336
|
+
"changed_contract": "cannot retain its old acceptance identity",
|
|
337
|
+
"no_thrashing": "must not cause ordinary/CER route thrashing",
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
TRUTH_SOURCE_INTAKE_FORBIDDEN = {
|
|
341
|
+
"missing_four_questions_dispatch": "when who owns it, who actually uses it, how it takes effect, and what counterexample can disprove it are unanswered, C may still dispatch a formal implementation batch",
|
|
342
|
+
"full_ingestion_required": "the truth-source intake gate requires default full-text ingestion, whole-repo review, or fixed Full Audit",
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
UNEXPECTED_FAILURE_UAT_MARKERS = {
|
|
346
|
+
"gate_off": "<!-- cer-uat-unexpected-failure:gate-off -->",
|
|
347
|
+
"caused": "<!-- cer-uat-unexpected-failure:caused -->",
|
|
348
|
+
"preexisting": "<!-- cer-uat-unexpected-failure:preexisting -->",
|
|
349
|
+
"unknown": "<!-- cer-uat-unexpected-failure:unknown -->",
|
|
350
|
+
"semantic_boundary": "<!-- cer-uat-unexpected-failure:semantic-boundary -->",
|
|
351
|
+
"acceptance_boundary": "<!-- cer-uat-unexpected-failure:acceptance-boundary -->",
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
UNEXPECTED_FAILURE_FORBIDDEN = {
|
|
355
|
+
"test_grants_authority": "A test failure grants E1 more modification authority",
|
|
356
|
+
"allowlist_grants_semantics": "A file in the allowlist authorizes E1 to change every meaning in that file",
|
|
357
|
+
"missing_baseline_guess": "Without a baseline, E1 should guess and repair",
|
|
358
|
+
"executor_expands_scope": "E1 may expand scope without C refreezing a new batch",
|
|
359
|
+
"controller_expands_without_new_identity": "C may expand scope while reusing the old `batchId` and `payloadDigest`",
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
SENDABLE_PACKET_REQUIREMENTS = {
|
|
363
|
+
"draft_sendable_split": "`draft_packet`",
|
|
364
|
+
"no_placeholders": "`sendable_packet` must not retain `<...>` placeholders",
|
|
365
|
+
"truth_intake_summary": "summary of the truth-source intake four questions passed in Controller preflight: who owns it, who actually uses it, how it takes effect, and what counterexample can disprove it",
|
|
366
|
+
"create_prompt_handshake_only": "The initial `create_thread` prompt for a new E1/R is not a formal batch",
|
|
367
|
+
"internal_return_channel": "The formal direct-push return channel is CER internal communication, not a prohibited project/source-root write or external side effect",
|
|
368
|
+
"create_prompt_no_full_payload": "Do not put the complete source corpus, candidate work content, or formal batch payload in the create prompt",
|
|
369
|
+
"large_payload_once": "C sends it exactly once in the formal `sendable_packet`",
|
|
370
|
+
"large_payload_split": "inputs that are too long or cross risk boundaries are split into multiple formal batches by semantic/risk unit",
|
|
371
|
+
"pre_dispatch_evidence": "A `sendable_packet` for long-running, multi-batch, high-risk, or non-simple formal implementation work must include a compact `pre_dispatch_evidence` block",
|
|
372
|
+
"pre_dispatch_not_new_owner": "It is not a new source of truth, fixed form, background monitor, or Full Audit",
|
|
373
|
+
"pre_dispatch_fields": "It includes at least: an `outcome_anchor` pointer or summary; the unfinished condition this batch improves and the readable outcome difference success should create; the truth-source intake four-question summary with source anchors; required sources read and the disposition of remaining unknowns; work-lane classification; and, when a drift checkpoint trigger exists, the checkpoint conclusion, or why no trigger applies",
|
|
374
|
+
"pre_dispatch_missing_blocks": "If it is missing, contradictory, depends on unread required sources, or merely says judgment was done without readable support, the packet is not sendable",
|
|
375
|
+
"pre_dispatch_assignee_blocks": "If E1/R receives a formal batch without required `pre_dispatch_evidence`, it must direct-push a zero-write blocker such as `BATCH_BLOCKED_MISSING_PRE_DISPATCH_EVIDENCE` and stop",
|
|
376
|
+
"concrete_bindings": "A real dispatch must fill actual `threadId` or platform-equivalent coordinate, `returnTarget`, `messageId`, `batchId`, `batchSeq`, `payloadDigest`, and any routing coordinate explicitly required by the active tool schema/receipt",
|
|
377
|
+
"sessionid_not_threadid": "sessionId is not a substitute for threadId as a formal dispatch coordinate",
|
|
378
|
+
"hostid_not_hard_required": "hostId is used only when the active tool schema or receipt requires or provides it",
|
|
379
|
+
"no_hostid_inference": "do not derive hostId from `local`, title, sessionId, threadId shape, or an error message",
|
|
380
|
+
"relative_identity_draft_only": "Relative wording such as `same E1`, `the E1 above`, or `next sequence` is draft-only",
|
|
381
|
+
"review_manifest": "R dispatch must fill actual `candidateIdentity`, `candidateManifest`, and candidate delivery evidence",
|
|
382
|
+
"missing_blocks": "Missing any one of these leaves the packet at `dispatch_blocked` or `decision_blocked`",
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
SENDABLE_PACKET_UAT_REQUIREMENTS = {
|
|
386
|
+
"placeholder_self_pass": "A formal `sendable_packet` still contains `<...>` placeholders",
|
|
387
|
+
"create_prompt_payload": "A new E1/R create prompt contains the complete source corpus, candidate work content, or formal",
|
|
388
|
+
"internal_return_channel_forbidden": "A dispatch packet both requires direct-push and treats the formal direct-push return channel as a prohibited external side effect",
|
|
389
|
+
"double_large_payload": "The same complete large input is sent in both the create prompt and formal `sendable_packet`",
|
|
390
|
+
"relative_identity": "A formal dispatch uses relative wording such as `same E1`, `the E1 above`, or `next sequence`",
|
|
391
|
+
"hostid_hard_required": "Controller still hard-requires `hostId`",
|
|
392
|
+
"hostid_inferred": "derives hostId from `local`, title, sessionId, threadId shape, or an error message",
|
|
393
|
+
"sessionid_replaces_threadid": "A formal dispatch uses sessionId instead of threadId as the formal dispatch coordinate",
|
|
394
|
+
"review_manifest_missing": "R dispatch lacks actual `candidateIdentity`, `candidateManifest`, or candidate delivery evidence",
|
|
395
|
+
"pre_dispatch_missing": "packet lacks `pre_dispatch_evidence`",
|
|
396
|
+
"pre_dispatch_claim_only": "only says \"C already judged\" without readable support",
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
SENDABLE_PACKET_FORBIDDEN = {
|
|
400
|
+
"placeholder_allowed": "A sendable dispatch may retain `<...>` placeholders",
|
|
401
|
+
"create_prompt_full_payload": "The create prompt may contain the complete source corpus or formal batch payload",
|
|
402
|
+
"double_send_large_payload": "C may send the same complete large input in both the create prompt and formal `sendable_packet`",
|
|
403
|
+
"relative_identity_allowed": "`same E1`, `the E1 above`, or `next sequence` may be used as formal dispatch identity",
|
|
404
|
+
"hostid_always_required": "Every real dispatch must include `hostId` even when the active tool schema requires only `threadId`",
|
|
405
|
+
"sessionid_infers_hostid": "hostId may be derived from sessionId, title, `local`, or an error message before continuing",
|
|
406
|
+
"sessionid_replaces_threadid": "sessionId may replace threadId as formal dispatch coordinate",
|
|
407
|
+
"review_manifest_optional": "R dispatch may omit `candidateManifest`",
|
|
408
|
+
"draft_pass": "`draft_packet` may self-rate as sendable",
|
|
409
|
+
"pre_dispatch_optional": "Long-running, multi-batch, high-risk, or non-simple formal implementation work does not need `pre_dispatch_evidence`",
|
|
410
|
+
"assignee_fills_missing_pre_dispatch": "E1/R may fill in C's missing pre-dispatch evidence and continue writing",
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
MESSAGE_ID_BOUNDARY_REQUIREMENTS = (
|
|
414
|
+
"`messageId` is only a CER message-layer identity, deduplication, and tracing field",
|
|
415
|
+
"It is not a Codex execution command, an App Server `method`, a JSON-RPC request `id`, a `threadId`, a `sessionId`, an idempotency key, or authorization",
|
|
416
|
+
"Without an actual tool call and its tool result or verifiable delivery evidence",
|
|
417
|
+
"a `messageId` alone is not proof that a message was delivered or work was executed",
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
MESSAGE_ID_UAT_REQUIREMENTS = {
|
|
421
|
+
"identity_not_command": "A `messageId` is merely placed in a prompt, dispatch packet, summary, or receipt-like text and treated as proof that a thread was created, a turn started, a tool was called, a write was triggered, or authority was granted",
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
MESSAGE_ID_FORBIDDEN = {
|
|
425
|
+
"messageid_starts_operation": "A `messageId` alone can create a thread, start a turn, or call a tool",
|
|
426
|
+
"messageid_is_authority": "A `messageId` itself is authorization or an idempotency key",
|
|
427
|
+
"send_success_is_delivered": "send success is `confirmed_delivered`",
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
LIVING_BRIEF_REQUIREMENTS = (
|
|
431
|
+
"C maintains a living task brief",
|
|
432
|
+
"The living task brief is not a new workflow and does not create fixed project documents",
|
|
433
|
+
"confirmed requirements/exclusions, safe inferences, critical gaps, latest user feedback, current batch freeze, next observable preview or decision point, and what changed from the previous version",
|
|
434
|
+
"C freezes only the next safely executable batch",
|
|
435
|
+
"E1/R dispatches use the latest living task brief and current batch freeze",
|
|
436
|
+
"E1 is authorized only to execute the current batch freeze",
|
|
437
|
+
"R reviews against the latest task brief, current batch freeze, candidate identity, and delivery evidence",
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
LIVING_BRIEF_ROADMAP_REQUIREMENTS = (
|
|
441
|
+
"shows the living task brief, current batch freeze, and next observable checkpoint",
|
|
442
|
+
"Any user-visible living-brief rendering must carry `CER` identity",
|
|
443
|
+
"Do not present it as a Codex task brief, Goal plan, assistant plan, or unbranded internal feature",
|
|
444
|
+
"CER roadmap | live brief",
|
|
445
|
+
"CER live brief: confirmed=<...> | safe inference=<...> | decisions needed=<...>",
|
|
446
|
+
"CER current batch freeze: <only what this batch will do>",
|
|
447
|
+
"CER last feedback / change: <... / none>",
|
|
448
|
+
"The living task brief also derives only from the highest available authority",
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
LIVING_BRIEF_UAT_REQUIREMENTS = {
|
|
452
|
+
"fuzzy_start": "For a fuzzy but startable multi-batch task, C creates a living task brief",
|
|
453
|
+
"no_full_spec_first": "does not require the user to write a complete specification first",
|
|
454
|
+
"not_project_context_prereq": "does not treat `$project-context-workflow` as a prerequisite",
|
|
455
|
+
"feedback_delta": "After the user sees an intermediate result and changes direction or adds a constraint, C first updates the living task brief and roadmap delta",
|
|
456
|
+
"review_latest_brief": "R reviews against the latest task brief, current batch freeze, candidate identity, and delivery evidence",
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
LIVING_BRIEF_FORBIDDEN = {
|
|
460
|
+
"new_workflow": "The living task brief is an independent new workflow",
|
|
461
|
+
"initial_prompt_full_freeze": "The initial prompt is always the complete frozen specification for the cycle",
|
|
462
|
+
"e1_unfrozen_future": "E1 may implement unfrozen future batches on its own",
|
|
463
|
+
"r_initial_prompt_only": "R reviews only against the initial prompt",
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
OUTCOME_ANCHOR_REQUIREMENTS = (
|
|
467
|
+
"immutable `outcome_anchor`",
|
|
468
|
+
"unacceptable substitute outcomes",
|
|
469
|
+
"`mainline_outcome`, `diagnostic`, `mechanism_improvement`, or `governance_self_improvement`",
|
|
470
|
+
"zero expected outcome improvement and no necessary-prerequisite role must not be dispatched",
|
|
471
|
+
"Activity is not outcome",
|
|
472
|
+
"only after reading back and accepting a difference against one of the user's completion conditions",
|
|
473
|
+
"same failure class is judged by shared root cause, user consequence, affected completion condition, and method",
|
|
474
|
+
"Renaming, version changes, repackaging",
|
|
475
|
+
"After two consecutive unresolved attempts in one class, C must not dispatch a third same-class repair",
|
|
476
|
+
"A batch may end only as accepted outcome",
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
OUTCOME_ANCHOR_ROADMAP_REQUIREMENTS = (
|
|
480
|
+
"accepted outcome difference against `outcome_anchor`",
|
|
481
|
+
"Do not substitute batch, task, or review counts for outcome progress",
|
|
482
|
+
"CER outcome anchor: unfinished=<completion condition> | accepted delta=<outcome difference / none>",
|
|
483
|
+
"CER work lane: <mainline_outcome / diagnostic / mechanism_improvement / governance_self_improvement>",
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
OUTCOME_ANCHOR_UAT_REQUIREMENTS = {
|
|
487
|
+
"anchor_fixed": "Long multi-batch work fixes `outcome_anchor` before the first batch",
|
|
488
|
+
"zero_delta_rejected": "An implementation batch with zero expected outcome improvement and no necessary-prerequisite role is rejected",
|
|
489
|
+
"diagnostic_not_progress": "A diagnostic batch may run and produce a handoff prerequisite, but it is classified as `diagnostic` and does not increase mainline progress",
|
|
490
|
+
"technical_pass_not_progress": "Technical checks, format, file consistency, or review may pass, but when `outcome_anchor` has no accepted outcome difference, the batch is not marked as successful progress",
|
|
491
|
+
"third_retry_intercepted": "After two consecutive unresolved attempts in the same failure class, a third same-class repair is intercepted",
|
|
492
|
+
"rename_same_retry": "Renaming, version changes, repackaging, or redispatching the same method is still treated as the same retry class",
|
|
493
|
+
"reviewer_rejects_drift": "R must reject a batch that diverges from the original outcome, is only technical activity, repeats rework, or substitutes another deliverable shape for what the user asked for",
|
|
494
|
+
"mechanism_not_mainline": "`mechanism_improvement` or `governance_self_improvement` does not contaminate mainline progress",
|
|
495
|
+
"adjacent_not_blocker": "Adjacent improvement failure does not automatically block the original task",
|
|
496
|
+
"simple_lightweight": "Simple, one-step, low-risk work with one clear endpoint still uses lightweight summary and C readback",
|
|
497
|
+
"completion_outcomes": "Completion reporting lists accepted outcome differences and unfinished conditions",
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
OUTCOME_ANCHOR_FORBIDDEN = {
|
|
501
|
+
"zero_delta_dispatch": "An implementation batch with zero expected outcome improvement may be dispatched",
|
|
502
|
+
"diagnostic_mainline": "A diagnostic batch increases mainline progress",
|
|
503
|
+
"third_retry_allowed": "A third same-class repair may continue",
|
|
504
|
+
"activity_completion": "Batch, task, Reviewer, or candidate counts are completion evidence",
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
DRIFT_CHECKPOINT_REQUIREMENTS = {
|
|
508
|
+
"sole_owner": "The long-task drift checkpoint is the sole owner in this section",
|
|
509
|
+
"no_new_monitor": "do not create another monitoring role, background process, or fixed table",
|
|
510
|
+
"resume_trigger": "resume/context transition",
|
|
511
|
+
"two_no_delta_trigger": "two consecutive batches with no accepted outcome difference",
|
|
512
|
+
"same_failure_trigger": "the second same-class failure",
|
|
513
|
+
"adjacent_trigger": "E1/R proposes an adjacent direction change or substitute deliverable",
|
|
514
|
+
"user_change_trigger": "the user changes direction or adds constraints",
|
|
515
|
+
"close_release_trigger": "before close/release/major delivery",
|
|
516
|
+
"next_condition": "whether the next batch still improves an unfinished `outcome_anchor` condition",
|
|
517
|
+
"readable_delta": "what readable outcome difference success will create",
|
|
518
|
+
"mainline_replacement": "are replacing the mainline outcome",
|
|
519
|
+
"no_dispatch": "C must not dispatch a formal implementation batch",
|
|
520
|
+
"allowed_exits": "may only switch to diagnostic work, narrow acceptance, stop for user decision, terminate the route",
|
|
521
|
+
"fresh_r_bounded": "when C cannot reliably disprove the risk and the risk level justifies it",
|
|
522
|
+
"not_progress": "A checkpoint, living task brief, or roadmap update does not count as outcome progress",
|
|
523
|
+
"no_monitoring": "must not trigger background monitoring, polling, automatic `wait_threads`, fixed R, fixed Full Audit",
|
|
524
|
+
"simple_exempt": "simple, one-step, low-risk work with one clear endpoint",
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
DRIFT_CHECKPOINT_UAT_REQUIREMENTS = {
|
|
528
|
+
"generic_trigger": "long-running, multi-batch, or context-pollution-prone work",
|
|
529
|
+
"two_no_delta": "two consecutive batches with no accepted outcome difference",
|
|
530
|
+
"same_failure": "the second same-class failure",
|
|
531
|
+
"adjacent_change": "E1/R proposes an adjacent direction change or substitute deliverable",
|
|
532
|
+
"next_condition": "which unfinished `outcome_anchor` condition the next batch improves",
|
|
533
|
+
"not_progress": "A drift checkpoint, living task brief, or roadmap update does not count as outcome progress",
|
|
534
|
+
"no_monitoring": "does not trigger background monitoring, polling, automatic `wait_threads`, fixed R, or fixed Full Audit",
|
|
535
|
+
"missing_checkpoint_dispatch": "After two consecutive batches with no accepted outcome difference, C dispatches another mainline implementation batch without a drift checkpoint",
|
|
536
|
+
"adjacent_rewrites_mainline": "After E1/R proposes an adjacent direction change, substitute deliverable, or out-of-scope blocker, C rewrites the next mainline batch without classifying whether that proposal replaces the mainline outcome",
|
|
537
|
+
"checkpoint_as_progress": "A drift checkpoint, living task brief, or roadmap update is counted as outcome progress",
|
|
538
|
+
"checkpoint_triggers_monitoring": "A drift checkpoint triggers background monitoring, polling, automatic `wait_threads`, fixed R, or fixed Full Audit",
|
|
539
|
+
"simple_forced": "Simple, one-step, low-risk work with one clear endpoint is forced to run a drift checkpoint",
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
DRIFT_CHECKPOINT_FORBIDDEN = {
|
|
543
|
+
"background_monitor": "a drift checkpoint starts background monitoring",
|
|
544
|
+
"automatic_wait": "a drift checkpoint may automatically use `wait_threads`",
|
|
545
|
+
"fixed_reviewer": "every drift checkpoint must create R",
|
|
546
|
+
"fixed_full_audit": "every drift checkpoint triggers Full Audit",
|
|
547
|
+
"progress_credit": "a drift checkpoint itself increases mainline outcome progress",
|
|
548
|
+
"simple_required": "simple one-step work must run a drift checkpoint",
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
RESULT_DISPOSITION_REQUIREMENTS = {
|
|
552
|
+
"sole_owner": "The result disposition gate is the sole owner in this section",
|
|
553
|
+
"accepted_as": "`accepted_as` is `evidence_only`, `working_candidate`, `terminal_deliverable`, or `authoritative_input`",
|
|
554
|
+
"bare_result": "Bare `RESULT_ACCEPTED` means only that C adjudicated this batch and that communication can deduplicate it",
|
|
555
|
+
"unmet_persistence_fields": "`unmet_conditions`, `persistence_readback`",
|
|
556
|
+
"permitted_next_use_only": "`permitted_next_use` is the only next-use permission field",
|
|
557
|
+
"prior_result_use_enum": "classify `prior_result_use` as `working_material` or `authority_input`",
|
|
558
|
+
"authority_fields": "if it is `authority_input`, C must list `promotion_evidence` and `project_owner_anchor`",
|
|
559
|
+
"default_working_material": "Candidates, drafts, diagnostics, derived outputs, and review-only results default to `working_material` only",
|
|
560
|
+
"authority_requires_owner": "To accept one as `authoritative_input`, C needs an explicit user decision or an actually read target-project owner anchor",
|
|
561
|
+
"reviewer_split": "separate `content_verdict`, `implementation_verdict`, `outcome_verdict`, and `authority_promotion_verdict`",
|
|
562
|
+
"reviewer_pass_limited": "a content or technical PASS does not automatically become an outcome PASS, authority-promotion PASS, or mainline progress",
|
|
563
|
+
"out_of_scope_not_pass": "`out_of_scope` is not PASS",
|
|
564
|
+
"review_scope_limited": "C must not expand R's original review scope",
|
|
565
|
+
"terminal_candidate": "Only when the `outcome_anchor` itself asks for a draft, candidate, or sample as the endpoint",
|
|
566
|
+
"persistence_blocks_next": "persistent truths conflict, are not synchronized, or the artifact role cannot be determined, `next_dispatch` must be `blocked`",
|
|
567
|
+
"missing_readback_blocks_authority": "When `persistence_readback` is missing, persistence is only claimed without owner readback",
|
|
568
|
+
"unmet_conditions_blocks_authority": "`unmet_conditions` are not cleared, the next batch may consume the result only as `working_material` or diagnostic evidence, or remain blocked",
|
|
569
|
+
"terminal_persistence_blocks_acceptance": "even when there is no next batch, C must not accept the result as a `terminal_deliverable`, report progress, or claim completion",
|
|
570
|
+
"terminal_artifact_set_consistency": "C must also read back the final-state claim of every artifact classified as a `terminal_deliverable`",
|
|
571
|
+
"closed_vocabulary": "`accepted_as`, `authority_effect`, `progress_effect`, and `prior_result_use` values above are closed vocabularies",
|
|
572
|
+
"validate_before_persistence": "Before persistence, the proper writer must validate these fields against this section",
|
|
573
|
+
"close_bundle_scope": "Before closing, terminally accepting, or handing off any long-running, multi-batch, high-risk, or non-simple formal CER batch",
|
|
574
|
+
"close_bundle_not_schema": "not a new runtime owner, public command, KDL dependency, or parallel result-disposition schema",
|
|
575
|
+
"close_bundle_not_ordinary_goal": "ordinary execution, Goal drafts, and low-risk small batches do not have to use it",
|
|
576
|
+
"close_bundle_identity": "existing `messageId`, `batchId`, `batchSeq`, `payloadDigest`",
|
|
577
|
+
"close_bundle_finish_line": "`pre_dispatch_evidence`/`outcome_anchor` pointer, this batch's unfinished condition, readable outcome delta",
|
|
578
|
+
"close_bundle_buckets": "`acceptance_blockers`, `worker_regressions`, `adjacent_backlog`, `scope_change_requests`",
|
|
579
|
+
"close_bundle_identity_mismatch": "dispatch, result, and ack identity or digest do not match",
|
|
580
|
+
"close_bundle_delivery_blocks": "`delivery_state` is still `delivery_unknown`/`not_delivered`",
|
|
581
|
+
"close_bundle_repair_buckets": "Only `acceptance_blockers` and `worker_regressions` may cause a bounded repair while repair budget remains",
|
|
582
|
+
"close_bundle_budget_no_reset": "ack must not reset attempt or raise max_attempts",
|
|
583
|
+
"close_bundle_adjacent_backlog": "`adjacent_backlog` is recorded separately",
|
|
584
|
+
"close_bundle_scope_change": "`scope_change_requests` may only block or stop for user rebaseline",
|
|
585
|
+
"close_bundle_close_clear": "`next_dispatch=close` must have no remaining acceptance blocker",
|
|
586
|
+
"close_bundle_pass_limited": "Validator or closure PASS proves only coordination-closure consistency",
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
RESULT_DISPOSITION_UAT_REQUIREMENTS = {
|
|
590
|
+
"content_pass_candidate": "When a Reviewer passes candidate content",
|
|
591
|
+
"derived_output_blocked": "When a `derived_output` is listed by the next batch as `authority_input`",
|
|
592
|
+
"authority_input_missing_fields": "When `prior_result_use: authority_input` is missing `promotion_evidence` or `project_owner_anchor`",
|
|
593
|
+
"working_material_use_limits": "`prior_result_use: working_material` permits only editing, comparison, review, or refinement, not decision authority",
|
|
594
|
+
"working_material_allowed": "`prior_result_use` to `working_material`",
|
|
595
|
+
"technical_pass_limited": "When Reviewer technical PASS has outcome FAIL",
|
|
596
|
+
"split_verdict_limited": "When Reviewer provides only `content_verdict: pass` or `implementation_verdict: pass`",
|
|
597
|
+
"authority_out_of_scope": "`authority_promotion_verdict` is `out_of_scope`",
|
|
598
|
+
"truth_conflict_blocks": "handoff, plan, progress, or another target-project source of truth",
|
|
599
|
+
"persistence_change_classes": "result disposition changes current phase, artifact role, next product route, authoritative source, progress claim, or later batch input",
|
|
600
|
+
"terminal_stale_state": "the final batch produced a correct deliverable but the target-project current-state owner still records the old phase, no terminal deliverable, or a stale next action",
|
|
601
|
+
"terminal_artifact_set_conflict": "a `RUN_RESULT` classified as a `terminal_deliverable` still says persistence pending, unaccepted, or an old phase",
|
|
602
|
+
"accepted_as_synonym_rejected": "`accepted_as=terminal_outcome`",
|
|
603
|
+
"phase1_legal_disposition": "When a Phase 1 candidate completes only a non-terminal checkpoint",
|
|
604
|
+
"progress_effect_synonym_rejected": "`progress_effect=accepted_outcome_delta_for_phase1_only`",
|
|
605
|
+
"draft_terminal_deliverable": "user's endpoint itself is a draft, candidate, or sample",
|
|
606
|
+
"missing_unmet_readback": "Result disposition is missing `unmet_conditions` or `persistence_readback`",
|
|
607
|
+
"next_allowed_use_rejected": "C uses `next_allowed_use` instead of the only valid `permitted_next_use`",
|
|
608
|
+
"next_batch_source_named": "the next batch does not state whether it consumes accepted authority, working material, diagnostic evidence, or clean baseline",
|
|
609
|
+
"close_bundle_scope": "Before closing a long-running, multi-batch, high-risk, or non-simple formal CER batch",
|
|
610
|
+
"close_bundle_no_second_schema": "It does not create a second result-disposition schema",
|
|
611
|
+
"close_bundle_no_ordinary_goal": "does not force ordinary execution, Goal drafts, or low-risk small batches to use it",
|
|
612
|
+
"close_bundle_repair_budget": "ack has not reset attempt or raised max_attempts",
|
|
613
|
+
"close_bundle_adjacent_backlog": "`adjacent_backlog` is the only finding",
|
|
614
|
+
"close_bundle_scope_change": "`scope_change_requests` are present",
|
|
615
|
+
"close_bundle_identity_delivery": "dispatch/result/ack identity or `payloadDigest` does not match",
|
|
616
|
+
"close_bundle_pass_limited": "close-bundle validator PASS means only coordination-closure consistency",
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
RESULT_DISPOSITION_FORBIDDEN = {
|
|
620
|
+
"bare_result_promotes": "Bare `RESULT_ACCEPTED` means official acceptance",
|
|
621
|
+
"candidate_auto_authority": "A candidate PASS automatically becomes authoritative input",
|
|
622
|
+
"technical_pass_outcome": "R technical PASS is outcome PASS",
|
|
623
|
+
"authority_without_promotion_fields": "`authority_input` may omit `promotion_evidence` or `project_owner_anchor`",
|
|
624
|
+
"out_of_scope_pass": "`out_of_scope` counts as PASS",
|
|
625
|
+
"unpersisted_next_dispatch": "C may dispatch the next batch before persistence",
|
|
626
|
+
"unpersisted_terminal_acceptance": "The final batch may be accepted as a `terminal_deliverable` and completion claimed while persistent truth is stale",
|
|
627
|
+
"contradictory_terminal_artifact_accepted": "A terminal set may include an artifact that still says persistence pending and still be accepted",
|
|
628
|
+
"missing_readback_authority": "Missing `persistence_readback` may still be authoritative input",
|
|
629
|
+
"next_allowed_use_substitutes": "`next_allowed_use` may substitute for `permitted_next_use`",
|
|
630
|
+
"unmet_conditions_dispatch": "C may dispatch the next batch before `unmet_conditions` are cleared",
|
|
631
|
+
"close_bundle_forces_ordinary": "delegation close bundle may be mandatory for ordinary execution, Goal drafts, or low-risk small batches",
|
|
632
|
+
"close_bundle_synonym_schema": "close bundle may create result-disposition synonym fields and replace this section's closed vocabularies",
|
|
633
|
+
"close_bundle_mismatch_close": "dispatch/result/ack identity mismatch may still be `RESULT_ACCEPTED`",
|
|
634
|
+
"close_bundle_adjacent_repair": "`adjacent_backlog` may by itself trigger mainline repair",
|
|
635
|
+
"close_bundle_scope_repair": "`scope_change_requests` may be packaged as `bounded_repair`",
|
|
636
|
+
"close_bundle_budget_reset": "ack may reset repair attempt or raise max_attempts",
|
|
637
|
+
"close_bundle_overclaim": "close-bundle validator PASS may claim outcome PASS, release-ready, npm-ready, or token saving",
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def read_texts(root: Path) -> dict[str, str]:
|
|
642
|
+
texts: dict[str, str] = {}
|
|
643
|
+
for relative in EXPECTED_FILES:
|
|
644
|
+
path = root / Path(relative)
|
|
645
|
+
if path.is_file():
|
|
646
|
+
texts[relative] = path.read_text(encoding="utf-8-sig")
|
|
647
|
+
return texts
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
def frontmatter_findings(skill_text: str) -> list[str]:
|
|
651
|
+
findings: list[str] = []
|
|
652
|
+
match = re.match(r"\A---\n([\s\S]*?)\n---\n", skill_text)
|
|
653
|
+
if not match:
|
|
654
|
+
return ["SKILL.md frontmatter is missing or malformed"]
|
|
655
|
+
keys: list[str] = []
|
|
656
|
+
for line in match.group(1).splitlines():
|
|
657
|
+
key_match = re.match(r"^([a-z_]+):", line)
|
|
658
|
+
if not key_match:
|
|
659
|
+
findings.append(f"SKILL.md frontmatter malformed line: {line}")
|
|
660
|
+
continue
|
|
661
|
+
keys.append(key_match.group(1))
|
|
662
|
+
if keys != ["name", "description"]:
|
|
663
|
+
findings.append(f"SKILL.md frontmatter keys must be name,description; actual={keys}")
|
|
664
|
+
if not re.search(r'^name:\s*cer-workflow-en\s*$', match.group(1), re.MULTILINE):
|
|
665
|
+
findings.append("SKILL.md name must be cer-workflow-en")
|
|
666
|
+
if "explicit CER-qualified" not in match.group(1) or "on-demand parallel candidate" not in match.group(1):
|
|
667
|
+
findings.append("SKILL.md description lacks explicit CER trigger or on-demand parallel capability")
|
|
668
|
+
return findings
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
def openai_yaml_findings(text: str) -> list[str]:
|
|
672
|
+
findings: list[str] = []
|
|
673
|
+
required_lines = (
|
|
674
|
+
'interface:',
|
|
675
|
+
' display_name: "CER Workflow"',
|
|
676
|
+
' short_description: "',
|
|
677
|
+
' default_prompt: "',
|
|
678
|
+
'policy:',
|
|
679
|
+
' allow_implicit_invocation: false',
|
|
680
|
+
)
|
|
681
|
+
for required in required_lines:
|
|
682
|
+
if required not in text:
|
|
683
|
+
findings.append(f"agents/openai.yaml missing required shape: {required}")
|
|
684
|
+
if "$cer-workflow-en" not in text:
|
|
685
|
+
findings.append("agents/openai.yaml default_prompt must include $cer-workflow-en")
|
|
686
|
+
default_prompt_match = re.search(
|
|
687
|
+
r'^\s+default_prompt:\s*"([^"]+)"\s*$', text, re.MULTILINE
|
|
688
|
+
)
|
|
689
|
+
if default_prompt_match:
|
|
690
|
+
default_prompt = default_prompt_match.group(1)
|
|
691
|
+
# This is controlled package metadata, so exact equality is safer than
|
|
692
|
+
# an open-ended synonym blacklist.
|
|
693
|
+
if default_prompt != EXPECTED_DEFAULT_PROMPT:
|
|
694
|
+
findings.append(
|
|
695
|
+
"agents/openai.yaml default_prompt must exactly match the canonical "
|
|
696
|
+
"risk-proportionate prompt and must not force Reviewer for simple work"
|
|
697
|
+
)
|
|
698
|
+
if re.search(r"\b(?:producer|lane|scratch|hash)\b", text, re.IGNORECASE):
|
|
699
|
+
findings.append("agents/openai.yaml must not expose producer setup vocabulary")
|
|
700
|
+
if re.search(r"^\s*(?:icon_small|icon_large|brand_color|dependencies):", text, re.MULTILINE):
|
|
701
|
+
findings.append("agents/openai.yaml contains an unprovided icon, brand or dependency")
|
|
702
|
+
top_keys = re.findall(r"^([a-z_]+):\s*$", text, re.MULTILINE)
|
|
703
|
+
if top_keys != ["interface", "policy"]:
|
|
704
|
+
findings.append(f"agents/openai.yaml top-level keys mismatch: {top_keys}")
|
|
705
|
+
for line in text.splitlines():
|
|
706
|
+
if re.match(r"^\s+(?:display_name|short_description|default_prompt):", line):
|
|
707
|
+
if not re.search(r':\s*"[^"]*"\s*$', line):
|
|
708
|
+
findings.append(f"agents/openai.yaml interface value must be quoted: {line}")
|
|
709
|
+
short_match = re.search(r'^\s+short_description:\s*"([^"]+)"\s*$', text, re.MULTILINE)
|
|
710
|
+
if short_match and not 25 <= len(short_match.group(1)) <= 64:
|
|
711
|
+
findings.append("agents/openai.yaml short_description must be 25-64 characters")
|
|
712
|
+
return findings
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
def normalized_contains(text: str, snippet: str) -> bool:
|
|
716
|
+
return re.sub(r"\s+", " ", snippet) in re.sub(r"\s+", " ", text)
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
def markdown_section(text: str, heading: str) -> str:
|
|
720
|
+
match = re.search(
|
|
721
|
+
rf"^{re.escape(heading)}[ \t]*\n([\s\S]*?)(?=^## |\Z)",
|
|
722
|
+
text,
|
|
723
|
+
flags=re.MULTILINE,
|
|
724
|
+
)
|
|
725
|
+
return match.group(1) if match else ""
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def command_table_row(text: str, command: str) -> str:
|
|
729
|
+
pattern = r"^\|\s*`" + re.escape(command) + r"(?:\s+[^`]*)?`\s*\|.*$"
|
|
730
|
+
match = re.search(pattern, text, flags=re.MULTILINE)
|
|
731
|
+
return match.group(0) if match else ""
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
def assert_snippets_present(
|
|
735
|
+
text: str,
|
|
736
|
+
snippets: tuple[str, ...],
|
|
737
|
+
label: str,
|
|
738
|
+
findings: list[str],
|
|
739
|
+
) -> None:
|
|
740
|
+
for snippet in snippets:
|
|
741
|
+
if not normalized_contains(text, snippet):
|
|
742
|
+
findings.append(f"trigger matrix missing {label}: {snippet}")
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
def trigger_matrix_findings(texts: dict[str, str]) -> list[str]:
|
|
746
|
+
findings: list[str] = []
|
|
747
|
+
skill = texts["SKILL.md"]
|
|
748
|
+
core = texts["references/core-runtime.md"]
|
|
749
|
+
uat = texts["references/uat.md"]
|
|
750
|
+
|
|
751
|
+
frontmatter = re.match(r"\A---\n([\s\S]*?)\n---\n", skill)
|
|
752
|
+
description = frontmatter.group(1) if frontmatter else ""
|
|
753
|
+
assert_snippets_present(
|
|
754
|
+
description,
|
|
755
|
+
EN_TRIGGER_MATRIX_EXPECTATIONS["frontmatter"],
|
|
756
|
+
"SKILL.md frontmatter trigger boundary",
|
|
757
|
+
findings,
|
|
758
|
+
)
|
|
759
|
+
for label, source in (
|
|
760
|
+
("SKILL.md /CER-auto row", command_table_row(skill, "/CER-auto")),
|
|
761
|
+
("core-runtime.md /CER-auto row", command_table_row(core, "/CER-auto")),
|
|
762
|
+
):
|
|
763
|
+
assert_snippets_present(
|
|
764
|
+
source,
|
|
765
|
+
EN_TRIGGER_MATRIX_EXPECTATIONS["auto_row"],
|
|
766
|
+
label,
|
|
767
|
+
findings,
|
|
768
|
+
)
|
|
769
|
+
for label, source in (
|
|
770
|
+
("SKILL.md /CER-start row", command_table_row(skill, "/CER-start")),
|
|
771
|
+
("core-runtime.md /CER-start row", command_table_row(core, "/CER-start")),
|
|
772
|
+
):
|
|
773
|
+
assert_snippets_present(
|
|
774
|
+
source,
|
|
775
|
+
EN_TRIGGER_MATRIX_EXPECTATIONS["start_row"],
|
|
776
|
+
label,
|
|
777
|
+
findings,
|
|
778
|
+
)
|
|
779
|
+
for label, source in (
|
|
780
|
+
("SKILL.md /CER-close row", command_table_row(skill, "/CER-close")),
|
|
781
|
+
("core-runtime.md /CER-close row", command_table_row(core, "/CER-close")),
|
|
782
|
+
):
|
|
783
|
+
assert_snippets_present(
|
|
784
|
+
source,
|
|
785
|
+
EN_TRIGGER_MATRIX_EXPECTATIONS["close_row"],
|
|
786
|
+
label,
|
|
787
|
+
findings,
|
|
788
|
+
)
|
|
789
|
+
assert_snippets_present(
|
|
790
|
+
markdown_section(skill, "## Commands"),
|
|
791
|
+
EN_TRIGGER_MATRIX_EXPECTATIONS["auto_help_template"],
|
|
792
|
+
"SKILL.md /CER-auto task template help",
|
|
793
|
+
findings,
|
|
794
|
+
)
|
|
795
|
+
assert_snippets_present(
|
|
796
|
+
markdown_section(core, "## Startup"),
|
|
797
|
+
EN_TRIGGER_MATRIX_EXPECTATIONS["startup_owner"],
|
|
798
|
+
"core-runtime.md startup owner",
|
|
799
|
+
findings,
|
|
800
|
+
)
|
|
801
|
+
assert_snippets_present(
|
|
802
|
+
markdown_section(core, "## Stop CER"),
|
|
803
|
+
EN_TRIGGER_MATRIX_EXPECTATIONS["stop_owner"],
|
|
804
|
+
"core-runtime.md stop owner",
|
|
805
|
+
findings,
|
|
806
|
+
)
|
|
807
|
+
install = markdown_section(uat, "## Installation Scenario")
|
|
808
|
+
assert_snippets_present(
|
|
809
|
+
install,
|
|
810
|
+
EN_TRIGGER_MATRIX_EXPECTATIONS["uat_install_auto"],
|
|
811
|
+
"uat.md installation auto matrix",
|
|
812
|
+
findings,
|
|
813
|
+
)
|
|
814
|
+
assert_snippets_present(
|
|
815
|
+
install,
|
|
816
|
+
EN_TRIGGER_MATRIX_EXPECTATIONS["uat_install_start"],
|
|
817
|
+
"uat.md installation start matrix",
|
|
818
|
+
findings,
|
|
819
|
+
)
|
|
820
|
+
assert_snippets_present(
|
|
821
|
+
install,
|
|
822
|
+
EN_TRIGGER_MATRIX_EXPECTATIONS["uat_install_close"],
|
|
823
|
+
"uat.md installation close matrix",
|
|
824
|
+
findings,
|
|
825
|
+
)
|
|
826
|
+
assert_snippets_present(
|
|
827
|
+
markdown_section(uat, "## Failure Conditions"),
|
|
828
|
+
EN_TRIGGER_MATRIX_EXPECTATIONS["uat_failure"],
|
|
829
|
+
"uat.md failure-condition matrix",
|
|
830
|
+
findings,
|
|
831
|
+
)
|
|
832
|
+
assert_snippets_present(
|
|
833
|
+
markdown_section(uat, "## Failure Conditions"),
|
|
834
|
+
EN_TRIGGER_MATRIX_EXPECTATIONS["uat_failure_auto"],
|
|
835
|
+
"uat.md auto failure-condition matrix",
|
|
836
|
+
findings,
|
|
837
|
+
)
|
|
838
|
+
return findings
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
def link_findings(root: Path, texts: dict[str, str]) -> list[str]:
|
|
842
|
+
findings: list[str] = []
|
|
843
|
+
link_re = re.compile(r"\[[^\]]+\]\(([^)]+)\)")
|
|
844
|
+
for relative, text in texts.items():
|
|
845
|
+
if not relative.endswith(".md"):
|
|
846
|
+
continue
|
|
847
|
+
source = PurePosixPath(relative)
|
|
848
|
+
for target in link_re.findall(text):
|
|
849
|
+
target_path = target.split("#", 1)[0]
|
|
850
|
+
if not target_path or "://" in target_path:
|
|
851
|
+
continue
|
|
852
|
+
resolved = (root / Path(str(source.parent / target_path))).resolve()
|
|
853
|
+
try:
|
|
854
|
+
resolved.relative_to(root.resolve())
|
|
855
|
+
except ValueError:
|
|
856
|
+
findings.append(f"relative link escapes skill root: {relative} -> {target}")
|
|
857
|
+
continue
|
|
858
|
+
if not resolved.is_file():
|
|
859
|
+
findings.append(f"relative link target missing: {relative} -> {target}")
|
|
860
|
+
return findings
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
def validate_texts(root: Path, texts: dict[str, str]) -> list[str]:
|
|
864
|
+
findings: list[str] = []
|
|
865
|
+
missing = sorted(EXPECTED_FILES - set(texts))
|
|
866
|
+
if missing:
|
|
867
|
+
findings.append(f"required files missing: {missing}")
|
|
868
|
+
return findings
|
|
869
|
+
|
|
870
|
+
version = texts["VERSION"]
|
|
871
|
+
if not re.fullmatch(r"\d+\.\d+\.\d+\n?", version):
|
|
872
|
+
findings.append("VERSION must contain exactly one stable semver line")
|
|
873
|
+
for relative in TEXT_FILES:
|
|
874
|
+
matches = SEMVER_RE.findall(texts[relative])
|
|
875
|
+
if matches:
|
|
876
|
+
findings.append(f"concrete package semver outside VERSION: {relative}: {matches}")
|
|
877
|
+
|
|
878
|
+
findings.extend(frontmatter_findings(texts["SKILL.md"]))
|
|
879
|
+
findings.extend(openai_yaml_findings(texts["agents/openai.yaml"]))
|
|
880
|
+
findings.extend(link_findings(root, texts))
|
|
881
|
+
findings.extend(trigger_matrix_findings(texts))
|
|
882
|
+
|
|
883
|
+
skill_commands = {
|
|
884
|
+
match.group(1)
|
|
885
|
+
for match in re.finditer(r"^\|\s*`(/CER-[a-z]+)(?:\s+[^`]*)?`", texts["SKILL.md"], re.MULTILINE)
|
|
886
|
+
}
|
|
887
|
+
if skill_commands != FORMAL_COMMANDS:
|
|
888
|
+
findings.append(f"slash commands must remain exactly six: {sorted(skill_commands)}")
|
|
889
|
+
|
|
890
|
+
all_markdown = "\n".join(
|
|
891
|
+
texts[relative] for relative in sorted(texts) if relative.endswith(".md")
|
|
892
|
+
)
|
|
893
|
+
normalized_markdown = re.sub(r"\s+", " ", all_markdown)
|
|
894
|
+
if all_markdown.count(OWNER_MARKER) != 1:
|
|
895
|
+
findings.append("parallel producer owner marker must occur exactly once")
|
|
896
|
+
if OWNER_MARKER not in texts["references/parallel-producers.md"]:
|
|
897
|
+
findings.append("parallel producer owner marker is not in its sole owner")
|
|
898
|
+
if all_markdown.count(UNEXPECTED_FAILURE_OWNER_MARKER) != 1:
|
|
899
|
+
findings.append("unexpected-failure gate owner marker must occur exactly once")
|
|
900
|
+
if UNEXPECTED_FAILURE_OWNER_MARKER not in texts["references/core-runtime.md"]:
|
|
901
|
+
findings.append("unexpected-failure gate owner marker is not in core-runtime.md")
|
|
902
|
+
if all_markdown.count(TRUTH_SOURCE_INTAKE_OWNER_MARKER) != 1:
|
|
903
|
+
findings.append("truth-source intake owner marker must occur exactly once")
|
|
904
|
+
if TRUTH_SOURCE_INTAKE_OWNER_MARKER not in texts["references/core-runtime.md"]:
|
|
905
|
+
findings.append("truth-source intake owner marker is not in core-runtime.md")
|
|
906
|
+
if all_markdown.count(DRIFT_CHECKPOINT_OWNER_MARKER) != 1:
|
|
907
|
+
findings.append("drift checkpoint owner marker must occur exactly once")
|
|
908
|
+
if DRIFT_CHECKPOINT_OWNER_MARKER not in texts["references/core-runtime.md"]:
|
|
909
|
+
findings.append("drift checkpoint owner marker is not in core-runtime.md")
|
|
910
|
+
if all_markdown.count(RESULT_DISPOSITION_OWNER_MARKER) != 1:
|
|
911
|
+
findings.append("result disposition owner marker must occur exactly once")
|
|
912
|
+
if RESULT_DISPOSITION_OWNER_MARKER not in texts["references/core-runtime.md"]:
|
|
913
|
+
findings.append("result disposition owner marker is not in core-runtime.md")
|
|
914
|
+
if all_markdown.count(EXECUTION_PROFILE_OWNER_MARKER) != 1:
|
|
915
|
+
findings.append("execution profile owner marker must occur exactly once")
|
|
916
|
+
if EXECUTION_PROFILE_OWNER_MARKER not in texts["references/core-runtime.md"]:
|
|
917
|
+
findings.append("execution profile owner marker is not in core-runtime.md")
|
|
918
|
+
if all_markdown.count(PUBLIC_RUNTIME_LANGUAGE_OWNER_MARKER) != 1:
|
|
919
|
+
findings.append("public runtime language owner marker must occur exactly once")
|
|
920
|
+
if PUBLIC_RUNTIME_LANGUAGE_OWNER_MARKER not in texts["references/core-runtime.md"]:
|
|
921
|
+
findings.append("public runtime language owner marker is not in core-runtime.md")
|
|
922
|
+
|
|
923
|
+
owner = re.sub(r"\s+", " ", texts["references/parallel-producers.md"])
|
|
924
|
+
for label, required in OWNER_REQUIREMENTS.items():
|
|
925
|
+
if required not in owner:
|
|
926
|
+
findings.append(f"parallel producer owner missing {label}")
|
|
927
|
+
if "## Exploration Helper Auto-Scheduling" in all_markdown:
|
|
928
|
+
findings.append("legacy exploration-helper owner section remains")
|
|
929
|
+
|
|
930
|
+
skill = texts["SKILL.md"]
|
|
931
|
+
core = texts["references/core-runtime.md"]
|
|
932
|
+
uat = re.sub(r"\s+", " ", texts["references/uat.md"])
|
|
933
|
+
roadmap = re.sub(r"\s+", " ", texts["references/roadmap.md"])
|
|
934
|
+
core_normalized = re.sub(r"\s+", " ", core)
|
|
935
|
+
execution_profile_match = re.search(
|
|
936
|
+
r"^## Execution Profile Gate[ \t]*\n([\s\S]*?)(?=^## |\Z)",
|
|
937
|
+
core,
|
|
938
|
+
re.MULTILINE,
|
|
939
|
+
)
|
|
940
|
+
if not execution_profile_match:
|
|
941
|
+
findings.append("core-runtime.md lacks the execution profile gate owner section")
|
|
942
|
+
else:
|
|
943
|
+
execution_profile_owner = re.sub(
|
|
944
|
+
r"\s+", " ", execution_profile_match.group(1)
|
|
945
|
+
)
|
|
946
|
+
if EXECUTION_PROFILE_OWNER_MARKER not in execution_profile_owner:
|
|
947
|
+
findings.append("execution profile marker is outside its owner section")
|
|
948
|
+
for label, required in EXECUTION_PROFILE_REQUIREMENTS.items():
|
|
949
|
+
if required not in execution_profile_owner:
|
|
950
|
+
findings.append(f"execution profile owner missing {label}")
|
|
951
|
+
public_runtime_match = re.search(
|
|
952
|
+
r"^## Public Runtime Language Boundary[ \t]*\n([\s\S]*?)(?=^## |\Z)",
|
|
953
|
+
core,
|
|
954
|
+
re.MULTILINE,
|
|
955
|
+
)
|
|
956
|
+
if not public_runtime_match:
|
|
957
|
+
findings.append("core-runtime.md lacks the public runtime language boundary owner section")
|
|
958
|
+
else:
|
|
959
|
+
public_runtime_owner = re.sub(r"\s+", " ", public_runtime_match.group(1))
|
|
960
|
+
if PUBLIC_RUNTIME_LANGUAGE_OWNER_MARKER not in public_runtime_owner:
|
|
961
|
+
findings.append("public runtime language marker is outside its owner section")
|
|
962
|
+
for label, required in PUBLIC_RUNTIME_LANGUAGE_REQUIREMENTS.items():
|
|
963
|
+
if required not in public_runtime_owner:
|
|
964
|
+
findings.append(f"public runtime language owner missing {label}")
|
|
965
|
+
preflight_match = re.search(
|
|
966
|
+
r"^## Controller Preflight[ \t]*\n([\s\S]*?)(?=^## Startup|\Z)",
|
|
967
|
+
core,
|
|
968
|
+
re.MULTILINE,
|
|
969
|
+
)
|
|
970
|
+
if not preflight_match:
|
|
971
|
+
findings.append("core-runtime.md lacks the Controller preflight owner section")
|
|
972
|
+
else:
|
|
973
|
+
preflight_owner = re.sub(r"\s+", " ", preflight_match.group(1))
|
|
974
|
+
if TRUTH_SOURCE_INTAKE_OWNER_MARKER not in preflight_owner:
|
|
975
|
+
findings.append("truth-source intake marker is outside Controller preflight")
|
|
976
|
+
for label, required in TRUTH_SOURCE_INTAKE_REQUIREMENTS.items():
|
|
977
|
+
if required not in preflight_owner:
|
|
978
|
+
findings.append(f"truth-source intake owner missing {label}")
|
|
979
|
+
unexpected_failure_match = re.search(
|
|
980
|
+
r"^## Execution Loop[ \t]*\n([\s\S]*?)(?=^## |\Z)", core, re.MULTILINE
|
|
981
|
+
)
|
|
982
|
+
if not unexpected_failure_match:
|
|
983
|
+
findings.append("core-runtime.md lacks the execution-loop owner section")
|
|
984
|
+
else:
|
|
985
|
+
unexpected_failure_owner = re.sub(
|
|
986
|
+
r"\s+", " ", unexpected_failure_match.group(1)
|
|
987
|
+
)
|
|
988
|
+
if UNEXPECTED_FAILURE_OWNER_MARKER not in unexpected_failure_owner:
|
|
989
|
+
findings.append("unexpected-failure gate marker is outside its owner section")
|
|
990
|
+
for label, required in UNEXPECTED_FAILURE_REQUIREMENTS.items():
|
|
991
|
+
if required not in unexpected_failure_owner:
|
|
992
|
+
findings.append(f"unexpected-failure gate owner missing {label}")
|
|
993
|
+
outcome_anchor_match = re.search(
|
|
994
|
+
r"^## Outcome Anchor And Progress Gate[ \t]*\n([\s\S]*?)(?=^## |\Z)",
|
|
995
|
+
core,
|
|
996
|
+
re.MULTILINE,
|
|
997
|
+
)
|
|
998
|
+
if not outcome_anchor_match:
|
|
999
|
+
findings.append("core-runtime.md lacks the outcome-anchor progress owner section")
|
|
1000
|
+
else:
|
|
1001
|
+
outcome_anchor_owner = re.sub(r"\s+", " ", outcome_anchor_match.group(1))
|
|
1002
|
+
if DRIFT_CHECKPOINT_OWNER_MARKER not in outcome_anchor_owner:
|
|
1003
|
+
findings.append("drift checkpoint marker is outside outcome-anchor progress section")
|
|
1004
|
+
if RESULT_DISPOSITION_OWNER_MARKER not in outcome_anchor_owner:
|
|
1005
|
+
findings.append("result disposition marker is outside outcome-anchor progress section")
|
|
1006
|
+
for label, required in DRIFT_CHECKPOINT_REQUIREMENTS.items():
|
|
1007
|
+
if required not in outcome_anchor_owner:
|
|
1008
|
+
findings.append(f"drift checkpoint owner missing {label}")
|
|
1009
|
+
for label, required in RESULT_DISPOSITION_REQUIREMENTS.items():
|
|
1010
|
+
if required not in outcome_anchor_owner:
|
|
1011
|
+
findings.append(f"result disposition owner missing {label}")
|
|
1012
|
+
self_contained_match = re.search(
|
|
1013
|
+
r"^## Self-Contained Dispatch[ \t]*\n([\s\S]*?)(?=^## |\Z)",
|
|
1014
|
+
core,
|
|
1015
|
+
re.MULTILINE,
|
|
1016
|
+
)
|
|
1017
|
+
if not self_contained_match:
|
|
1018
|
+
findings.append("core-runtime.md lacks the self-contained dispatch section")
|
|
1019
|
+
else:
|
|
1020
|
+
self_contained_owner = re.sub(r"\s+", " ", self_contained_match.group(1))
|
|
1021
|
+
for label, required in SENDABLE_PACKET_REQUIREMENTS.items():
|
|
1022
|
+
if required not in self_contained_owner:
|
|
1023
|
+
findings.append(f"sendable-packet gate missing {label}")
|
|
1024
|
+
message_identity_match = re.search(
|
|
1025
|
+
r"^## Ambiguous Tool Outcomes, Role Reconciliation, And Batch Deduplication[ \t]*\n([\s\S]*?)(?=^## |\Z)",
|
|
1026
|
+
core,
|
|
1027
|
+
re.MULTILINE,
|
|
1028
|
+
)
|
|
1029
|
+
if not message_identity_match:
|
|
1030
|
+
findings.append("core-runtime.md lacks the message-identity boundary section")
|
|
1031
|
+
else:
|
|
1032
|
+
message_identity_owner = re.sub(r"\s+", " ", message_identity_match.group(1))
|
|
1033
|
+
for index, required in enumerate(MESSAGE_ID_BOUNDARY_REQUIREMENTS):
|
|
1034
|
+
if required not in message_identity_owner:
|
|
1035
|
+
findings.append(f"message-identity boundary missing requirement_{index}")
|
|
1036
|
+
for label, forbidden in MESSAGE_ID_FORBIDDEN.items():
|
|
1037
|
+
if forbidden in message_identity_owner:
|
|
1038
|
+
findings.append(f"message-identity fixed contradiction present {label}")
|
|
1039
|
+
for index, required in enumerate(LIVING_BRIEF_REQUIREMENTS):
|
|
1040
|
+
if required not in core_normalized:
|
|
1041
|
+
findings.append(f"living-brief runtime missing requirement_{index}")
|
|
1042
|
+
for label, forbidden in LIVING_BRIEF_FORBIDDEN.items():
|
|
1043
|
+
if forbidden in core_normalized:
|
|
1044
|
+
findings.append(f"living-brief fixed contradiction present {label}")
|
|
1045
|
+
for label, forbidden in UNEXPECTED_FAILURE_FORBIDDEN.items():
|
|
1046
|
+
if forbidden in normalized_markdown:
|
|
1047
|
+
findings.append(f"unexpected-failure fixed contradiction present {label}")
|
|
1048
|
+
for label, required in DELIVERY_REQUIREMENTS.items():
|
|
1049
|
+
if required not in core_normalized:
|
|
1050
|
+
findings.append(f"delivery gate missing {label}")
|
|
1051
|
+
for label, required in DELIVERY_UAT_REQUIREMENTS.items():
|
|
1052
|
+
if required not in uat:
|
|
1053
|
+
findings.append(f"uat.md missing delivery counterexample {label}")
|
|
1054
|
+
for label, forbidden in SENDABLE_PACKET_FORBIDDEN.items():
|
|
1055
|
+
if forbidden in normalized_markdown:
|
|
1056
|
+
findings.append(f"sendable-packet fixed contradiction present {label}")
|
|
1057
|
+
for label, forbidden in TRUTH_SOURCE_INTAKE_FORBIDDEN.items():
|
|
1058
|
+
if forbidden in normalized_markdown:
|
|
1059
|
+
findings.append(f"truth-source intake fixed contradiction present {label}")
|
|
1060
|
+
for index, required in enumerate(OUTCOME_ANCHOR_REQUIREMENTS):
|
|
1061
|
+
if required not in core_normalized:
|
|
1062
|
+
findings.append(f"outcome-anchor runtime missing requirement_{index}")
|
|
1063
|
+
for label, forbidden in OUTCOME_ANCHOR_FORBIDDEN.items():
|
|
1064
|
+
if forbidden in normalized_markdown:
|
|
1065
|
+
findings.append(f"outcome-anchor fixed contradiction present {label}")
|
|
1066
|
+
for label, forbidden in DRIFT_CHECKPOINT_FORBIDDEN.items():
|
|
1067
|
+
if forbidden in normalized_markdown:
|
|
1068
|
+
findings.append(f"drift-checkpoint fixed contradiction present {label}")
|
|
1069
|
+
for label, forbidden in RESULT_DISPOSITION_FORBIDDEN.items():
|
|
1070
|
+
if forbidden in normalized_markdown:
|
|
1071
|
+
findings.append(f"result-disposition fixed contradiction present {label}")
|
|
1072
|
+
for label, forbidden in EXECUTION_PROFILE_FORBIDDEN.items():
|
|
1073
|
+
if forbidden in normalized_markdown:
|
|
1074
|
+
findings.append(f"execution-profile fixed contradiction present {label}")
|
|
1075
|
+
for label, forbidden in PUBLIC_RUNTIME_LANGUAGE_FORBIDDEN.items():
|
|
1076
|
+
if forbidden in normalized_markdown:
|
|
1077
|
+
findings.append(f"public runtime language fixed contradiction present {label}")
|
|
1078
|
+
if "[parallel-producers.md](references/parallel-producers.md)" not in skill:
|
|
1079
|
+
findings.append("SKILL.md lacks direct progressive-disclosure route")
|
|
1080
|
+
if "The long-task drift checkpoint is owned only" not in skill:
|
|
1081
|
+
findings.append("SKILL.md lacks drift-checkpoint owner pointer")
|
|
1082
|
+
if "`/CER-auto` route selection, recheck, and safe transition are owned only by" not in skill:
|
|
1083
|
+
findings.append("SKILL.md lacks execution-profile owner pointer")
|
|
1084
|
+
if "that owner's single bounded-read requirement" not in re.sub(r"\s+", " ", skill):
|
|
1085
|
+
findings.append("SKILL.md lacks the selector single-read owner pointer")
|
|
1086
|
+
if "[Parallel Candidate Producers](parallel-producers.md)" not in core:
|
|
1087
|
+
findings.append("core-runtime role summary lacks owner pointer")
|
|
1088
|
+
if "CER has only the formal roles C, E1, R, and E2" not in core:
|
|
1089
|
+
findings.append("core-runtime formal role boundary is incomplete")
|
|
1090
|
+
fifth_role_patterns = (
|
|
1091
|
+
r"CER\s+has\s+only\s+the\s+formal\s+roles\s+C,\s*E1,\s*R,\s*E2,\s*P",
|
|
1092
|
+
r"(?:producer|candidate producer|P)\s+is\s+(?:a\s+)?formal\s+(?:CER\s+)?role",
|
|
1093
|
+
r"(?:fifth|5th)\s+(?:CER\s+)?formal\s+role",
|
|
1094
|
+
)
|
|
1095
|
+
for pattern in fifth_role_patterns:
|
|
1096
|
+
if re.search(pattern, all_markdown, flags=re.IGNORECASE):
|
|
1097
|
+
findings.append("a fifth formal CER role must not be declared")
|
|
1098
|
+
break
|
|
1099
|
+
if "## Parallel Candidate Producer Counterexamples" not in uat:
|
|
1100
|
+
findings.append("uat.md lacks bounded producer counterexamples")
|
|
1101
|
+
for label, required in UAT_REQUIREMENTS.items():
|
|
1102
|
+
if required not in uat:
|
|
1103
|
+
findings.append(f"uat.md missing producer counterexample {label}")
|
|
1104
|
+
for label, required in SENDABLE_PACKET_UAT_REQUIREMENTS.items():
|
|
1105
|
+
if required not in uat:
|
|
1106
|
+
findings.append(f"uat.md missing sendable-packet counterexample {label}")
|
|
1107
|
+
for label, required in MESSAGE_ID_UAT_REQUIREMENTS.items():
|
|
1108
|
+
if required not in uat:
|
|
1109
|
+
findings.append(f"uat.md missing message-identity counterexample {label}")
|
|
1110
|
+
for label, required in LIVING_BRIEF_UAT_REQUIREMENTS.items():
|
|
1111
|
+
if required not in uat:
|
|
1112
|
+
findings.append(f"uat.md missing living-brief counterexample {label}")
|
|
1113
|
+
for label, required in TRUTH_SOURCE_INTAKE_UAT_REQUIREMENTS.items():
|
|
1114
|
+
if required not in uat:
|
|
1115
|
+
findings.append(f"uat.md missing truth-source intake counterexample {label}")
|
|
1116
|
+
for label, required in CONTROLLER_CHALLENGE_UAT_REQUIREMENTS.items():
|
|
1117
|
+
if required not in texts["references/uat.md"]:
|
|
1118
|
+
findings.append(f"uat.md missing Controller long-task challenge {label}")
|
|
1119
|
+
if "## Outcome Anchor And Progress Scenarios" not in texts["references/uat.md"]:
|
|
1120
|
+
findings.append("uat.md lacks outcome-anchor progress scenarios")
|
|
1121
|
+
for label, required in OUTCOME_ANCHOR_UAT_REQUIREMENTS.items():
|
|
1122
|
+
if required not in uat:
|
|
1123
|
+
findings.append(f"uat.md missing outcome-anchor counterexample {label}")
|
|
1124
|
+
for label, required in DRIFT_CHECKPOINT_UAT_REQUIREMENTS.items():
|
|
1125
|
+
if required not in uat:
|
|
1126
|
+
findings.append(f"uat.md missing drift-checkpoint counterexample {label}")
|
|
1127
|
+
for label, required in RESULT_DISPOSITION_UAT_REQUIREMENTS.items():
|
|
1128
|
+
if required not in uat:
|
|
1129
|
+
findings.append(f"uat.md missing result-disposition counterexample {label}")
|
|
1130
|
+
if "## Adaptive Execution Profile Scenarios" not in texts["references/uat.md"]:
|
|
1131
|
+
findings.append("uat.md lacks adaptive execution profile scenarios")
|
|
1132
|
+
for label, required in EXECUTION_PROFILE_UAT_REQUIREMENTS.items():
|
|
1133
|
+
if required not in uat:
|
|
1134
|
+
findings.append(f"uat.md missing execution-profile scenario {label}")
|
|
1135
|
+
if "## Public Runtime Language Boundary Scenarios" not in texts["references/uat.md"]:
|
|
1136
|
+
findings.append("uat.md lacks public runtime language boundary scenarios")
|
|
1137
|
+
for label, required in PUBLIC_RUNTIME_LANGUAGE_UAT_REQUIREMENTS.items():
|
|
1138
|
+
if required not in uat:
|
|
1139
|
+
findings.append(f"uat.md missing public runtime language scenario {label}")
|
|
1140
|
+
unexpected_failure_uat_match = re.search(
|
|
1141
|
+
r"^## Unexpected Failure And Scope-Exception Scenarios[ \t]*\n([\s\S]*?)(?=^## |\Z)",
|
|
1142
|
+
texts["references/uat.md"],
|
|
1143
|
+
re.MULTILINE,
|
|
1144
|
+
)
|
|
1145
|
+
if not unexpected_failure_uat_match:
|
|
1146
|
+
findings.append("uat.md lacks unexpected-failure scope-exception scenarios")
|
|
1147
|
+
else:
|
|
1148
|
+
unexpected_failure_uat = unexpected_failure_uat_match.group(1)
|
|
1149
|
+
for label, marker in UNEXPECTED_FAILURE_UAT_MARKERS.items():
|
|
1150
|
+
if all_markdown.count(marker) != 1 or marker not in unexpected_failure_uat:
|
|
1151
|
+
findings.append(f"unexpected-failure UAT marker invalid {label}")
|
|
1152
|
+
if "Parallel candidate producers are C's internal on-demand capability. They do not enter role columns" not in roadmap:
|
|
1153
|
+
findings.append("roadmap.md lacks display-only producer boundary")
|
|
1154
|
+
for index, required in enumerate(LIVING_BRIEF_ROADMAP_REQUIREMENTS):
|
|
1155
|
+
if required not in roadmap:
|
|
1156
|
+
findings.append(f"roadmap.md missing living-brief display requirement_{index}")
|
|
1157
|
+
for index, required in enumerate(OUTCOME_ANCHOR_ROADMAP_REQUIREMENTS):
|
|
1158
|
+
if required not in roadmap:
|
|
1159
|
+
findings.append(f"roadmap.md missing outcome-anchor display requirement_{index}")
|
|
1160
|
+
for relative in (
|
|
1161
|
+
"references/core-runtime.md",
|
|
1162
|
+
"references/roadmap.md",
|
|
1163
|
+
"references/uat.md",
|
|
1164
|
+
"references/parallel-producers.md",
|
|
1165
|
+
):
|
|
1166
|
+
if "## Contents" not in texts[relative]:
|
|
1167
|
+
findings.append(f"long reference lacks concise table of contents: {relative}")
|
|
1168
|
+
if "{package_version}" not in roadmap or "{package_version}" not in uat:
|
|
1169
|
+
findings.append("card/UAT templates lack package_version placeholder")
|
|
1170
|
+
if "never display the placeholder itself" not in roadmap:
|
|
1171
|
+
findings.append("roadmap lacks mandatory package_version substitution boundary")
|
|
1172
|
+
if re.search(r"(?:formal roles|role columns).{0,40}(?:producer).{0,20}(?:enter|add|include)", roadmap, re.IGNORECASE):
|
|
1173
|
+
findings.append("roadmap adds producer to formal role display")
|
|
1174
|
+
return findings
|
|
1175
|
+
|
|
1176
|
+
|
|
1177
|
+
def validate(root: Path) -> list[str]:
|
|
1178
|
+
root = root.resolve()
|
|
1179
|
+
actual_files = {
|
|
1180
|
+
path.relative_to(root).as_posix()
|
|
1181
|
+
for path in root.rglob("*")
|
|
1182
|
+
if path.is_file()
|
|
1183
|
+
}
|
|
1184
|
+
unexpected = sorted(actual_files - EXPECTED_FILES)
|
|
1185
|
+
if unexpected:
|
|
1186
|
+
return [f"unexpected package files: {unexpected}"]
|
|
1187
|
+
findings = validate_texts(root, read_texts(root))
|
|
1188
|
+
router_bytes = len((root / "SKILL.md").read_bytes())
|
|
1189
|
+
if router_bytes > MAX_ROUTER_BYTES:
|
|
1190
|
+
findings.append(
|
|
1191
|
+
f"SKILL.md concise-router budget exceeded: {router_bytes} > {MAX_ROUTER_BYTES} bytes"
|
|
1192
|
+
)
|
|
1193
|
+
return findings
|
|
1194
|
+
|
|
1195
|
+
|
|
1196
|
+
def mutation_matrix(root: Path) -> tuple[int, list[str]]:
|
|
1197
|
+
baseline = read_texts(root)
|
|
1198
|
+
failures: list[str] = []
|
|
1199
|
+
cases: list[tuple[str, dict[str, str]]] = []
|
|
1200
|
+
|
|
1201
|
+
def mutated(relative: str, old: str, new: str = "") -> dict[str, str]:
|
|
1202
|
+
candidate = copy.deepcopy(baseline)
|
|
1203
|
+
if old not in candidate[relative]:
|
|
1204
|
+
raise RuntimeError(f"self-test anchor missing: {relative}: {old}")
|
|
1205
|
+
candidate[relative] = candidate[relative].replace(old, new, 1)
|
|
1206
|
+
return candidate
|
|
1207
|
+
|
|
1208
|
+
def mutated_all(relative: str, old: str, new: str = "") -> dict[str, str]:
|
|
1209
|
+
candidate = copy.deepcopy(baseline)
|
|
1210
|
+
if old not in candidate[relative]:
|
|
1211
|
+
raise RuntimeError(f"self-test anchor missing: {relative}: {old}")
|
|
1212
|
+
candidate[relative] = candidate[relative].replace(old, new)
|
|
1213
|
+
return candidate
|
|
1214
|
+
|
|
1215
|
+
def mutated_fragment(relative: str, fragment: str) -> dict[str, str]:
|
|
1216
|
+
candidate = copy.deepcopy(baseline)
|
|
1217
|
+
pattern = re.escape(fragment).replace(r"\ ", r"\s+")
|
|
1218
|
+
changed, count = re.subn(pattern, "", candidate[relative])
|
|
1219
|
+
if count < 1:
|
|
1220
|
+
raise RuntimeError(f"self-test normalized anchor missing: {relative}: {fragment}")
|
|
1221
|
+
candidate[relative] = changed
|
|
1222
|
+
return candidate
|
|
1223
|
+
|
|
1224
|
+
cases.append(("version_invalid", mutated("VERSION", baseline["VERSION"], "version\n")))
|
|
1225
|
+
cases.append(
|
|
1226
|
+
(
|
|
1227
|
+
"semver_outside_version",
|
|
1228
|
+
mutated(
|
|
1229
|
+
"references/roadmap.md",
|
|
1230
|
+
"# User Checkpoints And Roadmap",
|
|
1231
|
+
"# User Checkpoints And Roadmap " + ".".join(("9", "9", "9")),
|
|
1232
|
+
),
|
|
1233
|
+
)
|
|
1234
|
+
)
|
|
1235
|
+
cases.append(("frontmatter_extra", mutated("SKILL.md", "name: cer-workflow-en", "name: cer-workflow-en\nmetadata: bad")))
|
|
1236
|
+
cases.append(
|
|
1237
|
+
(
|
|
1238
|
+
"extra_slash_command",
|
|
1239
|
+
mutated(
|
|
1240
|
+
"SKILL.md",
|
|
1241
|
+
"| `/CER-help`",
|
|
1242
|
+
"| `/CER-producer` | `producer` | forbidden |\n| `/CER-help`",
|
|
1243
|
+
),
|
|
1244
|
+
)
|
|
1245
|
+
)
|
|
1246
|
+
cases.append(("owner_marker_duplicate", mutated("SKILL.md", "# CER Workflow", f"# CER Workflow\n{OWNER_MARKER}")))
|
|
1247
|
+
cases.append(("owner_marker_missing", mutated("references/parallel-producers.md", OWNER_MARKER)))
|
|
1248
|
+
cases.append(
|
|
1249
|
+
(
|
|
1250
|
+
"unexpected_failure_owner_marker_duplicate",
|
|
1251
|
+
mutated(
|
|
1252
|
+
"SKILL.md",
|
|
1253
|
+
"# CER Workflow",
|
|
1254
|
+
f"# CER Workflow\n{UNEXPECTED_FAILURE_OWNER_MARKER}",
|
|
1255
|
+
),
|
|
1256
|
+
)
|
|
1257
|
+
)
|
|
1258
|
+
cases.append(
|
|
1259
|
+
(
|
|
1260
|
+
"unexpected_failure_owner_marker_missing",
|
|
1261
|
+
mutated("references/core-runtime.md", UNEXPECTED_FAILURE_OWNER_MARKER),
|
|
1262
|
+
)
|
|
1263
|
+
)
|
|
1264
|
+
wrong_section_marker = mutated(
|
|
1265
|
+
"references/core-runtime.md", UNEXPECTED_FAILURE_OWNER_MARKER
|
|
1266
|
+
)
|
|
1267
|
+
wrong_section_marker["references/core-runtime.md"] = wrong_section_marker[
|
|
1268
|
+
"references/core-runtime.md"
|
|
1269
|
+
].replace(
|
|
1270
|
+
"## YAGNI And Stop",
|
|
1271
|
+
f"{UNEXPECTED_FAILURE_OWNER_MARKER}\n## YAGNI And Stop",
|
|
1272
|
+
1,
|
|
1273
|
+
)
|
|
1274
|
+
cases.append(("unexpected_failure_owner_marker_wrong_section", wrong_section_marker))
|
|
1275
|
+
cases.append(
|
|
1276
|
+
(
|
|
1277
|
+
"drift_checkpoint_owner_marker_duplicate",
|
|
1278
|
+
mutated(
|
|
1279
|
+
"SKILL.md",
|
|
1280
|
+
"# CER Workflow",
|
|
1281
|
+
f"# CER Workflow\n{DRIFT_CHECKPOINT_OWNER_MARKER}",
|
|
1282
|
+
),
|
|
1283
|
+
)
|
|
1284
|
+
)
|
|
1285
|
+
cases.append(
|
|
1286
|
+
(
|
|
1287
|
+
"drift_checkpoint_owner_marker_missing",
|
|
1288
|
+
mutated("references/core-runtime.md", DRIFT_CHECKPOINT_OWNER_MARKER),
|
|
1289
|
+
)
|
|
1290
|
+
)
|
|
1291
|
+
drift_wrong_section = mutated(
|
|
1292
|
+
"references/core-runtime.md", DRIFT_CHECKPOINT_OWNER_MARKER
|
|
1293
|
+
)
|
|
1294
|
+
drift_wrong_section["references/core-runtime.md"] = drift_wrong_section[
|
|
1295
|
+
"references/core-runtime.md"
|
|
1296
|
+
].replace(
|
|
1297
|
+
"## YAGNI And Stop",
|
|
1298
|
+
f"{DRIFT_CHECKPOINT_OWNER_MARKER}\n## YAGNI And Stop",
|
|
1299
|
+
1,
|
|
1300
|
+
)
|
|
1301
|
+
cases.append(("drift_checkpoint_owner_marker_wrong_section", drift_wrong_section))
|
|
1302
|
+
cases.append(
|
|
1303
|
+
(
|
|
1304
|
+
"result_disposition_owner_marker_duplicate",
|
|
1305
|
+
mutated(
|
|
1306
|
+
"SKILL.md",
|
|
1307
|
+
"# CER Workflow",
|
|
1308
|
+
f"# CER Workflow\n{RESULT_DISPOSITION_OWNER_MARKER}",
|
|
1309
|
+
),
|
|
1310
|
+
)
|
|
1311
|
+
)
|
|
1312
|
+
cases.append(
|
|
1313
|
+
(
|
|
1314
|
+
"result_disposition_owner_marker_missing",
|
|
1315
|
+
mutated("references/core-runtime.md", RESULT_DISPOSITION_OWNER_MARKER),
|
|
1316
|
+
)
|
|
1317
|
+
)
|
|
1318
|
+
result_disposition_wrong_section = mutated(
|
|
1319
|
+
"references/core-runtime.md", RESULT_DISPOSITION_OWNER_MARKER
|
|
1320
|
+
)
|
|
1321
|
+
result_disposition_wrong_section["references/core-runtime.md"] = result_disposition_wrong_section[
|
|
1322
|
+
"references/core-runtime.md"
|
|
1323
|
+
].replace(
|
|
1324
|
+
"## Self-Contained Dispatch",
|
|
1325
|
+
f"{RESULT_DISPOSITION_OWNER_MARKER}\n## Self-Contained Dispatch",
|
|
1326
|
+
1,
|
|
1327
|
+
)
|
|
1328
|
+
cases.append(("result_disposition_owner_marker_wrong_section", result_disposition_wrong_section))
|
|
1329
|
+
cases.append(
|
|
1330
|
+
(
|
|
1331
|
+
"execution_profile_owner_marker_duplicate",
|
|
1332
|
+
mutated(
|
|
1333
|
+
"SKILL.md",
|
|
1334
|
+
"# CER Workflow",
|
|
1335
|
+
f"# CER Workflow\n{EXECUTION_PROFILE_OWNER_MARKER}",
|
|
1336
|
+
),
|
|
1337
|
+
)
|
|
1338
|
+
)
|
|
1339
|
+
cases.append(
|
|
1340
|
+
(
|
|
1341
|
+
"execution_profile_owner_marker_missing",
|
|
1342
|
+
mutated("references/core-runtime.md", EXECUTION_PROFILE_OWNER_MARKER),
|
|
1343
|
+
)
|
|
1344
|
+
)
|
|
1345
|
+
execution_profile_wrong_section = mutated(
|
|
1346
|
+
"references/core-runtime.md", EXECUTION_PROFILE_OWNER_MARKER
|
|
1347
|
+
)
|
|
1348
|
+
execution_profile_wrong_section["references/core-runtime.md"] = execution_profile_wrong_section[
|
|
1349
|
+
"references/core-runtime.md"
|
|
1350
|
+
].replace(
|
|
1351
|
+
"## YAGNI And Stop",
|
|
1352
|
+
f"{EXECUTION_PROFILE_OWNER_MARKER}\n## YAGNI And Stop",
|
|
1353
|
+
1,
|
|
1354
|
+
)
|
|
1355
|
+
cases.append(("execution_profile_owner_marker_wrong_section", execution_profile_wrong_section))
|
|
1356
|
+
cases.append(
|
|
1357
|
+
(
|
|
1358
|
+
"implicit_invocation_true",
|
|
1359
|
+
mutated("agents/openai.yaml", "allow_implicit_invocation: false", "allow_implicit_invocation: true"),
|
|
1360
|
+
)
|
|
1361
|
+
)
|
|
1362
|
+
cases.append(("default_prompt_missing_skill", mutated("agents/openai.yaml", "$cer-workflow-en", "CER")))
|
|
1363
|
+
cases.append(
|
|
1364
|
+
(
|
|
1365
|
+
"default_prompt_unconditional_reviewer",
|
|
1366
|
+
mutated(
|
|
1367
|
+
"agents/openai.yaml",
|
|
1368
|
+
"with one writer for this work; create a fresh Reviewer in proportion to risk",
|
|
1369
|
+
"with one writer and a fresh Reviewer for this work",
|
|
1370
|
+
),
|
|
1371
|
+
)
|
|
1372
|
+
)
|
|
1373
|
+
for label, counterexample in REVIEWER_PROPORTIONALITY_COUNTEREXAMPLES.items():
|
|
1374
|
+
cases.append(
|
|
1375
|
+
(
|
|
1376
|
+
f"default_prompt_{label}",
|
|
1377
|
+
mutated(
|
|
1378
|
+
"agents/openai.yaml",
|
|
1379
|
+
"without extra setup",
|
|
1380
|
+
f"without extra setup; {counterexample}",
|
|
1381
|
+
),
|
|
1382
|
+
)
|
|
1383
|
+
)
|
|
1384
|
+
cases.append(
|
|
1385
|
+
(
|
|
1386
|
+
"user_prompt_exposes_setup",
|
|
1387
|
+
mutated(
|
|
1388
|
+
"agents/openai.yaml",
|
|
1389
|
+
"without extra setup",
|
|
1390
|
+
"after configuring producer lanes, scratch roots, and hashes",
|
|
1391
|
+
),
|
|
1392
|
+
)
|
|
1393
|
+
)
|
|
1394
|
+
cases.append(
|
|
1395
|
+
(
|
|
1396
|
+
"trigger_frontmatter_plain_close_reversed",
|
|
1397
|
+
mutated(
|
|
1398
|
+
"SKILL.md",
|
|
1399
|
+
"Plain start/work or close/finish messages are not CER triggers",
|
|
1400
|
+
"Plain close/finish messages are CER close triggers",
|
|
1401
|
+
),
|
|
1402
|
+
)
|
|
1403
|
+
)
|
|
1404
|
+
cases.append(
|
|
1405
|
+
(
|
|
1406
|
+
"trigger_skill_auto_row_reversed",
|
|
1407
|
+
mutated(
|
|
1408
|
+
"SKILL.md",
|
|
1409
|
+
"no C exists before the route decision",
|
|
1410
|
+
"C exists before the route decision",
|
|
1411
|
+
),
|
|
1412
|
+
)
|
|
1413
|
+
)
|
|
1414
|
+
cases.append(
|
|
1415
|
+
(
|
|
1416
|
+
"trigger_core_auto_row_reversed",
|
|
1417
|
+
mutated(
|
|
1418
|
+
"references/core-runtime.md",
|
|
1419
|
+
"no C exists before the route decision",
|
|
1420
|
+
"C exists before the route decision",
|
|
1421
|
+
),
|
|
1422
|
+
)
|
|
1423
|
+
)
|
|
1424
|
+
cases.append(
|
|
1425
|
+
(
|
|
1426
|
+
"trigger_uat_install_auto_reversed",
|
|
1427
|
+
mutated(
|
|
1428
|
+
"references/uat.md",
|
|
1429
|
+
"no C exists before the route decision",
|
|
1430
|
+
"C exists before the route decision",
|
|
1431
|
+
),
|
|
1432
|
+
)
|
|
1433
|
+
)
|
|
1434
|
+
cases.append(
|
|
1435
|
+
(
|
|
1436
|
+
"trigger_skill_start_row_reversed",
|
|
1437
|
+
mutated(
|
|
1438
|
+
"SKILL.md",
|
|
1439
|
+
"Plain start/work messages do not start CER",
|
|
1440
|
+
"Plain start/work messages start CER",
|
|
1441
|
+
),
|
|
1442
|
+
)
|
|
1443
|
+
)
|
|
1444
|
+
cases.append(
|
|
1445
|
+
(
|
|
1446
|
+
"trigger_skill_close_row_reversed",
|
|
1447
|
+
mutated(
|
|
1448
|
+
"SKILL.md",
|
|
1449
|
+
"Plain close/finish messages do not close CER",
|
|
1450
|
+
"Plain close/finish messages close CER",
|
|
1451
|
+
),
|
|
1452
|
+
)
|
|
1453
|
+
)
|
|
1454
|
+
cases.append(
|
|
1455
|
+
(
|
|
1456
|
+
"trigger_core_start_row_reversed",
|
|
1457
|
+
mutated(
|
|
1458
|
+
"references/core-runtime.md",
|
|
1459
|
+
"Plain start/work messages do not start CER",
|
|
1460
|
+
"Plain start/work messages start CER",
|
|
1461
|
+
),
|
|
1462
|
+
)
|
|
1463
|
+
)
|
|
1464
|
+
cases.append(
|
|
1465
|
+
(
|
|
1466
|
+
"trigger_core_close_row_reversed",
|
|
1467
|
+
mutated(
|
|
1468
|
+
"references/core-runtime.md",
|
|
1469
|
+
"Plain close/finish messages do not close CER",
|
|
1470
|
+
"Plain close/finish messages close CER",
|
|
1471
|
+
),
|
|
1472
|
+
)
|
|
1473
|
+
)
|
|
1474
|
+
cases.append(
|
|
1475
|
+
(
|
|
1476
|
+
"trigger_core_startup_owner_reversed",
|
|
1477
|
+
mutated(
|
|
1478
|
+
"references/core-runtime.md",
|
|
1479
|
+
"Plain start/work messages belong to the target workspace's existing governance and are not CER triggers",
|
|
1480
|
+
"Plain start/work messages are CER triggers",
|
|
1481
|
+
),
|
|
1482
|
+
)
|
|
1483
|
+
)
|
|
1484
|
+
cases.append(
|
|
1485
|
+
(
|
|
1486
|
+
"trigger_core_stop_owner_reversed",
|
|
1487
|
+
mutated(
|
|
1488
|
+
"references/core-runtime.md",
|
|
1489
|
+
"Plain close/finish messages belong to the target workspace's existing governance and do not map to CER stop or close",
|
|
1490
|
+
"Plain close/finish messages belong to CER and map to CER stop or close",
|
|
1491
|
+
),
|
|
1492
|
+
)
|
|
1493
|
+
)
|
|
1494
|
+
cases.append(
|
|
1495
|
+
(
|
|
1496
|
+
"trigger_uat_install_start_reversed",
|
|
1497
|
+
mutated(
|
|
1498
|
+
"references/uat.md",
|
|
1499
|
+
"a plain start/work message does not",
|
|
1500
|
+
"a plain start/work message does",
|
|
1501
|
+
),
|
|
1502
|
+
)
|
|
1503
|
+
)
|
|
1504
|
+
cases.append(
|
|
1505
|
+
(
|
|
1506
|
+
"trigger_uat_install_close_reversed",
|
|
1507
|
+
mutated(
|
|
1508
|
+
"references/uat.md",
|
|
1509
|
+
"a plain close/finish message does not close CER and does not map to `/CER-stop`",
|
|
1510
|
+
"a plain close/finish message closes CER and maps to `/CER-stop`",
|
|
1511
|
+
),
|
|
1512
|
+
)
|
|
1513
|
+
)
|
|
1514
|
+
cases.append(
|
|
1515
|
+
(
|
|
1516
|
+
"trigger_uat_failure_condition_lost",
|
|
1517
|
+
mutated(
|
|
1518
|
+
"references/uat.md",
|
|
1519
|
+
"A plain start/work message starts CER, or a plain close/finish message triggers CER close/stop",
|
|
1520
|
+
"A plain start/work message does not start CER, and a plain close/finish message does not trigger CER close/stop",
|
|
1521
|
+
),
|
|
1522
|
+
)
|
|
1523
|
+
)
|
|
1524
|
+
cases.append(
|
|
1525
|
+
(
|
|
1526
|
+
"unprovided_dependency",
|
|
1527
|
+
mutated("agents/openai.yaml", "policy:", "dependencies:\n tools: []\npolicy:"),
|
|
1528
|
+
)
|
|
1529
|
+
)
|
|
1530
|
+
cases.append(
|
|
1531
|
+
(
|
|
1532
|
+
"missing_progressive_link",
|
|
1533
|
+
mutated_all(
|
|
1534
|
+
"SKILL.md",
|
|
1535
|
+
"[parallel-producers.md](references/parallel-producers.md)",
|
|
1536
|
+
"parallel producers",
|
|
1537
|
+
),
|
|
1538
|
+
)
|
|
1539
|
+
)
|
|
1540
|
+
for label, fragment in OWNER_REQUIREMENTS.items():
|
|
1541
|
+
cases.append(
|
|
1542
|
+
(
|
|
1543
|
+
f"owner_missing_{label}",
|
|
1544
|
+
mutated_fragment("references/parallel-producers.md", fragment),
|
|
1545
|
+
)
|
|
1546
|
+
)
|
|
1547
|
+
for label, fragment in UAT_REQUIREMENTS.items():
|
|
1548
|
+
cases.append(
|
|
1549
|
+
(f"uat_missing_{label}", mutated_fragment("references/uat.md", fragment))
|
|
1550
|
+
)
|
|
1551
|
+
for label, fragment in UNEXPECTED_FAILURE_REQUIREMENTS.items():
|
|
1552
|
+
cases.append(
|
|
1553
|
+
(
|
|
1554
|
+
f"unexpected_failure_owner_missing_{label}",
|
|
1555
|
+
mutated_fragment("references/core-runtime.md", fragment),
|
|
1556
|
+
)
|
|
1557
|
+
)
|
|
1558
|
+
for label, fragment in DELIVERY_REQUIREMENTS.items():
|
|
1559
|
+
cases.append(
|
|
1560
|
+
(
|
|
1561
|
+
f"delivery_gate_missing_{label}",
|
|
1562
|
+
mutated_fragment("references/core-runtime.md", fragment),
|
|
1563
|
+
)
|
|
1564
|
+
)
|
|
1565
|
+
for label, fragment in DELIVERY_UAT_REQUIREMENTS.items():
|
|
1566
|
+
cases.append(
|
|
1567
|
+
(
|
|
1568
|
+
f"delivery_uat_missing_{label}",
|
|
1569
|
+
mutated_fragment("references/uat.md", fragment),
|
|
1570
|
+
)
|
|
1571
|
+
)
|
|
1572
|
+
for label, fragment in TRUTH_SOURCE_INTAKE_REQUIREMENTS.items():
|
|
1573
|
+
cases.append(
|
|
1574
|
+
(
|
|
1575
|
+
f"truth_source_intake_owner_missing_{label}",
|
|
1576
|
+
mutated_fragment("references/core-runtime.md", fragment),
|
|
1577
|
+
)
|
|
1578
|
+
)
|
|
1579
|
+
for label, fragment in DRIFT_CHECKPOINT_REQUIREMENTS.items():
|
|
1580
|
+
cases.append(
|
|
1581
|
+
(
|
|
1582
|
+
f"drift_checkpoint_owner_missing_{label}",
|
|
1583
|
+
mutated_fragment("references/core-runtime.md", fragment),
|
|
1584
|
+
)
|
|
1585
|
+
)
|
|
1586
|
+
for label, fragment in RESULT_DISPOSITION_REQUIREMENTS.items():
|
|
1587
|
+
cases.append(
|
|
1588
|
+
(
|
|
1589
|
+
f"result_disposition_owner_missing_{label}",
|
|
1590
|
+
mutated_fragment("references/core-runtime.md", fragment),
|
|
1591
|
+
)
|
|
1592
|
+
)
|
|
1593
|
+
for label, fragment in EXECUTION_PROFILE_REQUIREMENTS.items():
|
|
1594
|
+
cases.append(
|
|
1595
|
+
(
|
|
1596
|
+
f"execution_profile_owner_missing_{label}",
|
|
1597
|
+
mutated_fragment("references/core-runtime.md", fragment),
|
|
1598
|
+
)
|
|
1599
|
+
)
|
|
1600
|
+
for label, fragment in PUBLIC_RUNTIME_LANGUAGE_REQUIREMENTS.items():
|
|
1601
|
+
cases.append(
|
|
1602
|
+
(
|
|
1603
|
+
f"public_runtime_language_owner_missing_{label}",
|
|
1604
|
+
mutated_fragment("references/core-runtime.md", fragment),
|
|
1605
|
+
)
|
|
1606
|
+
)
|
|
1607
|
+
for label, fragment in SENDABLE_PACKET_REQUIREMENTS.items():
|
|
1608
|
+
cases.append(
|
|
1609
|
+
(
|
|
1610
|
+
f"sendable_packet_owner_missing_{label}",
|
|
1611
|
+
mutated_fragment("references/core-runtime.md", fragment),
|
|
1612
|
+
)
|
|
1613
|
+
)
|
|
1614
|
+
for index, fragment in enumerate(MESSAGE_ID_BOUNDARY_REQUIREMENTS):
|
|
1615
|
+
cases.append(
|
|
1616
|
+
(
|
|
1617
|
+
f"message_id_owner_missing_{index}",
|
|
1618
|
+
mutated_fragment("references/core-runtime.md", fragment),
|
|
1619
|
+
)
|
|
1620
|
+
)
|
|
1621
|
+
for index, fragment in enumerate(LIVING_BRIEF_REQUIREMENTS):
|
|
1622
|
+
cases.append(
|
|
1623
|
+
(
|
|
1624
|
+
f"living_brief_runtime_missing_{index}",
|
|
1625
|
+
mutated_fragment("references/core-runtime.md", fragment),
|
|
1626
|
+
)
|
|
1627
|
+
)
|
|
1628
|
+
for index, fragment in enumerate(LIVING_BRIEF_ROADMAP_REQUIREMENTS):
|
|
1629
|
+
cases.append(
|
|
1630
|
+
(
|
|
1631
|
+
f"living_brief_roadmap_missing_{index}",
|
|
1632
|
+
mutated_fragment("references/roadmap.md", fragment),
|
|
1633
|
+
)
|
|
1634
|
+
)
|
|
1635
|
+
for index, fragment in enumerate(OUTCOME_ANCHOR_REQUIREMENTS):
|
|
1636
|
+
cases.append(
|
|
1637
|
+
(
|
|
1638
|
+
f"outcome_anchor_runtime_missing_{index}",
|
|
1639
|
+
mutated_fragment("references/core-runtime.md", fragment),
|
|
1640
|
+
)
|
|
1641
|
+
)
|
|
1642
|
+
for index, fragment in enumerate(OUTCOME_ANCHOR_ROADMAP_REQUIREMENTS):
|
|
1643
|
+
cases.append(
|
|
1644
|
+
(
|
|
1645
|
+
f"outcome_anchor_roadmap_missing_{index}",
|
|
1646
|
+
mutated_fragment("references/roadmap.md", fragment),
|
|
1647
|
+
)
|
|
1648
|
+
)
|
|
1649
|
+
for label, fragment in SENDABLE_PACKET_UAT_REQUIREMENTS.items():
|
|
1650
|
+
cases.append(
|
|
1651
|
+
(
|
|
1652
|
+
f"sendable_packet_uat_missing_{label}",
|
|
1653
|
+
mutated_fragment("references/uat.md", fragment),
|
|
1654
|
+
)
|
|
1655
|
+
)
|
|
1656
|
+
for label, fragment in MESSAGE_ID_UAT_REQUIREMENTS.items():
|
|
1657
|
+
cases.append(
|
|
1658
|
+
(
|
|
1659
|
+
f"message_id_uat_missing_{label}",
|
|
1660
|
+
mutated_fragment("references/uat.md", fragment),
|
|
1661
|
+
)
|
|
1662
|
+
)
|
|
1663
|
+
for label, fragment in LIVING_BRIEF_UAT_REQUIREMENTS.items():
|
|
1664
|
+
cases.append(
|
|
1665
|
+
(
|
|
1666
|
+
f"living_brief_uat_missing_{label}",
|
|
1667
|
+
mutated_fragment("references/uat.md", fragment),
|
|
1668
|
+
)
|
|
1669
|
+
)
|
|
1670
|
+
for label, fragment in TRUTH_SOURCE_INTAKE_UAT_REQUIREMENTS.items():
|
|
1671
|
+
cases.append(
|
|
1672
|
+
(
|
|
1673
|
+
f"truth_source_intake_uat_missing_{label}",
|
|
1674
|
+
mutated_fragment("references/uat.md", fragment),
|
|
1675
|
+
)
|
|
1676
|
+
)
|
|
1677
|
+
for label, fragment in CONTROLLER_CHALLENGE_UAT_REQUIREMENTS.items():
|
|
1678
|
+
cases.append(
|
|
1679
|
+
(
|
|
1680
|
+
f"controller_challenge_uat_missing_{label}",
|
|
1681
|
+
mutated_fragment("references/uat.md", fragment),
|
|
1682
|
+
)
|
|
1683
|
+
)
|
|
1684
|
+
for label, fragment in OUTCOME_ANCHOR_UAT_REQUIREMENTS.items():
|
|
1685
|
+
cases.append(
|
|
1686
|
+
(
|
|
1687
|
+
f"outcome_anchor_uat_missing_{label}",
|
|
1688
|
+
mutated_fragment("references/uat.md", fragment),
|
|
1689
|
+
)
|
|
1690
|
+
)
|
|
1691
|
+
for label, fragment in DRIFT_CHECKPOINT_UAT_REQUIREMENTS.items():
|
|
1692
|
+
cases.append(
|
|
1693
|
+
(
|
|
1694
|
+
f"drift_checkpoint_uat_missing_{label}",
|
|
1695
|
+
mutated_fragment("references/uat.md", fragment),
|
|
1696
|
+
)
|
|
1697
|
+
)
|
|
1698
|
+
for label, fragment in RESULT_DISPOSITION_UAT_REQUIREMENTS.items():
|
|
1699
|
+
cases.append(
|
|
1700
|
+
(
|
|
1701
|
+
f"result_disposition_uat_missing_{label}",
|
|
1702
|
+
mutated_fragment("references/uat.md", fragment),
|
|
1703
|
+
)
|
|
1704
|
+
)
|
|
1705
|
+
for label, fragment in EXECUTION_PROFILE_UAT_REQUIREMENTS.items():
|
|
1706
|
+
cases.append(
|
|
1707
|
+
(
|
|
1708
|
+
f"execution_profile_uat_missing_{label}",
|
|
1709
|
+
mutated_fragment("references/uat.md", fragment),
|
|
1710
|
+
)
|
|
1711
|
+
)
|
|
1712
|
+
for label, fragment in PUBLIC_RUNTIME_LANGUAGE_UAT_REQUIREMENTS.items():
|
|
1713
|
+
cases.append(
|
|
1714
|
+
(
|
|
1715
|
+
f"public_runtime_language_uat_missing_{label}",
|
|
1716
|
+
mutated_fragment("references/uat.md", fragment),
|
|
1717
|
+
)
|
|
1718
|
+
)
|
|
1719
|
+
for label, marker in UNEXPECTED_FAILURE_UAT_MARKERS.items():
|
|
1720
|
+
cases.append(
|
|
1721
|
+
(
|
|
1722
|
+
f"unexpected_failure_uat_marker_missing_{label}",
|
|
1723
|
+
mutated("references/uat.md", marker),
|
|
1724
|
+
)
|
|
1725
|
+
)
|
|
1726
|
+
for label, contradiction in UNEXPECTED_FAILURE_FORBIDDEN.items():
|
|
1727
|
+
cases.append(
|
|
1728
|
+
(
|
|
1729
|
+
f"unexpected_failure_contradiction_{label}",
|
|
1730
|
+
mutated(
|
|
1731
|
+
"references/core-runtime.md",
|
|
1732
|
+
"## Execution Loop",
|
|
1733
|
+
f"## Execution Loop\n\n{contradiction}.",
|
|
1734
|
+
),
|
|
1735
|
+
)
|
|
1736
|
+
)
|
|
1737
|
+
for label, contradiction in SENDABLE_PACKET_FORBIDDEN.items():
|
|
1738
|
+
cases.append(
|
|
1739
|
+
(
|
|
1740
|
+
f"sendable_packet_contradiction_{label}",
|
|
1741
|
+
mutated(
|
|
1742
|
+
"references/core-runtime.md",
|
|
1743
|
+
"## Self-Contained Dispatch",
|
|
1744
|
+
f"## Self-Contained Dispatch\n\n{contradiction}.",
|
|
1745
|
+
),
|
|
1746
|
+
)
|
|
1747
|
+
)
|
|
1748
|
+
for label, contradiction in MESSAGE_ID_FORBIDDEN.items():
|
|
1749
|
+
cases.append(
|
|
1750
|
+
(
|
|
1751
|
+
f"message_id_contradiction_{label}",
|
|
1752
|
+
mutated(
|
|
1753
|
+
"references/core-runtime.md",
|
|
1754
|
+
"## Ambiguous Tool Outcomes, Role Reconciliation, And Batch Deduplication",
|
|
1755
|
+
f"## Ambiguous Tool Outcomes, Role Reconciliation, And Batch Deduplication\n\n{contradiction}.",
|
|
1756
|
+
),
|
|
1757
|
+
)
|
|
1758
|
+
)
|
|
1759
|
+
for label, contradiction in LIVING_BRIEF_FORBIDDEN.items():
|
|
1760
|
+
cases.append(
|
|
1761
|
+
(
|
|
1762
|
+
f"living_brief_contradiction_{label}",
|
|
1763
|
+
mutated(
|
|
1764
|
+
"references/core-runtime.md",
|
|
1765
|
+
"## Controller Preflight",
|
|
1766
|
+
f"## Controller Preflight\n\n{contradiction}.",
|
|
1767
|
+
),
|
|
1768
|
+
)
|
|
1769
|
+
)
|
|
1770
|
+
for label, contradiction in TRUTH_SOURCE_INTAKE_FORBIDDEN.items():
|
|
1771
|
+
cases.append(
|
|
1772
|
+
(
|
|
1773
|
+
f"truth_source_intake_contradiction_{label}",
|
|
1774
|
+
mutated(
|
|
1775
|
+
"references/core-runtime.md",
|
|
1776
|
+
"## Controller Preflight",
|
|
1777
|
+
f"## Controller Preflight\n\n{contradiction}.",
|
|
1778
|
+
),
|
|
1779
|
+
)
|
|
1780
|
+
)
|
|
1781
|
+
for label, contradiction in OUTCOME_ANCHOR_FORBIDDEN.items():
|
|
1782
|
+
cases.append(
|
|
1783
|
+
(
|
|
1784
|
+
f"outcome_anchor_contradiction_{label}",
|
|
1785
|
+
mutated(
|
|
1786
|
+
"references/core-runtime.md",
|
|
1787
|
+
"## Outcome Anchor And Progress Gate",
|
|
1788
|
+
f"## Outcome Anchor And Progress Gate\n\n{contradiction}.",
|
|
1789
|
+
),
|
|
1790
|
+
)
|
|
1791
|
+
)
|
|
1792
|
+
for label, contradiction in DRIFT_CHECKPOINT_FORBIDDEN.items():
|
|
1793
|
+
cases.append(
|
|
1794
|
+
(
|
|
1795
|
+
f"drift_checkpoint_contradiction_{label}",
|
|
1796
|
+
mutated(
|
|
1797
|
+
"references/core-runtime.md",
|
|
1798
|
+
"## Outcome Anchor And Progress Gate",
|
|
1799
|
+
f"## Outcome Anchor And Progress Gate\n\n{contradiction}.",
|
|
1800
|
+
),
|
|
1801
|
+
)
|
|
1802
|
+
)
|
|
1803
|
+
for label, contradiction in RESULT_DISPOSITION_FORBIDDEN.items():
|
|
1804
|
+
cases.append(
|
|
1805
|
+
(
|
|
1806
|
+
f"result_disposition_contradiction_{label}",
|
|
1807
|
+
mutated(
|
|
1808
|
+
"references/core-runtime.md",
|
|
1809
|
+
"## Outcome Anchor And Progress Gate",
|
|
1810
|
+
f"## Outcome Anchor And Progress Gate\n\n{contradiction}.",
|
|
1811
|
+
),
|
|
1812
|
+
)
|
|
1813
|
+
)
|
|
1814
|
+
for label, contradiction in EXECUTION_PROFILE_FORBIDDEN.items():
|
|
1815
|
+
cases.append(
|
|
1816
|
+
(
|
|
1817
|
+
f"execution_profile_contradiction_{label}",
|
|
1818
|
+
mutated(
|
|
1819
|
+
"references/core-runtime.md",
|
|
1820
|
+
"## Execution Profile Gate",
|
|
1821
|
+
f"## Execution Profile Gate\n\n{contradiction}.",
|
|
1822
|
+
),
|
|
1823
|
+
)
|
|
1824
|
+
)
|
|
1825
|
+
for label, contradiction in PUBLIC_RUNTIME_LANGUAGE_FORBIDDEN.items():
|
|
1826
|
+
cases.append(
|
|
1827
|
+
(
|
|
1828
|
+
f"public_runtime_language_contradiction_{label}",
|
|
1829
|
+
mutated(
|
|
1830
|
+
"references/core-runtime.md",
|
|
1831
|
+
"## Public Runtime Language Boundary",
|
|
1832
|
+
f"## Public Runtime Language Boundary\n\n{contradiction}.",
|
|
1833
|
+
),
|
|
1834
|
+
)
|
|
1835
|
+
)
|
|
1836
|
+
cases.append(
|
|
1837
|
+
(
|
|
1838
|
+
"roadmap_adds_producer_role",
|
|
1839
|
+
mutated(
|
|
1840
|
+
"references/roadmap.md",
|
|
1841
|
+
"do not enter role columns,",
|
|
1842
|
+
"enter role columns,",
|
|
1843
|
+
),
|
|
1844
|
+
)
|
|
1845
|
+
)
|
|
1846
|
+
cases.append(
|
|
1847
|
+
(
|
|
1848
|
+
"fifth_formal_role",
|
|
1849
|
+
mutated(
|
|
1850
|
+
"references/core-runtime.md",
|
|
1851
|
+
"CER has only the formal roles C, E1, R, and E2",
|
|
1852
|
+
"CER has only the formal roles C, E1, R, E2, and P",
|
|
1853
|
+
),
|
|
1854
|
+
)
|
|
1855
|
+
)
|
|
1856
|
+
|
|
1857
|
+
for name, candidate in cases:
|
|
1858
|
+
if not validate_texts(root, candidate):
|
|
1859
|
+
failures.append(name)
|
|
1860
|
+
return len(cases), failures
|
|
1861
|
+
|
|
1862
|
+
|
|
1863
|
+
def main() -> int:
|
|
1864
|
+
parser = argparse.ArgumentParser(description="Validate the CER skill package")
|
|
1865
|
+
parser.add_argument("root", nargs="?", default=".", help="CER skill root")
|
|
1866
|
+
parser.add_argument("--self-test", action="store_true", help="run in-memory mutation matrix")
|
|
1867
|
+
args = parser.parse_args()
|
|
1868
|
+
root = Path(args.root).resolve()
|
|
1869
|
+
findings = validate(root)
|
|
1870
|
+
if findings:
|
|
1871
|
+
for finding in findings:
|
|
1872
|
+
print(f"FAIL: {finding}")
|
|
1873
|
+
print(f"status: failed ({len(findings)} findings)")
|
|
1874
|
+
return 1
|
|
1875
|
+
print("status: passed")
|
|
1876
|
+
print(f"version: {(root / 'VERSION').read_text(encoding='utf-8-sig').strip()}")
|
|
1877
|
+
print(f"files: {len(EXPECTED_FILES)}")
|
|
1878
|
+
if args.self_test:
|
|
1879
|
+
count, failures = mutation_matrix(root)
|
|
1880
|
+
print(f"mutation_cases: {count}")
|
|
1881
|
+
if failures:
|
|
1882
|
+
print(f"FAIL: mutation false-green: {failures}")
|
|
1883
|
+
return 1
|
|
1884
|
+
print("mutation_status: passed")
|
|
1885
|
+
return 0
|
|
1886
|
+
|
|
1887
|
+
|
|
1888
|
+
if __name__ == "__main__":
|
|
1889
|
+
sys.exit(main())
|